blob: bcc3ddd32875dd8251771f85a5c4537930a78312 [file] [log] [blame]
David K. Bainbridgedf9df632016-07-07 18:47:46 -07001// Copyright 2016 Open Networking Laboratory
2//
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.
gunjan56e19d272016-07-07 14:23:26 -070014package main
15
16import (
17 "encoding/json"
gunjan5c79837e2016-07-09 03:31:27 -070018 "errors"
gunjan56e19d272016-07-07 14:23:26 -070019 "fmt"
gunjan5c79837e2016-07-09 03:31:27 -070020 "github.com/gorilla/mux"
21 "github.com/kelseyhightower/envconfig"
gunjan56e19d272016-07-07 14:23:26 -070022 "io/ioutil"
gunjan5c79837e2016-07-09 03:31:27 -070023 "log"
gunjan56e19d272016-07-07 14:23:26 -070024 "net/http"
25 "os"
26 "strings"
27 "text/template"
28)
29
gunjan5c79837e2016-07-09 03:31:27 -070030type Config struct {
31 Port string `default:"8181"`
32 IP string `default:"127.0.0.1"`
gunjan5d6ca1772016-07-20 18:11:04 -070033 SwitchCount int `default:"0"`
34 HostCount int `default:"0"`
gunjan5c79837e2016-07-09 03:31:27 -070035 Username string `default:"karaf"`
36 Password string `default:"karaf"`
37 LogLevel string `default:"warning" envconfig:"LOG_LEVEL"`
38 LogFormat string `default:"text" envconfig:"LOG_FORMAT"`
39 ConfigServerPort string `default:"1337"`
40 ConfigServerIP string `default:"127.0.0.1"`
41}
42
gunjan56e19d272016-07-07 14:23:26 -070043type hosts struct {
44 Host []struct {
45 Mac string `json:"mac"`
46 IpAddresses []string `json:"ipAddresses"`
47 Location struct {
48 ElementID string `json:"elementId`
49 Port string `json:"port"`
50 } `json:"location"`
51 Comma string
52 Gateway string
53 } `json:"hosts"`
54}
55
56type devices struct {
57 Device []struct {
58 Id string `json:"id"`
59 ChassisId string `json:"chassisId"`
60 Annotations struct {
61 ManagementAddress string `json:"managementAddress"`
62 } `json:"annotations"`
63 Comma string `default:","`
64 } `json:"devices"`
65}
66
67type onosLinks struct {
gunjan5c79837e2016-07-09 03:31:27 -070068 Links []struct {
69 Src struct {
70 Port string `json:"port"`
71 Device string `json:"device"`
72 } `json:"src"`
73 Dst struct {
74 Port string `json:"port"`
75 Device string `json:"device"`
76 } `json:"dst"`
77 } `json:"links"`
gunjan56e19d272016-07-07 14:23:26 -070078}
79
80type linkStructJSON struct {
81 Val string
82 Comma string
83}
84
gunjan5c79837e2016-07-09 03:31:27 -070085type ConfigParam struct {
86 SwitchCount int `json:"switchcount"`
87 HostCount int `json:"hostcount"`
88}
89
90var c Config
91
gunjan56e19d272016-07-07 14:23:26 -070092func main() {
gunjan5c79837e2016-07-09 03:31:27 -070093
94 err := envconfig.Process("CONFIGGEN", &c)
95 if err != nil {
96 log.Fatalf("[ERROR] Unable to parse configuration options : %s", err)
97 }
98
99 router := mux.NewRouter()
100 router.HandleFunc("/config/", ConfigGenHandler).Methods("POST")
101 http.Handle("/", router)
102
103 fmt.Println("Config Generator server listening at: " + c.ConfigServerIP + ":" + c.ConfigServerPort)
104
105 http.ListenAndServe(c.ConfigServerIP+":"+c.ConfigServerPort, nil)
106
107}
108
109func ConfigGenHandler(w http.ResponseWriter, r *http.Request) {
110 var para ConfigParam
111
112 decoder := json.NewDecoder(r.Body)
113 defer r.Body.Close()
114 if err := decoder.Decode(&para); err != nil {
115 fmt.Errorf("Unable to decode request to provision : %s", err)
116 http.Error(w, err.Error(), http.StatusBadRequest)
117 return
118 }
119
120 c.HostCount = para.HostCount
121 c.SwitchCount = para.SwitchCount
122
123 onos := "http://" + c.Username + ":" + c.Password + "@" + c.IP + ":" + c.Port
gunjan56e19d272016-07-07 14:23:26 -0700124
125 err := os.Remove("network-cfg.json")
126 if err != nil {
gunjan5c79837e2016-07-09 03:31:27 -0700127 log.Println("Warning: no file called network-cfg.json (ignore if this is the first run)")
gunjan56e19d272016-07-07 14:23:26 -0700128 }
gunjan5c79837e2016-07-09 03:31:27 -0700129 err = generateDevicesJSON(onos)
130 if err != nil {
131 w.WriteHeader(http.StatusExpectationFailed)
132 fmt.Fprintf(w, err.Error())
133 return
134 }
gunjan56e19d272016-07-07 14:23:26 -0700135 generateLinkJSON(onos)
gunjan5c79837e2016-07-09 03:31:27 -0700136 err = generateHostJSON(onos)
137 if err != nil {
138 w.WriteHeader(http.StatusExpectationFailed)
139 fmt.Fprintf(w, err.Error())
140 return
141 }
gunjan56e19d272016-07-07 14:23:26 -0700142
143 fmt.Println("Config file generated: network-cfg.json")
144
gunjan5c79837e2016-07-09 03:31:27 -0700145 data, err := ioutil.ReadFile("network-cfg.json")
146 check(err)
147
148 w.WriteHeader(http.StatusAccepted)
149 fmt.Fprintf(w, string(data))
150
gunjan56e19d272016-07-07 14:23:26 -0700151}
152
153func writeToFile(object interface{}, t string) {
154 f, err := os.OpenFile("network-cfg.json", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
155 if err != nil {
156 panic(err)
157 }
158
159 defer f.Close()
160
161 tpl, err := template.ParseFiles(t)
162 check(err)
163 err = tpl.Execute(f, object)
164 check(err)
165}
166
gunjan5c79837e2016-07-09 03:31:27 -0700167func generateDevicesJSON(onos string) error {
gunjan56e19d272016-07-07 14:23:26 -0700168 ds := getData(onos + "/onos/v1/devices")
169
170 var d devices
171 err := json.Unmarshal(ds, &d)
172 check(err)
173
gunjan5c79837e2016-07-09 03:31:27 -0700174 if len(d.Device) != c.SwitchCount {
175 _ = os.Remove("network-cfg.json")
176 log.Println("[INFO] Cleaning up unfinished config file")
177 e := fmt.Sprintf("[ERROR] Number of switches configured don't match actual switches connected to the controller. Configured: %d, connected: %d", c.SwitchCount, len(d.Device))
178 log.Println(e)
179 return errors.New(e)
180 }
181
gunjan56e19d272016-07-07 14:23:26 -0700182 for k, _ := range d.Device {
183 d.Device[k].Comma = ","
184 if k >= len(d.Device)-1 {
185 d.Device[k].Comma = ""
186 }
187 }
188
189 writeToFile(d.Device, "devices.tpl")
gunjan5c79837e2016-07-09 03:31:27 -0700190 return nil
gunjan56e19d272016-07-07 14:23:26 -0700191
192}
193
gunjan5c79837e2016-07-09 03:31:27 -0700194func generateHostJSON(onos string) error {
gunjan56e19d272016-07-07 14:23:26 -0700195 hs := getData(onos + "/onos/v1/hosts")
196 var h hosts
197 err := json.Unmarshal(hs, &h)
198 check(err)
199
gunjan5c79837e2016-07-09 03:31:27 -0700200 if len(h.Host) != c.HostCount {
201 _ = os.Remove("network-cfg.json")
202 log.Println("[INFO] Cleaning up unfinished config file")
203 e := fmt.Sprintf("[ERROR] Number of hosts configured don't match actual hosts visible to the controller. Configured: %d, connected: %d", c.HostCount, len(h.Host))
204 log.Println(e)
205 return errors.New(e)
206 }
207
gunjan56e19d272016-07-07 14:23:26 -0700208 for k, _ := range h.Host {
209
210 h.Host[k].Comma = ","
211 if k >= len(h.Host)-1 {
212 h.Host[k].Comma = ""
213 }
214
215 parts := strings.Split(h.Host[k].IpAddresses[0], ".")
216 ip := ""
217 for _, v := range parts[:len(parts)-1] {
218 ip = ip + v + "."
219 }
220 h.Host[k].Gateway = ip
221 }
222
223 writeToFile(h.Host, "ports.tpl")
224
225 writeToFile(h.Host, "hosts.tpl")
gunjan5c79837e2016-07-09 03:31:27 -0700226 return nil
gunjan56e19d272016-07-07 14:23:26 -0700227
228}
229
230func generateLinkJSON(onos string) {
231
232 links := getData(onos + "/onos/v1/links")
233
234 var l onosLinks
235 err := json.Unmarshal(links, &l)
236 check(err)
237
238 var in []linkStructJSON
239
240 for k, v := range l.Links {
241
242 comma := ","
243 val := fmt.Sprint(v.Src.Device + "/" + v.Src.Port + "-" + v.Dst.Device + "/" + v.Dst.Port)
244 if k >= len(l.Links)-1 {
245 comma = ""
246 }
247
248 tmp := linkStructJSON{val, comma}
249 in = append(in, tmp)
250
251 }
252
253 writeToFile(in, "links.tpl")
254
255}
256
257func getData(url string) []byte {
258
259 resp, err := http.Get(url)
260 check(err)
261
262 defer resp.Body.Close()
263
264 body, err := ioutil.ReadAll(resp.Body)
265 check(err)
266
267 return body
268
269}
270
271func check(e error) {
272 if e != nil {
273 panic(e)
274 }
275}