Merge ~teknoraver/snappy-hwe-snaps/+git/wifi-ap:master into ~snappy-hwe-team/snappy-hwe-snaps/+git/wifi-ap:master

Proposed by Matteo Croce
Status: Merged
Approved by: Matteo Croce
Approved revision: 7039e0454f645058f93f0529d9dfcd9e9a098d7d
Merged at revision: 71ab6f3279e3d502e73c77160f642897695e7bd5
Proposed branch: ~teknoraver/snappy-hwe-snaps/+git/wifi-ap:master
Merge into: ~snappy-hwe-team/snappy-hwe-snaps/+git/wifi-ap:master
Diff against target: 263 lines (+245/-1)
2 files modified
cmd/client/cmd_wizard.go (+245/-0)
snapcraft.yaml (+0/-1)
Reviewer Review Type Date Requested Status
Matteo Croce (community) Approve
System Enablement Bot continuous-integration Approve
Simon Fels Needs Fixing
Review via email: mp+307335@code.launchpad.net

Description of the change

Add the wizard tool

To post a comment you must log in.
Revision history for this message
System Enablement Bot (system-enablement-ci-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Matteo Croce (teknoraver) :
Revision history for this message
System Enablement Bot (system-enablement-ci-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Simon Fels (morphis) wrote :

See comments inline.

review: Needs Fixing
Revision history for this message
Matteo Croce (teknoraver) :
Revision history for this message
Simon Fels (morphis) :
review: Needs Fixing
Revision history for this message
Matteo Croce (teknoraver) :
Revision history for this message
System Enablement Bot (system-enablement-ci-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Matteo Croce (teknoraver) :
Revision history for this message
System Enablement Bot (system-enablement-ci-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Simon Fels (morphis) :
review: Needs Fixing
Revision history for this message
System Enablement Bot (system-enablement-ci-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Simon Fels (morphis) :
review: Needs Fixing
Revision history for this message
System Enablement Bot (system-enablement-ci-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Matteo Croce (teknoraver) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/cmd/client/cmd_wizard.go b/cmd/client/cmd_wizard.go
2new file mode 100644
3index 0000000..0837135
4--- /dev/null
5+++ b/cmd/client/cmd_wizard.go
6@@ -0,0 +1,245 @@
7+//
8+// Copyright (C) 2016 Canonical Ltd
9+//
10+// This program is free software: you can redistribute it and/or modify
11+// it under the terms of the GNU General Public License version 3 as
12+// published by the Free Software Foundation.
13+//
14+// This program is distributed in the hope that it will be useful,
15+// but WITHOUT ANY WARRANTY; without even the implied warranty of
16+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+// GNU General Public License for more details.
18+//
19+// You should have received a copy of the GNU General Public License
20+// along with this program. If not, see <http://www.gnu.org/licenses/>.
21+
22+package main
23+
24+import (
25+ "bufio"
26+ "bytes"
27+ "encoding/json"
28+ "fmt"
29+ "io/ioutil"
30+ "net"
31+ "net/http"
32+ "os"
33+ "regexp"
34+ "strconv"
35+ "strings"
36+)
37+
38+// Utility function to read input and strip the trailing \n
39+func readUserInput(reader *bufio.Reader) string {
40+ ret, err := reader.ReadString('\n')
41+ if err != nil {
42+ panic(err)
43+ }
44+ return strings.TrimRight(ret, "\n")
45+}
46+
47+// Helper function to list network interfaces
48+func findExistingInterfaces(wifi bool) (wifis []string) {
49+ // Use sysfs to get interfaces list
50+ const sysNetPath = "/sys/class/net/"
51+ ifaces, err := ioutil.ReadDir(sysNetPath)
52+ wifis = []string{}
53+ if err == nil {
54+ for _, iface := range ifaces {
55+ if iface.Name() != "lo" {
56+ // The "wireless" subdirectory exists only for wireless interfaces
57+ if _, err := os.Stat(sysNetPath + iface.Name() + "/wireless"); os.IsNotExist(err) != wifi {
58+ wifis = append(wifis, iface.Name())
59+ }
60+ }
61+ }
62+ }
63+
64+ return
65+}
66+
67+type wizardStep func(map[string]string, *bufio.Reader) error
68+
69+var allSteps = [...]wizardStep{
70+ // determine the WiFi interface
71+ func(configuration map[string]string, reader *bufio.Reader) error {
72+ fmt.Println("What is the name of the wireless interface you want to use?")
73+ ifaces := findExistingInterfaces(true)
74+ fmt.Print("Available interfaces are: " + strings.Join(ifaces, ", ") + ": ")
75+ iface := readUserInput(reader)
76+ if re := regexp.MustCompile("^[[:alnum:]]+$"); !re.MatchString(iface) {
77+ return fmt.Errorf("Invalid interface name '%s' given", iface)
78+ }
79+ configuration["wifi.interface"] = iface
80+
81+ return nil
82+ },
83+
84+ // Ask for WiFi ESSID
85+ func(configuration map[string]string, reader *bufio.Reader) error {
86+ fmt.Print("Insert the ESSID of your access point: ")
87+ iface := readUserInput(reader)
88+ if len(iface) == 0 || len(iface) > 31 {
89+ return fmt.Errorf("ESSID length must be between 1 and 31 characters")
90+ }
91+ configuration["wifi.essid"] = iface
92+
93+ return nil
94+ },
95+
96+ // Select WiFi encryption type
97+ func(configuration map[string]string, reader *bufio.Reader) error {
98+ fmt.Print("Do you want to protect your network with a WPA2 password? (y/n) ")
99+ switch resp := strings.ToLower(readUserInput(reader)); resp {
100+ case "y":
101+ configuration["wifi.security"] = "wpa2"
102+ case "n":
103+ configuration["wifi.security"] = "open"
104+ default:
105+ return fmt.Errorf("Invalid answer: %s", resp)
106+ }
107+
108+ return nil
109+ },
110+
111+ // If WPA2 is set, ask for valid password
112+ func(configuration map[string]string, reader *bufio.Reader) error {
113+ if configuration["wifi.security"] == "open" {
114+ return nil
115+ }
116+ fmt.Print("Insert your WPA2 passphrase: ")
117+ key := readUserInput(reader)
118+ if len(key) < 8 || len(key) > 63 {
119+ return fmt.Errorf("WPA2 passphrase must be between 8 and 63 characters")
120+ }
121+ configuration["wifi.passphrase"] = key
122+
123+ return nil
124+ },
125+
126+ // Configure WiFi AP IP address
127+ func(configuration map[string]string, reader *bufio.Reader) error {
128+ fmt.Print("Insert the Access Point IP address: ")
129+ inputIp := readUserInput(reader)
130+ ipv4 := net.ParseIP(inputIp)
131+ if ipv4 == nil {
132+ return fmt.Errorf("Invalid IP address: %s", inputIp)
133+ }
134+ if !ipv4.IsGlobalUnicast() {
135+ return fmt.Errorf("%s is a reserved IPv4 address", inputIp)
136+ }
137+ if ipv4.To4() == nil {
138+ return fmt.Errorf("%s is not an IPv4 address", inputIp)
139+ }
140+
141+ configuration["wifi.address"] = inputIp
142+
143+ nmask := ipv4.DefaultMask()
144+ configuration["wifi.netmask"] = fmt.Sprintf("%d.%d.%d.%d", nmask[0], nmask[1], nmask[2], nmask[3])
145+
146+ return nil
147+ },
148+
149+ // Configure the DHCP pool
150+ func(configuration map[string]string, reader *bufio.Reader) error {
151+ var maxpoolsize byte
152+ ipv4 := net.ParseIP(configuration["wifi.address"])
153+ if ipv4[15] <= 128 {
154+ maxpoolsize = 254 - ipv4[15]
155+ } else {
156+ maxpoolsize = ipv4[15] - 1
157+ }
158+
159+ fmt.Printf("How many host do you want your DHCP pool to hold to? (1-%d) ", maxpoolsize)
160+ inputhost, err := strconv.ParseUint(readUserInput(reader), 10, 8)
161+ if err != nil {
162+ return err
163+ }
164+ if byte(inputhost) > maxpoolsize {
165+ return fmt.Errorf("%d is bigger than the maximum pool size %d", inputhost, maxpoolsize)
166+ }
167+
168+ nhosts := byte(inputhost)
169+ // Allocate the pool in the bigger half, trying to avoid overlap with access point IP
170+ if ipv4[15] <= 128 {
171+ configuration["dhcp.range-start"] = fmt.Sprintf("%d.%d.%d.%d", ipv4[12], ipv4[13], ipv4[14], ipv4[15]+1)
172+ configuration["dhcp.range-stop"] = fmt.Sprintf("%d.%d.%d.%d", ipv4[12], ipv4[13], ipv4[14], ipv4[15]+nhosts)
173+ } else {
174+ configuration["dhcp.range-start"] = fmt.Sprintf("%d.%d.%d.%d", ipv4[12], ipv4[13], ipv4[14], ipv4[15]-nhosts)
175+ configuration["dhcp.range-stop"] = fmt.Sprintf("%d.%d.%d.%d", ipv4[12], ipv4[13], ipv4[14], ipv4[15]-1)
176+ }
177+
178+ return nil
179+ },
180+
181+ // Select the wired interface to share
182+ func(configuration map[string]string, reader *bufio.Reader) error {
183+ fmt.Println("What is the wired interface do you want to share?")
184+ ifaces := findExistingInterfaces(false)
185+ fmt.Print("Available interfaces are: " + strings.Join(ifaces, ", ") + ": ")
186+ iface := readUserInput(reader)
187+ if re := regexp.MustCompile("^[[:alnum:]]+$"); !re.MatchString(iface) {
188+ return fmt.Errorf("Invalid interface name '%s' given", iface)
189+ }
190+ configuration["share.network-interface"] = iface
191+
192+ return nil
193+ },
194+}
195+
196+// Use the REST API to set the configuration
197+func applyConfiguration(configuration map[string]string) error {
198+ json, err := json.Marshal(configuration)
199+ if err != nil {
200+ return err
201+ }
202+
203+ response, err := sendHTTPRequest(getServiceConfigurationURI(), "POST", bytes.NewReader(json))
204+ if err != nil {
205+ return err
206+ }
207+
208+ if response.StatusCode == http.StatusOK && response.Status == http.StatusText(http.StatusOK) {
209+ fmt.Println("Configuration applied succesfully")
210+ return nil
211+ } else {
212+ return fmt.Errorf("Failed to set configuration, service returned: %d (%s)\n", response.StatusCode, response.Status)
213+ }
214+}
215+
216+type wizardCommand struct{}
217+
218+func (cmd *wizardCommand) Execute(args []string) error {
219+ // Setup some sane default values, we don't ask the user for everything
220+ configuration := map[string]string{
221+ "disabled": "0",
222+ "wifi.channel": "6",
223+ "wifi.operation-mode": "g",
224+ "dhcp.lease-time": "12h",
225+ }
226+
227+ reader := bufio.NewReader(os.Stdin)
228+
229+ for _, step := range allSteps {
230+ for {
231+ if err := step(configuration, reader); err != nil {
232+ fmt.Println("Error: ", err)
233+ fmt.Print("You want to try again? (y/n) ")
234+ answer := readUserInput(reader)
235+ if strings.ToLower(answer) != "y" {
236+ return err
237+ }
238+ } else {
239+ // Good answer
240+ break
241+ }
242+ }
243+ }
244+
245+ // fmt.Println("configuration: ", configuration)
246+ return applyConfiguration(configuration)
247+}
248+
249+func init() {
250+ addCommand("wizard", "Start the interactive wizard configuration", "", &wizardCommand{})
251+}
252diff --git a/snapcraft.yaml b/snapcraft.yaml
253index 6c7e396..c742c14 100644
254--- a/snapcraft.yaml
255+++ b/snapcraft.yaml
256@@ -72,7 +72,6 @@ parts:
257 snap:
258 - bin
259 build-packages:
260- - golang-go-flags-dev
261 - golang-github-gorilla-mux-dev
262
263 dnsmasq:

Subscribers

People subscribed via source and target branches

to all changes: