blob: e880c8697037674a57458d0e246a12d8bf042fec [file] [log] [blame]
Brian O'Connor6a37ea92017-08-03 22:45:59 -07001// Copyright 2016 Open Networking Foundation
David K. Bainbridgec04fd552016-11-08 18:39:29 -08002//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package main
15
16import (
17 "bytes"
18 "encoding/json"
19 "net/http"
20 "strings"
21 "text/template"
22)
23
24type GenerationOptions struct {
25 SwitchCount int `json:"switchcount"`
26 HostCount int `json:"hostcount"`
27}
28
29func (c *Config) configGenHandler(w http.ResponseWriter, r *http.Request) {
30 var options GenerationOptions
31
32 deviceMap := make(map[string]*onosDevice)
33
34 decoder := json.NewDecoder(r.Body)
35 defer r.Body.Close()
36 if err := decoder.Decode(&options); err != nil {
37 log.Errorf("Unable to decode provisioning request options: %s", err)
38 http.Error(w, err.Error(), http.StatusBadRequest)
39 return
40 }
41
42 var devices onosDevices
43 err := c.fetch("/onos/v1/devices", &devices)
44 if err != nil {
45 log.Errorf("Unable to retrieve device information from controller: %s", err)
46 http.Error(w, err.Error(), http.StatusInternalServerError)
47 return
48 }
49
50 // If the request specified the number of switches, validate we have that
51 // exact number
52 if options.SwitchCount > 0 && len(devices.Devices) != options.SwitchCount {
53 log.Errorf("Expecting %d switch(es), found %d, no configuration generated",
54 options.SwitchCount, len(devices.Devices))
55 http.Error(w, "Expected switch count mismatch",
56 http.StatusInternalServerError)
57 return
58 }
59
60 for _, device := range devices.Devices {
61 deviceMap[device.Id] = device
62 device.Mac = splitString(device.ChassisId, 2, ":")
63 }
64
65 var hosts onosHosts
66 err = c.fetch("/onos/v1/hosts", &hosts)
67 if err != nil {
68 log.Errorf("Unable to retrieve host information from controller: %s", err)
69 http.Error(w, err.Error(), http.StatusInternalServerError)
70 return
71
72 }
73
74 // If the request specified the number of hosts, validate we have that
75 // exact number
76 if options.HostCount > 0 && len(hosts.Hosts) != options.HostCount {
Jonathan Harte8f66d12017-08-21 16:05:42 -070077 log.Errorf("Expecting %d host(s), found %d, no configuration generated",
David K. Bainbridgec04fd552016-11-08 18:39:29 -080078 options.HostCount, len(hosts.Hosts))
79 http.Error(w, "Expected host count mismatch",
80 http.StatusInternalServerError)
81 return
82 }
83
84 // Use a simple heuristic to determine which switches are edge routers
85 // and which are not
86 markEdgeRouters(deviceMap, hosts)
87
88 // Generate the configuration file
89 cfg := onosConfig{
90 Devices: devices.Devices,
91 Hosts: hosts.Hosts,
92 }
93
94 funcMap := template.FuncMap{
95 // The name "inc" is what the function will be called in the template text.
96 "add": func(a, b int) int {
97 return a + b
98 },
99 "gateway": func(ips []string) string {
David K. Bainbridge566d9572017-09-21 10:51:53 -0700100 // Find the first v4 address, as determined by
101 // not having a ':' in the IP
102 for _, ip := range ips {
103 if strings.Index(ip, ":") == -1 {
104 parts := strings.Split(ip, ".")
105 targetIp := ""
106 for _, v := range parts[:len(parts)-1] {
107 targetIp = targetIp + v + "."
108 }
109 return targetIp + "254/24"
David K. Bainbridgec04fd552016-11-08 18:39:29 -0800110 }
David K. Bainbridgec04fd552016-11-08 18:39:29 -0800111 }
David K. Bainbridge566d9572017-09-21 10:51:53 -0700112 return "0.0.0.254/24"
David K. Bainbridgec04fd552016-11-08 18:39:29 -0800113 },
Jonathan Harte8f66d12017-08-21 16:05:42 -0700114 "vlan": func(ips []string) string {
David K. Bainbridge566d9572017-09-21 10:51:53 -0700115 // Find the first v4 address, as determined by
116 // not having a ':' in the IP
117 for _, ip := range ips {
118 if strings.Index(ip, ":") == -1 {
119 return strings.Split(ip, ".")[2]
120 }
Jonathan Harte8f66d12017-08-21 16:05:42 -0700121 }
David K. Bainbridge566d9572017-09-21 10:51:53 -0700122 return "0"
Jonathan Harte8f66d12017-08-21 16:05:42 -0700123 },
David K. Bainbridgec04fd552016-11-08 18:39:29 -0800124 }
125
126 tpl, err := template.New("netconfig.tpl").Funcs(funcMap).ParseFiles("netconfig.tpl")
127 if err != nil {
128 log.Errorf("Unable to parse template: %s", err)
129 http.Error(w, "Template parse error", http.StatusInternalServerError)
130 return
131 }
132
133 // Write template to buffer, so if there is an error we can return an
134 // http error
135 buf := new(bytes.Buffer)
136 err = tpl.Execute(buf, cfg)
137 if err != nil {
138 log.Errorf("Unexpected error while processing template: %s", err)
139 http.Error(w, "Template processing error", http.StatusInternalServerError)
140 }
141
142 w.Write(buf.Bytes())
143}
144
145// markEdgeRouters use hueristic to determine and mark switches that act
146// as edge routers
147func markEdgeRouters(dm map[string]*onosDevice, hosts onosHosts) {
148 // Walk the list of know compute nodes (hosts) and if the compute node
149 // is connected to a switch, then that switch is an edge router
150 for _, host := range hosts.Hosts {
Jonathan Hartf5ce31a2018-01-31 15:48:21 -0800151 // Assume that each host has only one location for now
152 if device, ok := dm[host.Locations[0].ElementID]; ok {
David K. Bainbridgec04fd552016-11-08 18:39:29 -0800153 (*device).IsEdgeRouter = true
154 }
155 }
156}
157
158// splitString used to convert a string to a psudeo MAC address by
159// splitting and separating it with a colon
160func splitString(src string, n int, sep string) string {
161 r := ""
162 for i, c := range src {
163 if i > 0 && i%n == 0 {
164 r += sep
165 }
166 r += string(c)
167 }
168 return r
169}
170
171// fetch fetch the specified data from ONOS
172func (c *Config) fetch(path string, data interface{}) error {
173 resp, err := http.Get(c.connect + path)
174 if err != nil {
175 return err
176 }
177
178 defer resp.Body.Close()
179 decoder := json.NewDecoder(resp.Body)
180 err = decoder.Decode(data)
181 return err
182}