blob: b0460f04201e6e077b027ff898b306fe0043d56c [file] [log] [blame]
// Copyright 2016 Open Networking Laboratory
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"encoding/json"
"errors"
"fmt"
"github.com/gorilla/mux"
"github.com/kelseyhightower/envconfig"
"io/ioutil"
"log"
"net/http"
"os"
"strings"
"text/template"
)
var unmarshalError = errors.New("Error Unmarshaling the JSON Data\n")
type Config struct {
Port string `default:"8181"`
IP string `default:"127.0.0.1"`
SwitchCount int `default:"0"`
HostCount int `default:"0"`
Username string `default:"karaf"`
Password string `default:"karaf"`
LogLevel string `default:"warning" envconfig:"LOG_LEVEL"`
LogFormat string `default:"text" envconfig:"LOG_FORMAT"`
ConfigServerPort string `default:"1337"`
ConfigServerIP string `default:"127.0.0.1"`
}
type hosts struct {
Host []struct {
Mac string `json:"mac"`
IpAddresses []string `json:"ipAddresses"`
Location struct {
ElementID string `json:"elementId`
Port string `json:"port"`
} `json:"location"`
Comma string
Gateway string
} `json:"hosts"`
}
type devices struct {
Device []struct {
Id string `json:"id"`
ChassisId string `json:"chassisId"`
Annotations struct {
ManagementAddress string `json:"managementAddress"`
} `json:"annotations"`
Comma string `default:","`
} `json:"devices"`
}
type onosLinks struct {
Links []struct {
Src struct {
Port string `json:"port"`
Device string `json:"device"`
} `json:"src"`
Dst struct {
Port string `json:"port"`
Device string `json:"device"`
} `json:"dst"`
} `json:"links"`
}
type linkStructJSON struct {
Val string
Comma string
}
type ConfigParam struct {
SwitchCount int `json:"switchcount"`
HostCount int `json:"hostcount"`
ONOSIP string `json:"onosip"`
}
var c Config
func main() {
err := envconfig.Process("CONFIGGEN", &c)
if err != nil {
log.Fatalf("[ERROR] Unable to parse configuration options : %s", err)
}
router := mux.NewRouter()
router.HandleFunc("/config/", ConfigGenHandler).Methods("POST")
http.Handle("/", router)
fmt.Println("Config Generator server listening at: " + c.ConfigServerIP + ":" + c.ConfigServerPort)
http.ListenAndServe(c.ConfigServerIP+":"+c.ConfigServerPort, nil)
}
func ConfigGenHandler(w http.ResponseWriter, r *http.Request) {
var para ConfigParam
decoder := json.NewDecoder(r.Body)
defer r.Body.Close()
if err := decoder.Decode(&para); err != nil {
fmt.Errorf("Unable to decode request to provision : %s", err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
c.HostCount = para.HostCount
c.SwitchCount = para.SwitchCount
c.IP = para.ONOSIP
onos := "http://" + c.Username + ":" + c.Password + "@" + c.IP + ":" + c.Port
err := os.Remove("network-cfg.json")
if err != nil {
log.Println("Warning: no file called network-cfg.json (ignore if this is the first run)")
}
err = generateDevicesJSON(onos)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, err.Error())
return
}
err = generateLinkJSON(onos)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, err.Error())
return
}
err = generateHostJSON(onos)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, err.Error())
return
}
fmt.Println("Config file generated: network-cfg.json")
data, err := ioutil.ReadFile("network-cfg.json")
check(err)
w.WriteHeader(http.StatusAccepted)
fmt.Fprintf(w, string(data))
}
func writeToFile(object interface{}, t string) {
f, err := os.OpenFile("network-cfg.json", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
panic(err)
}
defer f.Close()
tpl, err := template.ParseFiles(t)
check(err)
err = tpl.Execute(f, object)
check(err)
}
func generateDevicesJSON(onos string) error {
ds, err := getData(onos + "/onos/v1/devices")
if err != nil {
return err
}
var d devices
err = json.Unmarshal(ds, &d)
if err != nil {
return unmarshalError
}
if len(d.Device) != c.SwitchCount {
_ = os.Remove("network-cfg.json")
log.Println("[INFO] Cleaning up unfinished config file")
e := fmt.Sprintf("[ERROR] Number of switches configured don't match actual switches connected to the controller. Configured: %d, connected: %d", c.SwitchCount, len(d.Device))
log.Println(e)
return errors.New(e)
}
for k, _ := range d.Device {
d.Device[k].Comma = ","
if k >= len(d.Device)-1 {
d.Device[k].Comma = ""
}
}
writeToFile(d.Device, "devices.tpl")
return nil
}
func generateHostJSON(onos string) error {
hs, err := getData(onos + "/onos/v1/hosts")
if err != nil {
return err
}
var h hosts
err = json.Unmarshal(hs, &h)
if err != nil {
return unmarshalError
}
if len(h.Host) != c.HostCount {
_ = os.Remove("network-cfg.json")
log.Println("[INFO] Cleaning up unfinished config file")
e := fmt.Sprintf("[ERROR] Number of hosts configured don't match actual hosts visible to the controller. Configured: %d, connected: %d", c.HostCount, len(h.Host))
log.Println(e)
return errors.New(e)
}
for k, _ := range h.Host {
h.Host[k].Comma = ","
if k >= len(h.Host)-1 {
h.Host[k].Comma = ""
}
parts := strings.Split(h.Host[k].IpAddresses[0], ".")
ip := ""
for _, v := range parts[:len(parts)-1] {
ip = ip + v + "."
}
h.Host[k].Gateway = ip
}
writeToFile(h.Host, "ports.tpl")
writeToFile(h.Host, "hosts.tpl")
return nil
}
func generateLinkJSON(onos string) error {
links, err := getData(onos + "/onos/v1/links")
if err != nil {
return err
}
var l onosLinks
err = json.Unmarshal(links, &l)
if err != nil {
return unmarshalError
}
var in []linkStructJSON
for k, v := range l.Links {
comma := ","
val := fmt.Sprint(v.Src.Device + "/" + v.Src.Port + "-" + v.Dst.Device + "/" + v.Dst.Port)
if k >= len(l.Links)-1 {
comma = ""
}
tmp := linkStructJSON{val, comma}
in = append(in, tmp)
}
writeToFile(in, "links.tpl")
return nil
}
func getData(url string) ([]byte, error) {
resp, err := http.Get(url)
if err != nil {
return nil, errors.New("Error getting data from the URL\n")
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, errors.New("Error reading data from response body\n")
}
return body, nil
}
func check(e error) {
if e != nil {
panic(e)
}
}