CORD-313 refactor configuration generator
Change-Id: I4428ff0b67ee8d6ebb9b7009cd82413416c25a84
diff --git a/config-generator/handlers.go b/config-generator/handlers.go
new file mode 100644
index 0000000..eabdbe3
--- /dev/null
+++ b/config-generator/handlers.go
@@ -0,0 +1,168 @@
+// 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 (
+ "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 generaged",
+ 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 {
+ if len(ips) > 0 {
+ parts := strings.Split(ips[0], ".")
+ ip := ""
+ for _, v := range parts[:len(parts)-1] {
+ ip = ip + v + "."
+ }
+ return ip + "254/24"
+ } else {
+ return "0.0.0.254/24"
+ }
+ },
+ }
+
+ 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 {
+ if device, ok := dm[host.Location.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
+}