| // 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 { |
| 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" |
| } |
| }, |
| "vlan": func(ips []string) string { |
| if (len(ips) > 0) { |
| return strings.Split(ips[0], ".")[2] |
| } else { |
| 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 { |
| 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 |
| } |