blob: a12da9833c98bcaef963db8bcb6842ce1ce5b5a1 [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 {
100 if len(ips) > 0 {
101 parts := strings.Split(ips[0], ".")
102 ip := ""
103 for _, v := range parts[:len(parts)-1] {
104 ip = ip + v + "."
105 }
106 return ip + "254/24"
107 } else {
108 return "0.0.0.254/24"
109 }
110 },
Jonathan Harte8f66d12017-08-21 16:05:42 -0700111 "vlan": func(ips []string) string {
112 if (len(ips) > 0) {
113 return strings.Split(ips[0], ".")[2]
114 } else {
115 return "0"
116 }
117 },
David K. Bainbridgec04fd552016-11-08 18:39:29 -0800118 }
119
120 tpl, err := template.New("netconfig.tpl").Funcs(funcMap).ParseFiles("netconfig.tpl")
121 if err != nil {
122 log.Errorf("Unable to parse template: %s", err)
123 http.Error(w, "Template parse error", http.StatusInternalServerError)
124 return
125 }
126
127 // Write template to buffer, so if there is an error we can return an
128 // http error
129 buf := new(bytes.Buffer)
130 err = tpl.Execute(buf, cfg)
131 if err != nil {
132 log.Errorf("Unexpected error while processing template: %s", err)
133 http.Error(w, "Template processing error", http.StatusInternalServerError)
134 }
135
136 w.Write(buf.Bytes())
137}
138
139// markEdgeRouters use hueristic to determine and mark switches that act
140// as edge routers
141func markEdgeRouters(dm map[string]*onosDevice, hosts onosHosts) {
142 // Walk the list of know compute nodes (hosts) and if the compute node
143 // is connected to a switch, then that switch is an edge router
144 for _, host := range hosts.Hosts {
145 if device, ok := dm[host.Location.ElementID]; ok {
146 (*device).IsEdgeRouter = true
147 }
148 }
149}
150
151// splitString used to convert a string to a psudeo MAC address by
152// splitting and separating it with a colon
153func splitString(src string, n int, sep string) string {
154 r := ""
155 for i, c := range src {
156 if i > 0 && i%n == 0 {
157 r += sep
158 }
159 r += string(c)
160 }
161 return r
162}
163
164// fetch fetch the specified data from ONOS
165func (c *Config) fetch(path string, data interface{}) error {
166 resp, err := http.Get(c.connect + path)
167 if err != nil {
168 return err
169 }
170
171 defer resp.Body.Close()
172 decoder := json.NewDecoder(resp.Body)
173 err = decoder.Decode(data)
174 return err
175}