Switch to cobra for the CLI
This commit is contained in:
parent
f4650213b8
commit
cbb5f2e59b
@ -8,5 +8,4 @@ install:
|
|||||||
- go get golang.org/x/tools/cmd/cover
|
- go get golang.org/x/tools/cmd/cover
|
||||||
script:
|
script:
|
||||||
- ./.test-coverage
|
- ./.test-coverage
|
||||||
- go install github.com/FreifunkBremen/yanic/cmd/yanic
|
- go install github.com/FreifunkBremen/yanic
|
||||||
- go install github.com/FreifunkBremen/yanic/cmd/yanic-query
|
|
||||||
|
71
README.md
71
README.md
@ -28,16 +28,77 @@ Recently seen nodes that does not reply are requested via a unicast message.
|
|||||||
* [Mobi](https://www.gitbook.com/download/mobi/book/freifunkbremen/yanic)
|
* [Mobi](https://www.gitbook.com/download/mobi/book/freifunkbremen/yanic)
|
||||||
* [ePUB](https://www.gitbook.com/download/epub/book/freifunkbremen/yanic)
|
* [ePUB](https://www.gitbook.com/download/epub/book/freifunkbremen/yanic)
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
Read comments in [config_example.toml](config_example.toml) for more information.
|
||||||
|
|
||||||
## Quick startup
|
## Running
|
||||||
|
|
||||||
|
Yanic provides several commands:
|
||||||
|
|
||||||
### Usage
|
### Usage
|
||||||
|
|
||||||
|
Run Yanic without any arguments to get the usage information:
|
||||||
|
|
||||||
```
|
```
|
||||||
Usage of ./yanic:
|
Usage:
|
||||||
-config path/to/config.toml
|
yanic [command]
|
||||||
|
|
||||||
|
Available Commands:
|
||||||
|
help Help about any command
|
||||||
|
import Imports global statistics from the given RRD files, requires InfluxDB
|
||||||
|
query Sends a query on the interface to the destination and waits for a response
|
||||||
|
serve Runs the yanic server
|
||||||
|
|
||||||
|
Flags:
|
||||||
|
-h, --help help for yanic
|
||||||
|
--timestamps Enables timestamps for log output
|
||||||
|
|
||||||
|
Use "yanic [command] --help" for more information about a command.
|
||||||
```
|
```
|
||||||
### Configuration
|
|
||||||
Read comments in [config_example.toml](config_example.toml) for more information.
|
#### Serve
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
yanic serve [flags]
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
yanic serve -config /etc/yanic.toml
|
||||||
|
|
||||||
|
Flags:
|
||||||
|
-c, --config string Path to configuration file (default "config.toml")
|
||||||
|
-h, --help help for serve
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Import
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
yanic import <file.rrd> [flags]
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
yanic import -config /etc/yanic.toml olddata.rrd
|
||||||
|
|
||||||
|
Flags:
|
||||||
|
-c, --config string Path to configuration file (default "config.toml")
|
||||||
|
-h, --help help for import
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### Query
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
yanic query <interface> <destination> [flags]
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
yanic query wlan0 "[fe80::eade:27ff:dead:beef%wlp4s0]:1001"
|
||||||
|
|
||||||
|
Flags:
|
||||||
|
-h, --help help for query
|
||||||
|
--wait int Seconds to wait for a response (default 1)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### Live
|
### Live
|
||||||
* [meshviewer](https://map.bremen.freifunk.net) **Freifunk Bremen** with a patch to show state-version of `nodes.json`
|
* [meshviewer](https://map.bremen.freifunk.net) **Freifunk Bremen** with a patch to show state-version of `nodes.json`
|
||||||
|
26
cmd/config.go
Normal file
26
cmd/config.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/FreifunkBremen/yanic/database"
|
||||||
|
"github.com/FreifunkBremen/yanic/respond"
|
||||||
|
"github.com/FreifunkBremen/yanic/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
configPath string
|
||||||
|
collector *respond.Collector
|
||||||
|
connections database.Connection
|
||||||
|
nodes *runtime.Nodes
|
||||||
|
)
|
||||||
|
|
||||||
|
func loadConfig() *runtime.Config {
|
||||||
|
config, err := runtime.ReadConfigFile(configPath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "unable to load config file:", err)
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
}
|
47
cmd/import.go
Normal file
47
cmd/import.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/FreifunkBremen/yanic/database"
|
||||||
|
"github.com/FreifunkBremen/yanic/database/all"
|
||||||
|
"github.com/FreifunkBremen/yanic/rrd"
|
||||||
|
"github.com/FreifunkBremen/yanic/runtime"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// importCmd represents the import command
|
||||||
|
var importCmd = &cobra.Command{
|
||||||
|
Use: "import <file.rrd>",
|
||||||
|
Short: "Imports global statistics from the given RRD files, requires InfluxDB",
|
||||||
|
Example: "yanic import -config /etc/yanic.toml olddata.rrd",
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
path := args[0]
|
||||||
|
config := loadConfig()
|
||||||
|
|
||||||
|
connections, err := all.Connect(config.Database.Connection)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
database.Start(connections, config)
|
||||||
|
defer database.Close(connections)
|
||||||
|
|
||||||
|
log.Println("importing RRD from", path)
|
||||||
|
|
||||||
|
for ds := range rrd.Read(path) {
|
||||||
|
connections.InsertGlobals(
|
||||||
|
&runtime.GlobalStats{
|
||||||
|
Nodes: uint32(ds.Nodes),
|
||||||
|
Clients: uint32(ds.Clients),
|
||||||
|
},
|
||||||
|
ds.Time,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
RootCmd.AddCommand(importCmd)
|
||||||
|
importCmd.Flags().StringVarP(&configPath, "config", "c", "config.toml", "Path to configuration file")
|
||||||
|
}
|
44
cmd/query.go
Normal file
44
cmd/query.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/FreifunkBremen/yanic/respond"
|
||||||
|
"github.com/FreifunkBremen/yanic/runtime"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var wait int
|
||||||
|
|
||||||
|
// queryCmd represents the query command
|
||||||
|
var queryCmd = &cobra.Command{
|
||||||
|
Use: "query <interface> <destination>",
|
||||||
|
Short: "Sends a query on the interface to the destination and waits for a response",
|
||||||
|
Example: `yanic query wlan0 "[fe80::eade:27ff:dead:beef%wlp4s0]:1001"`,
|
||||||
|
Args: cobra.ExactArgs(2),
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
iface := args[0]
|
||||||
|
dstAddress := args[1]
|
||||||
|
|
||||||
|
log.Printf("Sending request address=%s iface=%s", dstAddress, iface)
|
||||||
|
|
||||||
|
nodes := runtime.NewNodes(&runtime.Config{})
|
||||||
|
|
||||||
|
collector := respond.NewCollector(nil, nodes, iface, 0)
|
||||||
|
defer collector.Close()
|
||||||
|
collector.SendPacket(net.ParseIP(dstAddress))
|
||||||
|
|
||||||
|
time.Sleep(time.Second * time.Duration(wait))
|
||||||
|
|
||||||
|
for id, data := range nodes.List {
|
||||||
|
log.Printf("%s: %+v", id, data)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
RootCmd.AddCommand(queryCmd)
|
||||||
|
queryCmd.Flags().IntVar(&wait, "wait", 1, "Seconds to wait for a response")
|
||||||
|
}
|
46
cmd/root.go
Normal file
46
cmd/root.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
timestamps bool
|
||||||
|
)
|
||||||
|
|
||||||
|
// RootCmd represents the base command when called without any subcommands
|
||||||
|
var RootCmd = &cobra.Command{
|
||||||
|
Use: "yanic",
|
||||||
|
Short: "Yet another node info collector",
|
||||||
|
Long: `A respondd client that fetches, stores and publishes information about a Freifunk network.`,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||||
|
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||||
|
func Execute() {
|
||||||
|
if err := RootCmd.Execute(); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cobra.OnInitialize(initConfig)
|
||||||
|
|
||||||
|
// Here you will define your flags and configuration settings.
|
||||||
|
// Cobra supports persistent flags, which, if defined here,
|
||||||
|
// will be global for your application.
|
||||||
|
RootCmd.PersistentFlags().BoolVar(×tamps, "timestamps", false, "Enables timestamps for log output")
|
||||||
|
}
|
||||||
|
|
||||||
|
func initConfig() {
|
||||||
|
if timestamps {
|
||||||
|
log.SetFlags(log.Lshortfile)
|
||||||
|
} else {
|
||||||
|
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||||
|
}
|
||||||
|
}
|
70
cmd/serve.go
Normal file
70
cmd/serve.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/FreifunkBremen/yanic/database"
|
||||||
|
"github.com/FreifunkBremen/yanic/database/all"
|
||||||
|
"github.com/FreifunkBremen/yanic/meshviewer"
|
||||||
|
"github.com/FreifunkBremen/yanic/respond"
|
||||||
|
"github.com/FreifunkBremen/yanic/runtime"
|
||||||
|
"github.com/FreifunkBremen/yanic/webserver"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// serveCmd represents the serve command
|
||||||
|
var serveCmd = &cobra.Command{
|
||||||
|
Use: "serve",
|
||||||
|
Short: "Runs the yanic server",
|
||||||
|
Example: "yanic serve -config /etc/yanic.toml",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
config := loadConfig()
|
||||||
|
|
||||||
|
connections, err := all.Connect(config.Database.Connection)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
database.Start(connections, config)
|
||||||
|
defer database.Close(connections)
|
||||||
|
|
||||||
|
nodes = runtime.NewNodes(config)
|
||||||
|
nodes.Start()
|
||||||
|
meshviewer.Start(config, nodes)
|
||||||
|
|
||||||
|
if config.Webserver.Enable {
|
||||||
|
log.Println("starting webserver on", config.Webserver.Bind)
|
||||||
|
srv := webserver.New(config.Webserver.Bind, config.Webserver.Webroot)
|
||||||
|
go srv.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Respondd.Enable {
|
||||||
|
// Delaying startup to start at a multiple of `duration` since the zero time.
|
||||||
|
if duration := config.Respondd.Synchronize.Duration; duration > 0 {
|
||||||
|
now := time.Now()
|
||||||
|
delay := duration - now.Sub(now.Truncate(duration))
|
||||||
|
log.Printf("delaying %0.1f seconds", delay.Seconds())
|
||||||
|
time.Sleep(delay)
|
||||||
|
}
|
||||||
|
|
||||||
|
collector = respond.NewCollector(connections, nodes, config.Respondd.Interface, config.Respondd.Port)
|
||||||
|
collector.Start(config.Respondd.CollectInterval.Duration)
|
||||||
|
defer collector.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for INT/TERM
|
||||||
|
sigs := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
sig := <-sigs
|
||||||
|
log.Println("received", sig)
|
||||||
|
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
RootCmd.AddCommand(serveCmd)
|
||||||
|
serveCmd.Flags().StringVarP(&configPath, "config", "c", "config.toml", "Path to configuration file")
|
||||||
|
}
|
@ -1,32 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/FreifunkBremen/yanic/respond"
|
|
||||||
"github.com/FreifunkBremen/yanic/runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Usage: yanic-query wlp4s0 "[fe80::eade:27ff:dead:beef%wlp4s0]:1001"
|
|
||||||
func main() {
|
|
||||||
iface := os.Args[1]
|
|
||||||
dstAddress := os.Args[2]
|
|
||||||
|
|
||||||
log.Printf("Sending request address=%s iface=%s", dstAddress, iface)
|
|
||||||
|
|
||||||
nodes := runtime.NewNodes(&runtime.Config{})
|
|
||||||
|
|
||||||
collector := respond.NewCollector(nil, nodes, iface, 0)
|
|
||||||
collector.SendPacket(net.ParseIP(dstAddress))
|
|
||||||
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
|
|
||||||
for id, data := range nodes.List {
|
|
||||||
log.Printf("%s: %+v", id, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
collector.Close()
|
|
||||||
}
|
|
@ -1,101 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/FreifunkBremen/yanic/database"
|
|
||||||
"github.com/FreifunkBremen/yanic/database/all"
|
|
||||||
"github.com/FreifunkBremen/yanic/meshviewer"
|
|
||||||
"github.com/FreifunkBremen/yanic/respond"
|
|
||||||
"github.com/FreifunkBremen/yanic/rrd"
|
|
||||||
"github.com/FreifunkBremen/yanic/runtime"
|
|
||||||
"github.com/FreifunkBremen/yanic/webserver"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
configFile string
|
|
||||||
config *runtime.Config
|
|
||||||
collector *respond.Collector
|
|
||||||
connections database.Connection
|
|
||||||
nodes *runtime.Nodes
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
var importPath string
|
|
||||||
var timestamps bool
|
|
||||||
flag.StringVar(&importPath, "import", "", "import global statistics from the given RRD file, requires influxdb")
|
|
||||||
flag.StringVar(&configFile, "config", "config.toml", "path of configuration file (default:config.yaml)")
|
|
||||||
flag.BoolVar(×tamps, "timestamps", true, "print timestamps in output")
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
if !timestamps {
|
|
||||||
log.SetFlags(0)
|
|
||||||
}
|
|
||||||
log.Println("Yanic say hello")
|
|
||||||
|
|
||||||
config, err := runtime.ReadConfigFile(configFile)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes = runtime.NewNodes(config)
|
|
||||||
|
|
||||||
connections, err = all.Connect(config.Database.Connection)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
database.Start(connections, config)
|
|
||||||
defer database.Close(connections)
|
|
||||||
|
|
||||||
if connections != nil && importPath != "" {
|
|
||||||
importRRD(importPath)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes.Start()
|
|
||||||
meshviewer.Start(config, nodes)
|
|
||||||
|
|
||||||
if config.Webserver.Enable {
|
|
||||||
log.Println("starting webserver on", config.Webserver.Bind)
|
|
||||||
srv := webserver.New(config.Webserver.Bind, config.Webserver.Webroot)
|
|
||||||
go srv.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.Respondd.Enable {
|
|
||||||
// Delaying startup to start at a multiple of `duration` since the zero time.
|
|
||||||
if duration := config.Respondd.Synchronize.Duration; duration > 0 {
|
|
||||||
now := time.Now()
|
|
||||||
delay := duration - now.Sub(now.Truncate(duration))
|
|
||||||
log.Printf("delaying %0.1f seconds", delay.Seconds())
|
|
||||||
time.Sleep(delay)
|
|
||||||
}
|
|
||||||
|
|
||||||
collector = respond.NewCollector(connections, nodes, config.Respondd.Interface, config.Respondd.Port)
|
|
||||||
collector.Start(config.Respondd.CollectInterval.Duration)
|
|
||||||
defer collector.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for INT/TERM
|
|
||||||
sigs := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
|
||||||
sig := <-sigs
|
|
||||||
log.Println("received", sig)
|
|
||||||
}
|
|
||||||
|
|
||||||
func importRRD(path string) {
|
|
||||||
log.Println("importing RRD from", path)
|
|
||||||
for ds := range rrd.Read(path) {
|
|
||||||
connections.InsertGlobals(
|
|
||||||
&runtime.GlobalStats{
|
|
||||||
Nodes: uint32(ds.Nodes),
|
|
||||||
Clients: uint32(ds.Clients),
|
|
||||||
},
|
|
||||||
ds.Time,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,7 +4,7 @@ Description=yanic
|
|||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
User=yanic
|
User=yanic
|
||||||
ExecStart=/opt/go/bin/yanic -config /etc/yanic.conf
|
ExecStart=/opt/go/bin/yanic serve -config /etc/yanic.conf
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=5s
|
RestartSec=5s
|
||||||
Environment=PATH=/usr/bin:/usr/local/bin
|
Environment=PATH=/usr/bin:/usr/local/bin
|
||||||
|
7
main.go
Normal file
7
main.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "github.com/FreifunkBremen/yanic/cmd"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cmd.Execute()
|
||||||
|
}
|
@ -45,12 +45,12 @@ func ReadConfigFile(path string) (config *Config, err error) {
|
|||||||
|
|
||||||
file, err := ioutil.ReadFile(path)
|
file, err := ioutil.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = toml.Unmarshal(file, config)
|
err = toml.Unmarshal(file, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
Loading…
Reference in New Issue
Block a user