mirror of
https://github.com/Team254/cheesy-arena-lite.git
synced 2026-03-09 21:56:50 -04:00
Added automatic configuration of Cisco Aironet AP.
This commit is contained in:
149
aironet.go
Normal file
149
aironet.go
Normal file
@@ -0,0 +1,149 @@
|
||||
// Copyright 2014 Team 254. All Rights Reserved.
|
||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||
//
|
||||
// Methods for configuring a Cisco Aironet AP1252AG access point for team SSIDs and VLANs.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const aironetTelnetPort = 23
|
||||
const (
|
||||
red1Vlan = 11
|
||||
red2Vlan = 12
|
||||
red3Vlan = 13
|
||||
blue1Vlan = 14
|
||||
blue2Vlan = 15
|
||||
blue3Vlan = 16
|
||||
)
|
||||
|
||||
var aironetMutex sync.Mutex
|
||||
|
||||
// Sets up wireless networks for the given set of teams.
|
||||
func ConfigureTeamWifi(red1, red2, red3, blue1, blue2, blue3 *Team) error {
|
||||
for _, team := range []*Team{red1, red2, red3, blue1, blue2, blue3} {
|
||||
if team != nil && (len(team.WpaKey) < 8 || len(team.WpaKey) > 63) {
|
||||
return fmt.Errorf("Invalid WPA key '%s' configured for team %d.", team.WpaKey, team.Id)
|
||||
}
|
||||
}
|
||||
|
||||
// Determine what new SSIDs are needed and build the commands to set them up.
|
||||
oldSsids, err := getSsids()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
addSsidsCommand := ""
|
||||
associateSsidsCommand := ""
|
||||
replaceSsid := func(team *Team, vlan int) {
|
||||
if team == nil {
|
||||
return
|
||||
}
|
||||
if oldSsids[strconv.Itoa(team.Id)] == vlan {
|
||||
delete(oldSsids, strconv.Itoa(team.Id))
|
||||
} else {
|
||||
addSsidsCommand += fmt.Sprintf("dot11 ssid %d\nvlan %d\nauthentication open\nauthentication "+
|
||||
"key-management wpa version 2\nmbssid guest-mode\nwpa-psk ascii %s\n", team.Id, vlan, team.WpaKey)
|
||||
associateSsidsCommand += fmt.Sprintf("ssid %d\n", team.Id)
|
||||
}
|
||||
}
|
||||
replaceSsid(red1, red1Vlan)
|
||||
replaceSsid(red2, red2Vlan)
|
||||
replaceSsid(red3, red3Vlan)
|
||||
replaceSsid(blue1, blue1Vlan)
|
||||
replaceSsid(blue2, blue2Vlan)
|
||||
replaceSsid(blue3, blue3Vlan)
|
||||
if len(addSsidsCommand) != 0 {
|
||||
associateSsidsCommand = "interface Dot11Radio1\n" + associateSsidsCommand
|
||||
}
|
||||
|
||||
// Build the command to remove the SSIDs that are no longer needed.
|
||||
removeSsidsCommand := ""
|
||||
for ssid, _ := range oldSsids {
|
||||
removeSsidsCommand += fmt.Sprintf("no dot11 ssid %s\n", ssid)
|
||||
}
|
||||
|
||||
command := removeSsidsCommand + addSsidsCommand + associateSsidsCommand
|
||||
if len(command) > 0 {
|
||||
_, err = runAironetConfigCommand(removeSsidsCommand + addSsidsCommand + associateSsidsCommand)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns a map of currently-configured SSIDs to VLANs.
|
||||
func getSsids() (map[string]int, error) {
|
||||
// Get the entire config dump.
|
||||
config, err := runAironetCommand("show running-config\n")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Parse out the SSIDs and VLANs from the config dump.
|
||||
re := regexp.MustCompile("(?s)dot11 ssid (\\w+)\\s+vlan (\\d+)")
|
||||
ssidMatches := re.FindAllStringSubmatch(config, -1)
|
||||
if ssidMatches == nil {
|
||||
// There are probably no SSIDs currently configured.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Build the map of SSID to VLAN.
|
||||
ssids := make(map[string]int)
|
||||
for _, match := range ssidMatches {
|
||||
vlan, _ := strconv.Atoi(match[2])
|
||||
ssids[match[1]] = vlan
|
||||
}
|
||||
return ssids, nil
|
||||
}
|
||||
|
||||
// Logs into the Aironet via Telnet and runs the given command in user exec mode. Reads the output and returns
|
||||
// it as a string.
|
||||
func runAironetCommand(command string) (string, error) {
|
||||
// Make sure multiple commands aren't being run at the same time.
|
||||
aironetMutex.Lock()
|
||||
defer aironetMutex.Unlock()
|
||||
|
||||
// Open a Telnet connection to the AP.
|
||||
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", eventSettings.ApAddress, aironetTelnetPort))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
// Login to the AP, send the command, and log out all at once.
|
||||
writer := bufio.NewWriter(conn)
|
||||
_, err = writer.WriteString(fmt.Sprintf("%s\n%s\nterminal length 0\n%sexit\n", eventSettings.ApUsername,
|
||||
eventSettings.ApPassword, command))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
err = writer.Flush()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Read the response.
|
||||
var reader bytes.Buffer
|
||||
_, err = reader.ReadFrom(conn)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return reader.String(), nil
|
||||
}
|
||||
|
||||
// Logs into the Aironet via Telnet and runs the given command in global configuration mode. Reads the output
|
||||
// and returns it as a string.
|
||||
func runAironetConfigCommand(command string) (string, error) {
|
||||
return runAironetCommand(fmt.Sprintf("config terminal\n%send\ncopy running-config startup-config\n\n",
|
||||
command))
|
||||
}
|
||||
306
ap_config.txt
Normal file
306
ap_config.txt
Normal file
@@ -0,0 +1,306 @@
|
||||
|
||||
!
|
||||
! Last configuration change at 05:36:53 UTC Sat Aug 16 2014 by patfair
|
||||
! NVRAM config last updated at 05:36:53 UTC Sat Aug 16 2014 by patfair
|
||||
! NVRAM config last updated at 05:36:53 UTC Sat Aug 16 2014 by patfair
|
||||
version 15.2
|
||||
no service pad
|
||||
service timestamps debug datetime msec
|
||||
service timestamps log datetime msec
|
||||
service password-encryption
|
||||
!
|
||||
hostname ChezyAP
|
||||
!
|
||||
logging rate-limit console 9
|
||||
!
|
||||
aaa new-model
|
||||
!
|
||||
!
|
||||
aaa authentication login default local
|
||||
aaa authorization exec default local
|
||||
!
|
||||
!
|
||||
!
|
||||
!
|
||||
!
|
||||
aaa session-id common
|
||||
!
|
||||
!
|
||||
dot11 syslog
|
||||
dot11 vlan-name Blue1 vlan 14
|
||||
dot11 vlan-name Blue2 vlan 15
|
||||
dot11 vlan-name Blue3 vlan 16
|
||||
dot11 vlan-name CheesyArena vlan 2
|
||||
dot11 vlan-name Red1 vlan 11
|
||||
dot11 vlan-name Red2 vlan 12
|
||||
dot11 vlan-name Red3 vlan 13
|
||||
!
|
||||
dot11 ssid 1
|
||||
vlan 11
|
||||
authentication open
|
||||
authentication key-management wpa version 2
|
||||
mbssid guest-mode
|
||||
wpa-psk ascii 7 0257550A5A575E701D
|
||||
!
|
||||
dot11 ssid 2
|
||||
vlan 12
|
||||
authentication open
|
||||
authentication key-management wpa version 2
|
||||
mbssid guest-mode
|
||||
wpa-psk ascii 7 06545D731E1C5B4B57
|
||||
!
|
||||
dot11 ssid 3
|
||||
vlan 13
|
||||
authentication open
|
||||
authentication key-management wpa version 2
|
||||
mbssid guest-mode
|
||||
wpa-psk ascii 7 115A4A564441585F57
|
||||
!
|
||||
dot11 ssid 4
|
||||
vlan 14
|
||||
authentication open
|
||||
authentication key-management wpa version 2
|
||||
mbssid guest-mode
|
||||
wpa-psk ascii 7 101A5D4D5143465F58
|
||||
!
|
||||
dot11 ssid 5
|
||||
vlan 15
|
||||
authentication open
|
||||
authentication key-management wpa version 2
|
||||
mbssid guest-mode
|
||||
wpa-psk ascii 7 00514653510E5E535A
|
||||
!
|
||||
dot11 ssid 6
|
||||
vlan 16
|
||||
authentication open
|
||||
authentication key-management wpa version 2
|
||||
mbssid guest-mode
|
||||
wpa-psk ascii 7 1441445D5A527C7D72
|
||||
!
|
||||
dot11 ssid Cheesy Arena
|
||||
vlan 2
|
||||
authentication open
|
||||
authentication key-management wpa version 2
|
||||
guest-mode
|
||||
wpa-psk ascii 7 030D5704100A22435C071D0A1001
|
||||
!
|
||||
crypto pki token default removal timeout 0
|
||||
!
|
||||
!
|
||||
username patfair privilege 15 password 7 0548545F70181C2F101343
|
||||
!
|
||||
!
|
||||
bridge irb
|
||||
!
|
||||
!
|
||||
interface Dot11Radio0
|
||||
no ip address
|
||||
no ip route-cache
|
||||
!
|
||||
encryption mode ciphers aes-ccm tkip
|
||||
!
|
||||
encryption vlan 2 mode ciphers aes-ccm tkip
|
||||
!
|
||||
ssid Cheesy Arena
|
||||
!
|
||||
antenna gain 0
|
||||
station-role root
|
||||
no dot11 extension aironet
|
||||
bridge-group 1
|
||||
bridge-group 1 subscriber-loop-control
|
||||
bridge-group 1 spanning-disabled
|
||||
bridge-group 1 block-unknown-source
|
||||
no bridge-group 1 source-learning
|
||||
no bridge-group 1 unicast-flooding
|
||||
!
|
||||
interface Dot11Radio0.2
|
||||
encapsulation dot1Q 2
|
||||
no ip route-cache
|
||||
bridge-group 2
|
||||
bridge-group 2 subscriber-loop-control
|
||||
bridge-group 2 spanning-disabled
|
||||
bridge-group 2 block-unknown-source
|
||||
no bridge-group 2 source-learning
|
||||
no bridge-group 2 unicast-flooding
|
||||
!
|
||||
interface Dot11Radio1
|
||||
no ip address
|
||||
no ip route-cache
|
||||
!
|
||||
encryption mode ciphers aes-ccm tkip
|
||||
!
|
||||
encryption vlan 11 mode ciphers aes-ccm tkip
|
||||
!
|
||||
encryption vlan 12 mode ciphers aes-ccm tkip
|
||||
!
|
||||
encryption vlan 13 mode ciphers aes-ccm tkip
|
||||
!
|
||||
encryption vlan 14 mode ciphers aes-ccm tkip
|
||||
!
|
||||
encryption vlan 15 mode ciphers aes-ccm tkip
|
||||
!
|
||||
encryption vlan 16 mode ciphers aes-ccm tkip
|
||||
!
|
||||
ssid 1
|
||||
!
|
||||
ssid 2
|
||||
!
|
||||
ssid 3
|
||||
!
|
||||
ssid 4
|
||||
!
|
||||
ssid 5
|
||||
!
|
||||
ssid 6
|
||||
!
|
||||
antenna gain 0
|
||||
dfs band 3 block
|
||||
mbssid
|
||||
channel dfs
|
||||
station-role root
|
||||
no dot11 extension aironet
|
||||
bridge-group 1
|
||||
bridge-group 1 subscriber-loop-control
|
||||
bridge-group 1 spanning-disabled
|
||||
bridge-group 1 block-unknown-source
|
||||
no bridge-group 1 source-learning
|
||||
no bridge-group 1 unicast-flooding
|
||||
!
|
||||
interface Dot11Radio1.11
|
||||
encapsulation dot1Q 11
|
||||
no ip route-cache
|
||||
bridge-group 11
|
||||
bridge-group 11 subscriber-loop-control
|
||||
bridge-group 11 spanning-disabled
|
||||
bridge-group 11 block-unknown-source
|
||||
no bridge-group 11 source-learning
|
||||
no bridge-group 11 unicast-flooding
|
||||
!
|
||||
interface Dot11Radio1.12
|
||||
encapsulation dot1Q 12
|
||||
no ip route-cache
|
||||
bridge-group 12
|
||||
bridge-group 12 subscriber-loop-control
|
||||
bridge-group 12 spanning-disabled
|
||||
bridge-group 12 block-unknown-source
|
||||
no bridge-group 12 source-learning
|
||||
no bridge-group 12 unicast-flooding
|
||||
!
|
||||
interface Dot11Radio1.13
|
||||
encapsulation dot1Q 13
|
||||
no ip route-cache
|
||||
bridge-group 13
|
||||
bridge-group 13 subscriber-loop-control
|
||||
bridge-group 13 spanning-disabled
|
||||
bridge-group 13 block-unknown-source
|
||||
no bridge-group 13 source-learning
|
||||
no bridge-group 13 unicast-flooding
|
||||
!
|
||||
interface Dot11Radio1.14
|
||||
encapsulation dot1Q 14
|
||||
no ip route-cache
|
||||
bridge-group 14
|
||||
bridge-group 14 subscriber-loop-control
|
||||
bridge-group 14 spanning-disabled
|
||||
bridge-group 14 block-unknown-source
|
||||
no bridge-group 14 source-learning
|
||||
no bridge-group 14 unicast-flooding
|
||||
!
|
||||
interface Dot11Radio1.15
|
||||
encapsulation dot1Q 15
|
||||
no ip route-cache
|
||||
bridge-group 15
|
||||
bridge-group 15 subscriber-loop-control
|
||||
bridge-group 15 spanning-disabled
|
||||
bridge-group 15 block-unknown-source
|
||||
no bridge-group 15 source-learning
|
||||
no bridge-group 15 unicast-flooding
|
||||
!
|
||||
interface Dot11Radio1.16
|
||||
encapsulation dot1Q 16
|
||||
no ip route-cache
|
||||
bridge-group 16
|
||||
bridge-group 16 subscriber-loop-control
|
||||
bridge-group 16 spanning-disabled
|
||||
bridge-group 16 block-unknown-source
|
||||
no bridge-group 16 source-learning
|
||||
no bridge-group 16 unicast-flooding
|
||||
!
|
||||
interface GigabitEthernet0
|
||||
no ip address
|
||||
no ip route-cache
|
||||
duplex auto
|
||||
speed auto
|
||||
bridge-group 1
|
||||
bridge-group 1 spanning-disabled
|
||||
no bridge-group 1 source-learning
|
||||
!
|
||||
interface GigabitEthernet0.2
|
||||
encapsulation dot1Q 2
|
||||
no ip route-cache
|
||||
bridge-group 2
|
||||
bridge-group 2 spanning-disabled
|
||||
no bridge-group 2 source-learning
|
||||
!
|
||||
interface GigabitEthernet0.11
|
||||
encapsulation dot1Q 11
|
||||
no ip route-cache
|
||||
bridge-group 11
|
||||
bridge-group 11 spanning-disabled
|
||||
no bridge-group 11 source-learning
|
||||
!
|
||||
interface GigabitEthernet0.12
|
||||
encapsulation dot1Q 12
|
||||
no ip route-cache
|
||||
bridge-group 12
|
||||
bridge-group 12 spanning-disabled
|
||||
no bridge-group 12 source-learning
|
||||
!
|
||||
interface GigabitEthernet0.13
|
||||
encapsulation dot1Q 13
|
||||
no ip route-cache
|
||||
bridge-group 13
|
||||
bridge-group 13 spanning-disabled
|
||||
no bridge-group 13 source-learning
|
||||
!
|
||||
interface GigabitEthernet0.14
|
||||
encapsulation dot1Q 14
|
||||
no ip route-cache
|
||||
bridge-group 14
|
||||
bridge-group 14 spanning-disabled
|
||||
no bridge-group 14 source-learning
|
||||
!
|
||||
interface GigabitEthernet0.15
|
||||
encapsulation dot1Q 15
|
||||
no ip route-cache
|
||||
bridge-group 15
|
||||
bridge-group 15 spanning-disabled
|
||||
no bridge-group 15 source-learning
|
||||
!
|
||||
interface GigabitEthernet0.16
|
||||
encapsulation dot1Q 16
|
||||
no ip route-cache
|
||||
bridge-group 16
|
||||
bridge-group 16 spanning-disabled
|
||||
no bridge-group 16 source-learning
|
||||
!
|
||||
interface BVI1
|
||||
ip address 10.0.0.60 255.0.0.0
|
||||
no ip route-cache
|
||||
!
|
||||
ip default-gateway 10.0.0.1
|
||||
ip http server
|
||||
ip http authentication aaa
|
||||
no ip http secure-server
|
||||
ip http help-path http://www.cisco.com/warp/public/779/smbiz/prodconfig/help/eag
|
||||
!
|
||||
bridge 1 route ip
|
||||
!
|
||||
!
|
||||
!
|
||||
line con 0
|
||||
line vty 0 4
|
||||
transport input all
|
||||
!
|
||||
sntp server 216.66.0.142
|
||||
end
|
||||
17
arena.go
17
arena.go
@@ -197,6 +197,8 @@ func (arena *Arena) LoadMatch(match *Match) error {
|
||||
return err
|
||||
}
|
||||
|
||||
arena.SetupNetwork()
|
||||
|
||||
// Reset the realtime scores.
|
||||
arena.redRealtimeScore = new(RealtimeScore)
|
||||
arena.blueRealtimeScore = new(RealtimeScore)
|
||||
@@ -256,10 +258,25 @@ func (arena *Arena) SubstituteTeam(teamId int, station string) error {
|
||||
case "B3":
|
||||
arena.currentMatch.Blue3 = teamId
|
||||
}
|
||||
arena.SetupNetwork()
|
||||
arena.matchLoadTeamsNotifier.Notify(nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Asynchronously reconfigures the networking hardware for the new set of teams.
|
||||
func (arena *Arena) SetupNetwork() {
|
||||
if eventSettings.NetworkSecurityEnabled {
|
||||
go func() {
|
||||
err := ConfigureTeamWifi(arena.AllianceStations["R1"].team, arena.AllianceStations["R2"].team,
|
||||
arena.AllianceStations["R3"].team, arena.AllianceStations["B1"].team,
|
||||
arena.AllianceStations["B2"].team, arena.AllianceStations["B3"].team)
|
||||
if err != nil {
|
||||
log.Printf("Failed to configure team WiFi: %s", err.Error())
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
// Returns nil if the match can be started, and an error otherwise.
|
||||
func (arena *Arena) CheckCanStartMatch() error {
|
||||
if arena.MatchState != PRE_MATCH {
|
||||
|
||||
@@ -8,7 +8,8 @@ CREATE TABLE teams (
|
||||
country VARCHAR(255),
|
||||
rookieyear int,
|
||||
robotname VARCHAR(255),
|
||||
accomplishments VARCHAR(1000)
|
||||
accomplishments VARCHAR(1000),
|
||||
wpakey VARCHAR(16)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
|
||||
@@ -10,7 +10,11 @@ CREATE TABLE event_settings (
|
||||
tbapublishingenabled bool,
|
||||
tbaeventcode VARCHAR(16),
|
||||
tbasecretid VARCHAR(255),
|
||||
tbasecret VARCHAR(255)
|
||||
tbasecret VARCHAR(255),
|
||||
networksecurityenabled bool,
|
||||
apaddress VARCHAR(255),
|
||||
apusername VARCHAR(255),
|
||||
appassword VARCHAR(255)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
|
||||
@@ -17,6 +17,10 @@ type EventSettings struct {
|
||||
TbaEventCode string
|
||||
TbaSecretId string
|
||||
TbaSecret string
|
||||
NetworkSecurityEnabled bool
|
||||
ApAddress string
|
||||
ApUsername string
|
||||
ApPassword string
|
||||
}
|
||||
|
||||
const eventSettingsId = 0
|
||||
|
||||
@@ -45,6 +45,10 @@ func SettingsPostHandler(w http.ResponseWriter, r *http.Request) {
|
||||
eventSettings.TbaEventCode = r.PostFormValue("tbaEventCode")
|
||||
eventSettings.TbaSecretId = r.PostFormValue("tbaSecretId")
|
||||
eventSettings.TbaSecret = r.PostFormValue("tbaSecret")
|
||||
eventSettings.NetworkSecurityEnabled = r.PostFormValue("networkSecurityEnabled") == "on"
|
||||
eventSettings.ApAddress = r.PostFormValue("apAddress")
|
||||
eventSettings.ApUsername = r.PostFormValue("apUsername")
|
||||
eventSettings.ApPassword = r.PostFormValue("apPassword")
|
||||
err := db.SaveEventSettings(eventSettings)
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
|
||||
@@ -8,6 +8,7 @@ package main
|
||||
import (
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"github.com/dchest/uniuri"
|
||||
"github.com/gorilla/mux"
|
||||
"html"
|
||||
"html/template"
|
||||
@@ -19,6 +20,8 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
const wpaKeyLength = 8
|
||||
|
||||
var officialTeamInfoUrl = "https://my.usfirst.org/frc/scoring/index.lasso?page=teamlist"
|
||||
var officialTeamInfo map[int][]string
|
||||
|
||||
@@ -124,6 +127,13 @@ func TeamEditPostHandler(w http.ResponseWriter, r *http.Request) {
|
||||
team.RookieYear, _ = strconv.Atoi(r.PostFormValue("rookieYear"))
|
||||
team.RobotName = r.PostFormValue("robotName")
|
||||
team.Accomplishments = r.PostFormValue("accomplishments")
|
||||
if eventSettings.NetworkSecurityEnabled {
|
||||
team.WpaKey = r.PostFormValue("wpaKey")
|
||||
if len(team.WpaKey) < 8 || len(team.WpaKey) > 63 {
|
||||
handleWebErr(w, fmt.Errorf("WPA key must be between 8 and 63 characters."))
|
||||
return
|
||||
}
|
||||
}
|
||||
err = db.SaveTeam(team)
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
@@ -168,6 +178,28 @@ func TeamsPublishHandler(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, "/setup/teams", 302)
|
||||
}
|
||||
|
||||
// Generates random WPA keys and saves them to the team models.
|
||||
func TeamsGenerateWpaKeysHandler(w http.ResponseWriter, r *http.Request) {
|
||||
generateAllKeys := false
|
||||
if all, ok := r.URL.Query()["all"]; ok {
|
||||
generateAllKeys = all[0] == "true"
|
||||
}
|
||||
|
||||
teams, err := db.GetAllTeams()
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
for _, team := range teams {
|
||||
if len(team.WpaKey) == 0 || generateAllKeys {
|
||||
team.WpaKey = uniuri.NewLen(wpaKeyLength)
|
||||
db.SaveTeam(&team)
|
||||
}
|
||||
}
|
||||
|
||||
http.Redirect(w, r, "/setup/teams", 302)
|
||||
}
|
||||
|
||||
func renderTeams(w http.ResponseWriter, r *http.Request, showErrorMessage bool) {
|
||||
teams, err := db.GetAllTeams()
|
||||
if err != nil {
|
||||
|
||||
1
team.go
1
team.go
@@ -15,6 +15,7 @@ type Team struct {
|
||||
RookieYear int
|
||||
RobotName string
|
||||
Accomplishments string
|
||||
WpaKey string
|
||||
}
|
||||
|
||||
func (database *Database) CreateTeam(team *Team) error {
|
||||
|
||||
@@ -54,6 +54,14 @@
|
||||
<textarea class="form-control" rows="5" name="accomplishments">{{.Team.Accomplishments}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
{{if .EventSettings.NetworkSecurityEnabled}}
|
||||
<div class="form-group">
|
||||
<label class="col-lg-3 control-label">WPA Key</label>
|
||||
<div class="col-lg-9">
|
||||
<input type="text" class="form-control" name="wpaKey" value="{{.Team.WpaKey}}">
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="form-group">
|
||||
<div class="col-lg-9 col-lg-offset-3">
|
||||
<a href="/setup/teams"><button type="button" class="btn btn-default">Cancel</button></a>
|
||||
|
||||
@@ -113,6 +113,35 @@
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Hardware</legend>
|
||||
<p>Enable this setting if you have a Cisco Aironet AP1252AG access point and Catalyst 3500-series
|
||||
switch available, for isolating each team to its own SSID and VLAN.</p>
|
||||
<div class="form-group">
|
||||
<label class="col-lg-7 control-label">Enable advanced network security</label>
|
||||
<div class="col-lg-1 checkbox">
|
||||
<input type="checkbox" name="networkSecurityEnabled"{{if .NetworkSecurityEnabled}} checked{{end}}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-lg-5 control-label">AP Address</label>
|
||||
<div class="col-lg-7">
|
||||
<input type="text" class="form-control" name="apAddress" value="{{.ApAddress}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-lg-5 control-label">AP Username</label>
|
||||
<div class="col-lg-7">
|
||||
<input type="text" class="form-control" name="apUsername" value="{{.ApUsername}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-lg-5 control-label">AP Password</label>
|
||||
<div class="col-lg-7">
|
||||
<input type="password" class="form-control" name="apPassword" value="{{.ApPassword}}">
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div class="form-group">
|
||||
<div class="col-lg-7 col-lg-offset-5">
|
||||
<button type="submit" class="btn btn-info">Save</button>
|
||||
|
||||
@@ -31,6 +31,14 @@
|
||||
</button>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .EventSettings.NetworkSecurityEnabled}}
|
||||
<div class="form-group">
|
||||
<a href="/setup/teams/generate_wpa_keys?all=true" class="btn btn-primary">Generate All WPA Keys</a>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<a href="/setup/teams/generate_wpa_keys?all=false" class="btn btn-primary">Generate Missing WPA Keys</a>
|
||||
</div>
|
||||
{{end}}
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
1
web.go
1
web.go
@@ -112,6 +112,7 @@ func newHandler() http.Handler {
|
||||
router.HandleFunc("/setup/teams/{id}/edit", TeamEditPostHandler).Methods("POST")
|
||||
router.HandleFunc("/setup/teams/{id}/delete", TeamDeletePostHandler).Methods("POST")
|
||||
router.HandleFunc("/setup/teams/publish", TeamsPublishHandler).Methods("POST")
|
||||
router.HandleFunc("/setup/teams/generate_wpa_keys", TeamsGenerateWpaKeysHandler).Methods("GET")
|
||||
router.HandleFunc("/setup/schedule", ScheduleGetHandler).Methods("GET")
|
||||
router.HandleFunc("/setup/schedule/generate", ScheduleGeneratePostHandler).Methods("POST")
|
||||
router.HandleFunc("/setup/schedule/save", ScheduleSavePostHandler).Methods("POST")
|
||||
|
||||
Reference in New Issue
Block a user