From e6ee5d688f495d15913146e2c74f7b1f66f5a215 Mon Sep 17 00:00:00 2001 From: Philippe Caseiro Date: Wed, 10 Oct 2018 17:38:04 +0200 Subject: [PATCH] Improve JSON-RPC methods for emlid interaction --- .vscode/settings.json | 3 + cmd/server/rpc/server.go | 153 ++++++++++++++++++++++++++++++ go.mod | 1 - go.sum | 18 ++++ openwrt/dhcp_client.go | 48 +++++++++- openwrt/dhcp_client_test.go | 2 +- openwrt/uci_wireless_interface.go | 12 ++- openwrt/wifi_scanner.go | 6 +- 8 files changed, 233 insertions(+), 10 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..510b7a9 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "printcode.browserPath": "/usr/bin/firefox" +} \ No newline at end of file diff --git a/cmd/server/rpc/server.go b/cmd/server/rpc/server.go index 874ea6d..aab226e 100644 --- a/cmd/server/rpc/server.go +++ b/cmd/server/rpc/server.go @@ -1,13 +1,17 @@ package rpc import ( + "context" "fmt" "net/http" "time" + "forge.cadoles.com/Pyxis/orion/emlid" + "forge.cadoles.com/Pyxis/orion/emlid/updater" "forge.cadoles.com/Pyxis/orion/openwrt" "github.com/gorilla/rpc" "github.com/gorilla/rpc/json" + "github.com/pkg/errors" ) // OrionService is the JSON-RPC API @@ -71,6 +75,7 @@ func (o *OrionService) OwrtCreateWifiInterface(r *http.Request, args *CreateIfaceArgs, reply *CreateIfaceResponse) error { reply.Iface = nil + if args.Cleanup { o.UCI.LoadWirelessConf() w := o.UCI.GetWifiIfaces() @@ -153,6 +158,154 @@ func (o *OrionService) OwrtConnectWifiInterface(r *http.Request, return nil } +// OrionBox describe an fresh Orion box (base or rover) +type OrionBox struct { + Address string + SSID string + Security string + WifiKey string +} + +// OrionServer describe the Orion master server +type OrionServer struct { + Address string + SSID string + Security string + WifiKey string + ClientIface *openwrt.UCIWirelessInterface +} + +// UpdateOrionBoxArgs argument structure for exported method OwrtCreateWifiInterface +type UpdateOrionBoxArgs struct { + Box *OrionBox + Server *OrionServer +} + +// UpdateOrionBoxResponse argument structure for exported method OwrtCreateWifiInterface +type UpdateOrionBoxResponse struct { + IP string + Netmask string + Synced bool + Version string + Errors []string +} + +// Connect wifi interface to a Orion box wifi hotspot! +func (o *OrionService) connectBox(box *OrionBox, server *OrionServer) error { + o.UCI.LoadWirelessConf() + if server == nil { + return fmt.Errorf("Server definition is empty") + } + + if box == nil { + return fmt.Errorf("Box definitioni is emtpy") + } + fmt.Println("DEBUG") + fmt.Println(server) + fmt.Println("DEBUG") + iface := server.ClientIface + cells := iface.Scan() + for _, cell := range cells { + fmt.Printf("Cell : %s\n", cell.Ssid) + if cell.Ssid == box.SSID { + cn := iface.Connect(o.UCI, cell, box.WifiKey) + if cn.ReturnCode != 0 { + return fmt.Errorf("%s\n%s", cn.Stdout, cn.Stderr) + } + dhcli := openwrt.NewDhcpClient(iface.SysDevName) + dhres := dhcli.AskForIP() + if dhres.CmdRes.ReturnCode != 0 { + return fmt.Errorf("%s\n%s", cn.Stdout, cn.Stderr) + } + return nil + } + } + return fmt.Errorf("Wifi cell with SSID %s is not available", box.SSID) +} + +// UpdateOrionBox starts provisionning process for an Orion box (base or rover) +func (o *OrionService) UpdateOrionBox(r *http.Request, + args *UpdateOrionBoxArgs, + reply *UpdateOrionBoxResponse) error { + + if err := o.connectBox(args.Box, args.Server); err != nil { + reply.Errors = append(reply.Errors, err.Error()) + return errors.Wrap(err, "Connect to box failed") + } + + re := o.UCI.Reload() + if re.ReturnCode != 0 { + return fmt.Errorf("%s\n%s", re.Stdout, re.Stderr) + } + + ssid := args.Server.SSID + security := args.Server.Security + wifiKey := args.Server.WifiKey + + if security == "" { + security = string(updater.SecurityWPAPSK) + } + + //services, err := emlid.Discover(20 * time.Second) + //if err != nil { + // return err + //} + + //if len(services) == 0 { + // return fmt.Errorf("Discovery don't find any Orion Box") + //} + + //fmt.Println(services) + boxCli := updater.NewClient( + //emlid.WithService(services[0]), + emlid.WithEndpoint(args.Box.Address, 80), + ) + + if err := boxCli.Connect(); err != nil { + return errors.Wrap(err, "Box connect to master failed") + } + defer boxCli.Close() + + ctx, addWifiCancel := context.WithTimeout(r.Context(), 55*time.Second) + defer addWifiCancel() + + done, err := boxCli.AddWifiNetwork(ctx, ssid, updater.WifiSecurity(security), wifiKey) + if err != nil { + return errors.Wrap(err, "AddWifiNetworkFailed") + } + + if !done { + return fmt.Errorf("Impossible to add wifi network") + } + + ctx, joinWifiCancel := context.WithTimeout(ctx, 55*time.Second) + defer joinWifiCancel() + + if err := boxCli.JoinWifiNetwork(ctx, ssid, true); err != nil { + return errors.Wrap(err, "Time sync failed") + } + + ctx, timeSyncedCancel := context.WithTimeout(r.Context(), 55*time.Second) + defer timeSyncedCancel() + synced, err := boxCli.TimeSynced(ctx) + if err != nil { + return errors.Wrap(err, "Time sync failed") + } + reply.Synced = synced + + ctx, reachviewVersionCancel := context.WithTimeout(r.Context(), 55*time.Second) + defer reachviewVersionCancel() + version, err := boxCli.ReachViewVersion(ctx) + if err != nil { + return err + } + reply.Version = version + + ctx, rebootCancel := context.WithTimeout(r.Context(), 55*time.Second) + defer rebootCancel() + return boxCli.RebootNow(ctx, true) +} + // NewServer returns a new configured JSON-RPC server func NewServer() *rpc.Server { server := rpc.NewServer() diff --git a/go.mod b/go.mod index 8490b43..4989193 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,6 @@ require ( github.com/cenkalti/backoff v2.0.0+incompatible // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-chi/chi v3.3.3+incompatible - github.com/gorilla/rpc v1.1.0 github.com/gorilla/websocket v1.4.0 // indirect github.com/grandcat/zeroconf v0.0.0-20180329153754-df75bb3ccae1 github.com/miekg/dns v1.0.12 // indirect diff --git a/go.sum b/go.sum index c0132ef..c701095 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,16 @@ forge.cadoles.com/Pyxis/golang-socketio v0.0.0-20180919100209-bb857ced6b95 h1:o3G5+9RjczCK1xAYFaRMknk1kY9Ule6PNfiW6N6hEpg= forge.cadoles.com/Pyxis/golang-socketio v0.0.0-20180919100209-bb857ced6b95/go.mod h1:I6kYOFWNkFlNeQLI7ZqfTRz4NdPHZxX0Bzizmzgchs0= +<<<<<<< HEAD github.com/caarlos0/env v3.4.0+incompatible h1:FRwBdvENjLHZoUbFnULnFss9wKtcapdaM35DfxiTjeM= github.com/caarlos0/env v3.4.0+incompatible/go.mod h1:tdCsowwCzMLdkqRYDlHpZCp2UooDD3MspDBjZ2AD02Y= github.com/cenkalti/backoff v2.0.0+incompatible h1:5IIPUHhlnUZbcHQsQou5k1Tn58nJkeJL9U+ig5CHJbY= github.com/cenkalti/backoff v2.0.0+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +======= +github.com/caarlos0/env v3.3.0+incompatible h1:jCfY0ilpzC2FFViyZyDKCxKybDESTwaR+ebh8zm6AOE= +github.com/caarlos0/env v3.3.0+incompatible/go.mod h1:tdCsowwCzMLdkqRYDlHpZCp2UooDD3MspDBjZ2AD02Y= +github.com/caarlos0/env v3.4.0+incompatible h1:FRwBdvENjLHZoUbFnULnFss9wKtcapdaM35DfxiTjeM= +github.com/caarlos0/env v3.4.0+incompatible/go.mod h1:tdCsowwCzMLdkqRYDlHpZCp2UooDD3MspDBjZ2AD02Y= +>>>>>>> d39cbb6... Improve JSON-RPC methods for emlid interaction github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-chi/chi v3.3.3+incompatible h1:KHkmBEMNkwKuK4FdQL7N2wOeB9jnIx7jR5wsuSBEFI8= @@ -16,8 +23,17 @@ github.com/grandcat/zeroconf v0.0.0-20180329153754-df75bb3ccae1 h1:VSELJSxQlpi1b github.com/grandcat/zeroconf v0.0.0-20180329153754-df75bb3ccae1/go.mod h1:YjKB0WsLXlMkO9p+wGTCoPIDGRJH0mz7E526PxkQVxI= github.com/miekg/dns v1.0.12 h1:814rTNaw7Q7pGncpSEDT06YS8rdGmpUEnKgpQzctJsk= github.com/miekg/dns v1.0.12/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +<<<<<<< HEAD github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +======= +github.com/mitchellh/mapstructure v1.0.0 h1:vVpGvMXJPqSDh2VYHF7gsfQj8Ncx+Xw5Y1KHeTRY+7I= +github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/oleksandr/bonjour v0.0.0-20160508152359-5dcf00d8b228 h1:Cvfd2dOlXIPTeEkOT/h8PyK4phBngOM4at9/jlgy7d4= +github.com/oleksandr/bonjour v0.0.0-20160508152359-5dcf00d8b228/go.mod h1:MGuVJ1+5TX1SCoO2Sx0eAnjpdRytYla2uC1YIZfkC9c= +>>>>>>> d39cbb6... Improve JSON-RPC methods for emlid interaction github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -28,6 +44,8 @@ golang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4 h1:Vk3wNqEZwyGyei9yq5ekj7 golang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3 h1:czFLhve3vsQetD6JOJ8NZZvGQIXlnN3/yXxbT6/awxI= golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58 h1:otZG8yDCO4LVps5+9bxOeNiCvgmOyt96J3roHTYs7oE= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20181005133103-4497e2df6f9e h1:EfdBzeKbFSvOjoIqSZcfS8wp0FBLokGBEs9lz1OtSg0= diff --git a/openwrt/dhcp_client.go b/openwrt/dhcp_client.go index 40c81f3..b4bd2e7 100644 --- a/openwrt/dhcp_client.go +++ b/openwrt/dhcp_client.go @@ -1,11 +1,24 @@ package openwrt +import ( + "bufio" + "regexp" + "strings" +) + // DhcpClient represents a dhcp client ... :) type DhcpClient struct { exec Executor iface string } +// DhcpResult contains sorted result form AskFroIP +type DhcpResult struct { + CmdRes *CommandResult + IP string + Netmask string +} + // NewDhcpClient return an UCI instance to interact with UCI func NewDhcpClient(netIface string) *DhcpClient { exec := &localExecutor{} @@ -32,7 +45,36 @@ func NewDhcpClientWithExecutor(netIface string, exe Executor) *DhcpClient { // return &DhcpClient{exec, iface} //} -// AskForIP runs a dhclient ip request with udhcpc -func (dc *DhcpClient) AskForIP() *CommandResult { - return dc.exec.Run("udhcpc", "-i", dc.iface) +func parseDhcpClientOut(out *CommandResult) *DhcpResult { + if out.ReturnCode != 0 { + return &DhcpResult{ + out, + "", + "", + } + } + + scanner := bufio.NewScanner(strings.NewReader(out.Stdout)) + for scanner.Scan() { + line := scanner.Text() + re := regexp.MustCompile(`^udhcpc: ifconfig`) + if re.MatchString(line) { + spl := strings.Split(line, " ") + ip := spl[3] + mask := spl[5] + return &DhcpResult{ + out, + ip, + mask, + } + } + + } + return nil +} + +// AskForIP runs a dhclient ip request with udhcpc +func (dc *DhcpClient) AskForIP() *DhcpResult { + out := dc.exec.Run("udhcpc", "-i", dc.iface) + return parseDhcpClientOut(out) } diff --git a/openwrt/dhcp_client_test.go b/openwrt/dhcp_client_test.go index b21ca7e..87959aa 100644 --- a/openwrt/dhcp_client_test.go +++ b/openwrt/dhcp_client_test.go @@ -6,7 +6,7 @@ func TestDhcpClientAskForIP(t *testing.T) { uexec := createMockExecutor("", "", 0) dhc := NewDhcpClientWithExecutor("wlan1", uexec) res := dhc.AskForIP() - if res.ReturnCode != 0 { + if res.CmdRes.ReturnCode != 0 { t.Error("Error in DHCP Client !!") } } diff --git a/openwrt/uci_wireless_interface.go b/openwrt/uci_wireless_interface.go index a1660cb..3b08247 100644 --- a/openwrt/uci_wireless_interface.go +++ b/openwrt/uci_wireless_interface.go @@ -22,6 +22,7 @@ type UCIWirelessInterface struct { Network string Encryption string Key string + Country string } // NewUCIWirelessInterface builds a new UCIWirelessInterface instance @@ -71,10 +72,11 @@ func (wi *UCIWirelessInterface) Create(uci *UCI) *Action { if addRes.ReturnCode != 0 { return addRes } + fmt.Println(wi.Index) if wi.Index <= 0 { - confPrefix = fmt.Sprintf("wireless.@wifi-iface[%d]", wi.Index) - } else { confPrefix = fmt.Sprintf("wireless.@wifi-iface[%d]", len(uci.Wireless.Interfaces)) + } else { + confPrefix = fmt.Sprintf("wireless.@wifi-iface[%d]", wi.Index) } conf := make(map[string][]string) @@ -85,6 +87,8 @@ func (wi *UCIWirelessInterface) Create(uci *UCI) *Action { conf["mode"] = append(conf["mode"], fmt.Sprintf("%s.mode", confPrefix), wi.Mode) conf["bssid"] = append(conf["bssid"], fmt.Sprintf("%s.bssid", confPrefix), wi.Bssid) conf["key"] = append(conf["key"], fmt.Sprintf("%s.key", confPrefix), wi.Key) + conf["disabled"] = append(conf["disabled"], fmt.Sprintf("%s.disabled", confPrefix), "0") + conf["country"] = append(conf["country"], fmt.Sprintf("%s.country", confPrefix), wi.Country) for _, value := range conf { if value[1] != "" { @@ -168,6 +172,7 @@ func (wi *UCIWirelessInterface) Up(uci *UCI) *Action { // Delete remove wifi interface from UCI Configuration func (wi *UCIWirelessInterface) Delete(uci *UCI) *Action { toDelete := fmt.Sprintf("wireless.@wifi-iface[%d]", wi.Index) + fmt.Printf("DEBUG\n%s\nDEBUG", toDelete) del := uci.Delete(toDelete) if del.ReturnCode != 0 { return del @@ -178,6 +183,8 @@ func (wi *UCIWirelessInterface) Delete(uci *UCI) *Action { // Update add a new entry for wifi interface in UCI Configuration func (wi *UCIWirelessInterface) Update(uci *UCI) *Action { wi.Delete(uci) + fmt.Println("IN UPDATE !") + fmt.Println(wi) create := wi.Create(uci) if create.ReturnCode != 0 { return create @@ -200,6 +207,7 @@ func (wi *UCIWirelessInterface) Connect(uci *UCI, cell *WifiCell, secret string) wi.Encryption = cell.Encryption wi.Mode = "sta" wi.Key = secret + fmt.Println(wi) res := wi.Update(uci) if res.ReturnCode != 0 { return res diff --git a/openwrt/wifi_scanner.go b/openwrt/wifi_scanner.go index 137b5a6..3a4aad6 100644 --- a/openwrt/wifi_scanner.go +++ b/openwrt/wifi_scanner.go @@ -33,7 +33,7 @@ func NewWifiWithExecutor(exec Executor, wIface string) *WifiScanner { func (w *WifiScanner) getEncryption(line string) string { var enc string if strings.Contains(line, "PSK") { - enc = "psk" + enc = "psk2" } else if strings.Contains(line, NONE) { enc = NONE } else { @@ -61,9 +61,9 @@ func (w *WifiScanner) parseWifiCells(stdout string) int { } if strings.Contains(line, "SSID:") { - ssid = strings.Split(line, ":")[1] + ssid = strings.Split(line, " ")[1] ssid = strings.Trim(ssid, "\"") - ssid = strings.Trim(ssid, " ") + //ssid = strings.Trim(ssid, " ") if ssid == "" { ssid = " " }