blob: e880c8697037674a57458d0e246a12d8bf042fec [file] [log] [blame]
// Copyright 2016 Open Networking Foundation
//
// 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 (
"bytes"
"encoding/json"
"net/http"
"strings"
"text/template"
)
type GenerationOptions struct {
SwitchCount int `json:"switchcount"`
HostCount int `json:"hostcount"`
}
func (c *Config) configGenHandler(w http.ResponseWriter, r *http.Request) {
var options GenerationOptions
deviceMap := make(map[string]*onosDevice)
decoder := json.NewDecoder(r.Body)
defer r.Body.Close()
if err := decoder.Decode(&options); err != nil {
log.Errorf("Unable to decode provisioning request options: %s", err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
var devices onosDevices
err := c.fetch("/onos/v1/devices", &devices)
if err != nil {
log.Errorf("Unable to retrieve device information from controller: %s", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// If the request specified the number of switches, validate we have that
// exact number
if options.SwitchCount > 0 && len(devices.Devices) != options.SwitchCount {
log.Errorf("Expecting %d switch(es), found %d, no configuration generated",
options.SwitchCount, len(devices.Devices))
http.Error(w, "Expected switch count mismatch",
http.StatusInternalServerError)
return
}
for _, device := range devices.Devices {
deviceMap[device.Id] = device
device.Mac = splitString(device.ChassisId, 2, ":")
}
var hosts onosHosts
err = c.fetch("/onos/v1/hosts", &hosts)
if err != nil {
log.Errorf("Unable to retrieve host information from controller: %s", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// If the request specified the number of hosts, validate we have that
// exact number
if options.HostCount > 0 && len(hosts.Hosts) != options.HostCount {
log.Errorf("Expecting %d host(s), found %d, no configuration generated",
options.HostCount, len(hosts.Hosts))
http.Error(w, "Expected host count mismatch",
http.StatusInternalServerError)
return
}
// Use a simple heuristic to determine which switches are edge routers
// and which are not
markEdgeRouters(deviceMap, hosts)
// Generate the configuration file
cfg := onosConfig{
Devices: devices.Devices,
Hosts: hosts.Hosts,
}
funcMap := template.FuncMap{
// The name "inc" is what the function will be called in the template text.
"add": func(a, b int) int {
return a + b
},
"gateway": func(ips []string) string {
// Find the first v4 address, as determined by
// not having a ':' in the IP
for _, ip := range ips {
if strings.Index(ip, ":") == -1 {
parts := strings.Split(ip, ".")
targetIp := ""
for _, v := range parts[:len(parts)-1] {
targetIp = targetIp + v + "."
}
return targetIp + "254/24"
}
}
return "0.0.0.254/24"
},
"vlan": func(ips []string) string {
// Find the first v4 address, as determined by
// not having a ':' in the IP
for _, ip := range ips {
if strings.Index(ip, ":") == -1 {
return strings.Split(ip, ".")[2]
}
}
return "0"
},
}
tpl, err := template.New("netconfig.tpl").Funcs(funcMap).ParseFiles("netconfig.tpl")
if err != nil {
log.Errorf("Unable to parse template: %s", err)
http.Error(w, "Template parse error", http.StatusInternalServerError)
return
}
// Write template to buffer, so if there is an error we can return an
// http error
buf := new(bytes.Buffer)
err = tpl.Execute(buf, cfg)
if err != nil {
log.Errorf("Unexpected error while processing template: %s", err)
http.Error(w, "Template processing error", http.StatusInternalServerError)
}
w.Write(buf.Bytes())
}
// markEdgeRouters use hueristic to determine and mark switches that act
// as edge routers
func markEdgeRouters(dm map[string]*onosDevice, hosts onosHosts) {
// Walk the list of know compute nodes (hosts) and if the compute node
// is connected to a switch, then that switch is an edge router
for _, host := range hosts.Hosts {
// Assume that each host has only one location for now
if device, ok := dm[host.Locations[0].ElementID]; ok {
(*device).IsEdgeRouter = true
}
}
}
// splitString used to convert a string to a psudeo MAC address by
// splitting and separating it with a colon
func splitString(src string, n int, sep string) string {
r := ""
for i, c := range src {
if i > 0 && i%n == 0 {
r += sep
}
r += string(c)
}
return r
}
// fetch fetch the specified data from ONOS
func (c *Config) fetch(path string, data interface{}) error {
resp, err := http.Get(c.connect + path)
if err != nil {
return err
}
defer resp.Body.Close()
decoder := json.NewDecoder(resp.Body)
err = decoder.Decode(data)
return err
}