Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Visualisation Backend #406

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
2352448
WIP commit with initial implementation of the backend
VoyTechnology Apr 20, 2016
5456b95
Made exporting optional in Raft, previously it was on by default
VoyTechnology Apr 20, 2016
f1e396f
Added Export API to the documentation
VoyTechnology Apr 20, 2016
961a715
Added exporting flags to paranoid-cli
VoyTechnology Apr 20, 2016
3376577
Incremented version number after adding the enable-export flag
VoyTechnology Apr 20, 2016
fd06481
Fixed issues from in code review
VoyTechnology Apr 21, 2016
1cb871f
Changed the sendLeaderDataRequest to not cause a panic
VoyTechnology Apr 21, 2016
8288bb7
Fixed formatting and used labels for a loop to break properly
VoyTechnology Apr 21, 2016
47e910e
Added NodeChange and SetState and Event functions
VoyTechnology Apr 21, 2016
5e2e961
Added state exporting from UpdateCurrentConfiguration
VoyTechnology Apr 21, 2016
b74f46a
Added MessageEvent content
VoyTechnology Apr 21, 2016
bb9c290
Modified the LeaderData protobuf to be an approximate of the final data
VoyTechnology Apr 21, 2016
e24d504
Added a channel for messages and hopefully implemented a proper versi…
VoyTechnology Apr 21, 2016
e4270ee
Hopefully changed exporting of the status from leader node to be send…
VoyTechnology Apr 21, 2016
c4638de
Added a workaround to check is the leader still the leader
VoyTechnology Apr 21, 2016
91b9556
Added exporter to wait group
VoyTechnology Apr 21, 2016
0de8bc3
Fixed some Waitgroup issues, moved some code into utils, added a new …
VoyTechnology Apr 21, 2016
35b6172
Fixed styling issues, moved accidentally moved function back to where…
VoyTechnology Apr 21, 2016
cc40e71
Moved an export function to the correct file
VoyTechnology Apr 21, 2016
8b0afc5
Applied changes as stated in code review
VoyTechnology Apr 21, 2016
5bbfad2
Fix build
Apr 21, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions docs/export_api_v0.1.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
Export API
==========

This document defined the API used for exporting data from Raft

## Enabling Exporting ##
By default the exporting function is off, to turn it on `pfsd` must be run with
`-enable-export` flag.

The default port for the exporter is `10100`, and it can be changed with
`-export-port <port>`

## API Specification ##
| Name | Description | Available Options |
| ---- | ----------- | --------------- |
| type | Defines the type of the message | `state`<br> `nodechange`<br>`event`
| data | The data changes depending on what type the message is | [See Below](#Data)

The first message send is of type `state`, it is send on connecting to the server

### Data ###
| Type | Content |
| ---- | ------- |
| `state` | `nodes` - Array of [nodes](#Node) |
| `nodechange` | `action` - Can be of type `add`, `delete`, `update`<br>`node` - Single [node](#Node) to which the change applies. |
| `event` | `source` - Node UUID which is the source of the event<br>`target` - Node UUID which is the target of the event<br>`details` - Specific details of the event, declared internally

### Node ###
| Field Name | Description |
| ---------- | ----------- |
| `uuid` | Unique identifier for the node |
| `commonName` | Display Name of the node |
| `addr` | Address of the node, in a `host:port` format
| `state` | State of the node, can contain multiple states, with <code>&#124;</code> as the delimiter. It's the server's responsibility to make sure there are no race conditions in states. The available states are as follows: `current`, `leader`, `candidate`, `follower` (or blank), `inactive`

## Examples ##
```json
{
"type":"state",
"data": {
"nodes": [
{
"uuid":"1234-abcd-5678-efgh",
"commonName":"node-1",
"addr":"10.0.0.1:67890",
"state":"current|leader"
}, {
"uuid":"9012-ijkl-3456-mnop",
"commonName":"node-2",
"addr":"10.0.0.2:78901",
"state":"inactive"
}
]
}
}
```

```json
{
"type":"nodechange",
"data": {
"action":"update",
"node":{
"uuid":"9012-ijkl-3456-mnop",
"commonName":"node-2",
"addr":"10.0.0.2:78901",
"state":"follower"
}
}
}
```

```json
{
"type":"event",
"data": {
"source":"9012-ijkl-3456-mnop",
"target":"1234-abcd-5678-efgh",
"details": "write-request"
}
}
```
4 changes: 4 additions & 0 deletions paranoid-cli/commands/mountcmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ func doMount(c *cli.Context, args []string) {
if iface != "" {
pfsdFlags = append(pfsdFlags, "-interface="+iface)
}
if c.Bool("enable-export") {
pfsdFlags = append(pfsdFlags, "-enable-export")
pfsdFlags = append(pfsdFlags, "-export-port="+c.String("export-port"))
}
cmd := exec.Command("pfsd", append(pfsdFlags, pfsdArgs...)...)
err = cmd.Start()
if err != nil {
Expand Down
70 changes: 33 additions & 37 deletions paranoid-cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,37 @@ import (
"path"
)


func main() {
// store the flags contained by both mount and automount
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are these package-level in main?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just removed code dublication. Should it be it be in main()?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this code duplication is easier to read than when the code is moved outside of the command definitions.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One of them had a typo :)

mountFlags := []cli.Flag{
cli.BoolFlag{
Name: "n, noprompt",
Usage: "disable the prompt when attempting to mount a PFS without TLS/SSL",
},
cli.StringFlag{
Name: "i, interface",
Usage: "name a network interface over which to make connections. Defaults to default interface",
},
cli.StringFlag{
Name: "d, discovery-addr",
Usage: "use a custom discovery server. Specified with ip:port. Defaults to public discovery server",
},
cli.StringFlag{
Name: "pool-password",
Usage: "connect to a pool that is password protected",
},
cli.BoolFlag{
Name: "enable-export",
Usage: "enable exporting of Raft",
},
cli.StringFlag{
Name: "export-port",
Value: "10100",
Usage: "port on which the Raft exporter should listen",
},
}


usr, err := user.Current()
if err != nil {
Expand Down Expand Up @@ -46,7 +76,7 @@ func main() {
app := cli.NewApp()
app.Name = "paranoid-cli"
app.HelpName = "paranoid-cli"
app.Version = "0.4.1"
app.Version = "0.4.2"
app.Flags = []cli.Flag{
cli.BoolFlag{
Name: "verbose",
Expand Down Expand Up @@ -91,24 +121,7 @@ func main() {
Usage: "mount a paranoid file system",
ArgsUsage: "pfs-name mountpoint",
Action: commands.Mount,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "n, noprompt",
Usage: "disable the prompt when attempting to mount a PFS without TLS/SSL",
},
cli.StringFlag{
Name: "i, interface",
Usage: "name a network interface over which to make connections. Defaults to default interface",
},
cli.StringFlag{
Name: "d, discovery-addr",
Usage: "Use a custom discovery server. Specified with ip:port. Defaults to public discovery server",
},
cli.StringFlag{
Name: "pool-password",
Usage: "connect to a pool that is password portected",
},
},
Flags: mountFlags,
},
{
Name: "secure",
Expand Down Expand Up @@ -153,24 +166,7 @@ func main() {
Usage: "automount a paranoid file system with previous settings",
ArgsUsage: "pfs-name",
Action: commands.AutoMount,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "n, noprompt",
Usage: "disable the prompt when attempting to mount a PFS without TLS/SSL",
},
cli.StringFlag{
Name: "i, interface",
Usage: "name a network interface over which to make connections. Defaults to default interface",
},
cli.StringFlag{
Name: "d, discovery-addr",
Usage: "Use a custom discovery server. Specified with ip:port. Defaults to public discovery server",
},
cli.StringFlag{
Name: "pool-password",
Usage: "connect to a pool that is password portected",
},
},
Flags: mountFlags,
},
{
Name: "unmount",
Expand Down
23 changes: 23 additions & 0 deletions pfsd/exporter/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package exporter

import (
"golang.org/x/net/websocket"
)

type client struct {
ws *websocket.Conn
msgs chan Message
}

func (c *client) write(msg Message) {
c.msgs <- msg
}

func (c *client) listen() {
for {
select {
case msg := <-c.msgs:
websocket.JSON.Send(c.ws, msg)
}
}
}
70 changes: 70 additions & 0 deletions pfsd/exporter/exporter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// exporter package creates an WebServer which exports Raft information
package exporter

import (
"github.com/cpssd/paranoid/logger"
)

var (
Log *logger.ParanoidLogger
server *Server
nodeList map[string]MessageNode
)

func init() {
nodeList = make(map[string]MessageNode)
}

func Send(msg Message) {
server.Send(msg)
}

func NewStdServer(port string) {
server = NewServer(port)
}

func Listen() {
server.Run()
}

// SetState is used to set the local exporter state
func SetState(nodes []MessageNode) {
for i := 0; i < len(nodes); i++ {
nodeList[nodes[i].Uuid] = nodes[i]
}
}

// NodeChange sends a message to the client whenever there is a change in nodes
func NodeChange(node MessageNode) {
msg := Message{
Type: NodeChangeMessage,
Data: MessageData{
Node: node,
},
}

// Check does the message exist to determine the response
// TODO: Add implementation when a node is deleted. This is currently
// not supported with our raft implementation
if _, ok := nodeList[node.Uuid]; !ok {
msg.Data.Action = "add"
} else {
msg.Data.Action = "update"
}

nodeList[node.Uuid] = node
server.Send(msg)
}

// Send an event that happened to the client
func Event(msg Message) {
server.Send(msg)
}

func toNodeArray(m map[string]MessageNode) []MessageNode {
var nodes []MessageNode
for _, n := range m {
nodes = append(nodes, n)
}
return nodes
}
1 change: 1 addition & 0 deletions pfsd/exporter/exporter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package exporter
51 changes: 51 additions & 0 deletions pfsd/exporter/message.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package exporter

type MessageType string

const (
StateMessage MessageType = "state"
NodeChangeMessage = "nodechange"
RaftEventMessage = "event"
)

func (m MessageType) String() string {
switch m {
case StateMessage:
return "state"
case NodeChangeMessage:
return "nodechange"
case RaftEventMessage:
return "event"
default:
return ""
}
}

type Message struct {
Type MessageType `json:"type"`
Data MessageData `json:"data"`
}

type MessageData struct {
// Used for "status"
Nodes []MessageNode `json:"nodes,omitempty"`
// Used for "nodechange"
Action string `json:"action,omitempty"`
// Used for "nodechange"
Node MessageNode `json:"node,omitempty"`
// Used for "event"
Event MessageEvent `json:"event,omitempty"`
}

type MessageNode struct {
Uuid string `json:"uuid"`
CommonName string `json:"commonName"`
State string `json:"state"`
Addr string `json:"addr"`
}

type MessageEvent struct {
Source string `json:"source"`
Target string `json:"target"`
Details string `json:"details"`
}
44 changes: 44 additions & 0 deletions pfsd/exporter/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package exporter

import (
"golang.org/x/net/websocket"
"net/http"
)

type Server struct {
messages chan Message
port string
client *client
}

func NewServer(port string) *Server {
return &Server{
port: port,
messages: make(chan Message),
}
}

// Run starts the websocket server
func (s *Server) Run() {
onConnected := func(ws *websocket.Conn) {
s.client = &client{
ws: ws,
msgs: make(chan Message),
}
s.client.listen()

// Send the state message
s.Send(Message{
Type: StateMessage,
Data: MessageData{
Nodes: toNodeArray(nodeList),
},
})
}

http.ListenAndServe(":"+s.port, websocket.Handler(onConnected))
}

func (s *Server) Send(msg Message) {
s.client.write(msg)
}
Loading