add API for aliases/ansible
This commit is contained in:
parent
b700426a3b
commit
d1aa7ab4d7
|
@ -0,0 +1,43 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/FreifunkBremen/respond-collector/models"
|
||||
)
|
||||
type ApiAliases struct {
|
||||
aliases *models.Aliases
|
||||
config *models.Config
|
||||
nodes *models.Nodes
|
||||
}
|
||||
func NewAliases (config *models.Config, router *httprouter.Router,prefix string,nodes *models.Nodes) {
|
||||
api := &ApiAliases{
|
||||
aliases: models.NewAliases(config),
|
||||
nodes: nodes,
|
||||
config: config,
|
||||
}
|
||||
router.GET(prefix, api.GetAll)
|
||||
router.GET(prefix+"/ansible", api.AnsibleDiff)
|
||||
router.GET(prefix+"/alias/:nodeid", api.GetOne)
|
||||
router.POST(prefix+"/alias/:nodeid", api.SaveOne)
|
||||
}
|
||||
func (api *ApiAliases) GetAll(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
jsonOutput(w,api.aliases.List)
|
||||
}
|
||||
func (api *ApiAliases) GetOne(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
if alias := api.aliases.List[ps.ByName("nodeid")]; alias !=nil{
|
||||
jsonOutput(w,alias)
|
||||
}
|
||||
fmt.Fprint(w, "Not found: ", ps.ByName("nodeid"),"\n")
|
||||
}
|
||||
func (api *ApiAliases) SaveOne(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
alias := &models.Alias{Hostname: ps.ByName("nodeid")}
|
||||
api.aliases.Update(ps.ByName("nodeid"),alias)
|
||||
api.GetOne(w,r,ps)
|
||||
}
|
||||
func (api *ApiAliases) AnsibleDiff(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
diff := api.aliases.List
|
||||
//TODO diff between List and api.nodes (for run not at all)
|
||||
jsonOutput(w,models.GenerateAnsible(api.nodes,diff))
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
func jsonOutput(w http.ResponseWriter,data interface{}){
|
||||
js, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write(js)
|
||||
}
|
|
@ -7,7 +7,9 @@ webserver:
|
|||
port: 8080
|
||||
address: 127.0.0.1
|
||||
webroot: webroot
|
||||
websocketnode: false
|
||||
api:
|
||||
newnodes: true
|
||||
aliases: true
|
||||
nodes:
|
||||
enable: true
|
||||
nodes_path: /var/www/html/meshviewer/data/nodes.json
|
||||
|
|
26
main.go
26
main.go
|
@ -10,21 +10,21 @@ import (
|
|||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/NYTimes/gziphandler"
|
||||
|
||||
"github.com/FreifunkBremen/respond-collector/data"
|
||||
"github.com/FreifunkBremen/respond-collector/models"
|
||||
"github.com/FreifunkBremen/respond-collector/respond"
|
||||
"github.com/FreifunkBremen/respond-collector/websocketserver"
|
||||
"github.com/NYTimes/gziphandler"
|
||||
"github.com/FreifunkBremen/respond-collector/api"
|
||||
)
|
||||
|
||||
var (
|
||||
configFile string
|
||||
config *models.Config
|
||||
wsserverForNodes *websocketserver.Server
|
||||
collector *respond.Collector
|
||||
statsDb *StatsDb
|
||||
nodes *models.Nodes
|
||||
//aliases = models.NewNodes()
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -42,18 +42,21 @@ func main() {
|
|||
collector = respond.NewCollector("nodeinfo statistics neighbours", collectInterval, onReceive)
|
||||
}
|
||||
|
||||
if config.Webserver.WebsocketNode {
|
||||
wsserverForNodes = websocketserver.NewServer("/nodes")
|
||||
go wsserverForNodes.Listen()
|
||||
}
|
||||
|
||||
if config.Webserver.Enable {
|
||||
http.Handle("/", gziphandler.GzipHandler(http.FileServer(http.Dir(config.Webserver.Webroot))))
|
||||
router := httprouter.New()
|
||||
if config.Webserver.Api.NewNode {
|
||||
}
|
||||
if config.Webserver.Api.Aliases {
|
||||
api.NewAliases(config,router,"/api/aliases",nodes)
|
||||
log.Println("api started")
|
||||
}
|
||||
router.NotFound = gziphandler.GzipHandler(http.FileServer(http.Dir(config.Webserver.Webroot)))
|
||||
|
||||
address := net.JoinHostPort(config.Webserver.Address, config.Webserver.Port)
|
||||
log.Println("starting webserver on", address)
|
||||
// TODO bad
|
||||
log.Fatal(http.ListenAndServe(address, nil))
|
||||
log.Fatal(http.ListenAndServe(address, router))
|
||||
}
|
||||
|
||||
// Wait for INT/TERM
|
||||
|
@ -63,9 +66,6 @@ func main() {
|
|||
log.Println("received", sig)
|
||||
|
||||
// Close everything at the end
|
||||
if wsserverForNodes != nil {
|
||||
wsserverForNodes.Close()
|
||||
}
|
||||
if collector != nil {
|
||||
collector.Close()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"sync"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Alias struct {
|
||||
Hostname string `json:"hostname"`
|
||||
}
|
||||
// Nodes struct: cache DB of Node's structs
|
||||
type Aliases struct {
|
||||
List map[string]*Alias `json:"nodes"` // the current nodemap, indexed by node ID
|
||||
config *Config
|
||||
sync.Mutex
|
||||
}
|
||||
// NewNodes create Nodes structs
|
||||
func NewAliases(config *Config) *Aliases {
|
||||
aliases := &Aliases{
|
||||
List: make(map[string]*Alias),
|
||||
config: config,
|
||||
}
|
||||
|
||||
if config.Nodes.AliasesPath != "" {
|
||||
aliases.load()
|
||||
}
|
||||
go aliases.worker()
|
||||
|
||||
return aliases
|
||||
}
|
||||
|
||||
func (e *Aliases) Update(nodeID string, newalias *Alias) {
|
||||
e.Lock()
|
||||
e.List[nodeID] = newalias
|
||||
e.Unlock()
|
||||
|
||||
}
|
||||
|
||||
func (e *Aliases) load() {
|
||||
path := e.config.Nodes.AliasesPath
|
||||
log.Println("loading", path)
|
||||
|
||||
if data, err := ioutil.ReadFile(path); err == nil {
|
||||
if err := json.Unmarshal(data, e); err == nil {
|
||||
log.Println("loaded", len(e.List), "aliases")
|
||||
} else {
|
||||
log.Println("failed to unmarshal nodes:", err)
|
||||
}
|
||||
|
||||
} else {
|
||||
log.Println("failed loading cached nodes:", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Periodically saves the cached DB to json file
|
||||
func (e *Aliases) worker() {
|
||||
c := time.Tick(time.Second * 5)
|
||||
|
||||
for range c {
|
||||
log.Println("saving", len(e.List), "aliases")
|
||||
e.Lock()
|
||||
save(e, e.config.Nodes.AliasesPath)
|
||||
e.Unlock()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package models
|
||||
|
||||
type Ansible struct {
|
||||
Nodes []string `json:"nodes"`
|
||||
Meta struct {
|
||||
HostVars []*AnsibleHostVars `json:"hostvars"`
|
||||
} `json:"_meta"`
|
||||
}
|
||||
type AnsibleHostVars struct {
|
||||
Address string `json:"ansible_ssh_host"`
|
||||
Hostname string `json:"node_name"`
|
||||
}
|
||||
|
||||
func GenerateAnsible(nodes *Nodes,aliases map[string]*Alias) *Ansible{
|
||||
ansible := &Ansible{Nodes:make([]string,0)}
|
||||
for nodeid,alias := range aliases{
|
||||
if node := nodes.List[nodeid]; node != nil {
|
||||
|
||||
ansible.Nodes = append(ansible.Nodes,nodeid)
|
||||
|
||||
vars := &AnsibleHostVars{
|
||||
Address: node.Nodeinfo.Network.Addresses[0],
|
||||
Hostname: alias.Hostname,
|
||||
}
|
||||
ansible.Meta.HostVars = append(ansible.Meta.HostVars,vars)
|
||||
|
||||
}
|
||||
}
|
||||
return ansible
|
||||
}
|
|
@ -20,14 +20,15 @@ type Config struct {
|
|||
Port string `yaml:"port"`
|
||||
Address string `yaml:"address"`
|
||||
Webroot string `yaml:"webroot"`
|
||||
WebsocketNode bool `yaml:"websocketnode"`
|
||||
WebsocketAliases bool `yaml:"websocketaliases"`
|
||||
Api struct {
|
||||
NewNode bool `yaml:"newnode"`
|
||||
Aliases bool `yaml:"aliases"`
|
||||
} `yaml:"api"`
|
||||
} `yaml:"webserver"`
|
||||
Nodes struct {
|
||||
Enable bool `yaml:"enable"`
|
||||
NodesPath string `yaml:"nodes_path"`
|
||||
GraphsPath string `yaml:"graphs_path"`
|
||||
AliasesEnable bool `yaml:"aliases_enable"`
|
||||
AliasesPath string `yaml:"aliases_path"`
|
||||
SaveInterval int `yaml:"saveinterval"`
|
||||
VpnAddresses []string `yaml:"vpn_addresses"`
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
package websocketserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/net/websocket"
|
||||
)
|
||||
|
||||
const channelBufSize = 100
|
||||
|
||||
var maxID = 0
|
||||
|
||||
//Client struct
|
||||
type Client struct {
|
||||
id int
|
||||
ws *websocket.Conn
|
||||
server *Server
|
||||
ch chan interface{}
|
||||
doneCh chan bool
|
||||
}
|
||||
|
||||
//NewClient creates a new Client
|
||||
func NewClient(ws *websocket.Conn, server *Server) *Client {
|
||||
|
||||
if ws == nil {
|
||||
panic("ws cannot be nil")
|
||||
}
|
||||
|
||||
if server == nil {
|
||||
panic("server cannot be nil")
|
||||
}
|
||||
|
||||
maxID++
|
||||
|
||||
return &Client{
|
||||
id: maxID,
|
||||
ws: ws,
|
||||
server: server,
|
||||
ch: make(chan interface{}, channelBufSize),
|
||||
doneCh: make(chan bool),
|
||||
}
|
||||
}
|
||||
|
||||
//GetConnection the websocket connection of a listen client
|
||||
func (c *Client) GetConnection() *websocket.Conn {
|
||||
return c.ws
|
||||
}
|
||||
|
||||
//Write send the msg informations to the clients
|
||||
func (c *Client) Write(msg interface{}) {
|
||||
select {
|
||||
case c.ch <- msg:
|
||||
default:
|
||||
c.server.Del(c)
|
||||
err := fmt.Errorf("Client %d is disconnected.", c.id)
|
||||
c.server.Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Listen Write and Read request via chanel
|
||||
func (c *Client) Listen() {
|
||||
c.listen()
|
||||
}
|
||||
|
||||
// listen for new msg informations
|
||||
func (c *Client) listen() {
|
||||
for {
|
||||
select {
|
||||
case msg := <-c.ch:
|
||||
err := websocket.JSON.Send(c.ws, msg)
|
||||
if err != nil {
|
||||
c.doneCh <- true
|
||||
}
|
||||
case gone := <-c.doneCh:
|
||||
if gone {
|
||||
c.server.Del(c)
|
||||
err := fmt.Errorf("Client %d is disconnected.", c.id)
|
||||
c.server.Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
package websocketserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/net/websocket"
|
||||
)
|
||||
|
||||
//Server struct
|
||||
type Server struct {
|
||||
pattern string
|
||||
clients map[int]*Client
|
||||
addCh chan *Client
|
||||
delCh chan *Client
|
||||
sendAllCh chan interface{}
|
||||
closeCh chan bool
|
||||
errCh chan error
|
||||
}
|
||||
|
||||
//NewServer creates a new server
|
||||
func NewServer(pattern string) *Server {
|
||||
return &Server{
|
||||
pattern: pattern,
|
||||
clients: make(map[int]*Client),
|
||||
addCh: make(chan *Client),
|
||||
delCh: make(chan *Client),
|
||||
sendAllCh: make(chan interface{}),
|
||||
closeCh: make(chan bool),
|
||||
errCh: make(chan error),
|
||||
}
|
||||
}
|
||||
|
||||
//Add a listen client
|
||||
func (s *Server) Add(c *Client) {
|
||||
s.addCh <- c
|
||||
}
|
||||
|
||||
//Del a listen client
|
||||
func (s *Server) Del(c *Client) {
|
||||
s.delCh <- c
|
||||
}
|
||||
|
||||
//SendAll to all listen clients a msg
|
||||
func (s *Server) SendAll(msg interface{}) {
|
||||
s.sendAllCh <- msg
|
||||
}
|
||||
|
||||
//Close stops server
|
||||
func (s *Server) Close() {
|
||||
s.closeCh <- true
|
||||
}
|
||||
|
||||
//Err send to server
|
||||
func (s *Server) Err(err error) {
|
||||
s.errCh <- err
|
||||
}
|
||||
|
||||
func (s *Server) sendAll(msg interface{}) {
|
||||
for _, c := range s.clients {
|
||||
c.Write(msg)
|
||||
}
|
||||
}
|
||||
|
||||
// Listen and serve.
|
||||
// It serves client connection and broadcast request.
|
||||
func (s *Server) Listen() {
|
||||
|
||||
log.Println("Listening Server...")
|
||||
|
||||
// websocket handler
|
||||
onConnected := func(ws *websocket.Conn) {
|
||||
defer func() {
|
||||
err := ws.Close()
|
||||
if err != nil {
|
||||
s.errCh <- err
|
||||
}
|
||||
}()
|
||||
|
||||
client := NewClient(ws, s)
|
||||
s.Add(client)
|
||||
defer func() {
|
||||
s.Del(client)
|
||||
err := fmt.Errorf("Client %d is disconnected.", client.id)
|
||||
s.Err(err)
|
||||
}()
|
||||
client.Listen()
|
||||
}
|
||||
http.Handle(s.pattern, websocket.Handler(onConnected))
|
||||
log.Println("Created handler")
|
||||
|
||||
for {
|
||||
select {
|
||||
|
||||
// Add new a client
|
||||
case c := <-s.addCh:
|
||||
log.Println("Added new client")
|
||||
s.clients[c.id] = c
|
||||
log.Println("Now", len(s.clients), "clients connected.")
|
||||
|
||||
// del a client
|
||||
case c := <-s.delCh:
|
||||
log.Println("Delete client")
|
||||
delete(s.clients, c.id)
|
||||
|
||||
// broadcast message for all clients
|
||||
case msg := <-s.sendAllCh:
|
||||
s.sendAll(msg)
|
||||
|
||||
case err := <-s.errCh:
|
||||
log.Println("Error:", err.Error())
|
||||
|
||||
case <-s.closeCh:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue