blob: 9c0bd8ca922767850189471d917798e54864f570 [file] [log] [blame]
cuilin20187b2a8c32019-03-26 19:52:28 -07001/*
2 * Copyright 2018-present Open Networking Foundation
3
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7
8 * http://www.apache.org/licenses/LICENSE-2.0
9
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package main
17
18import (
19 "context"
20 "errors"
21 "fmt"
22 "github.com/opencord/voltha-go/adapters"
23 com "github.com/opencord/voltha-go/adapters/common"
cuilin20187b2a8c32019-03-26 19:52:28 -070024 "github.com/opencord/voltha-go/common/log"
25 "github.com/opencord/voltha-go/db/kvstore"
26 "github.com/opencord/voltha-go/kafka"
Girish Gowdru0c588b22019-04-23 23:24:56 -040027 ac "github.com/opencord/voltha-openolt-adapter/adaptercore"
28 "github.com/opencord/voltha-openolt-adapter/config"
manikkaraj kbf256be2019-03-25 00:13:48 +053029 ic "github.com/opencord/voltha-protos/go/inter_container"
30 "github.com/opencord/voltha-protos/go/voltha"
cuilin20187b2a8c32019-03-26 19:52:28 -070031 "os"
32 "os/signal"
33 "strconv"
34 "syscall"
35 "time"
36)
37
38type adapter struct {
39 instanceId string
40 config *config.AdapterFlags
41 iAdapter adapters.IAdapter
42 kafkaClient kafka.Client
43 kvClient kvstore.Client
44 kip *kafka.InterContainerProxy
45 coreProxy *com.CoreProxy
46 adapterProxy *com.AdapterProxy
47 halted bool
48 exitChannel chan int
49 receiverChannels []<-chan *ic.InterContainerMessage
50}
51
52func init() {
53 log.AddPackage(log.JSON, log.DebugLevel, nil)
54}
55
56func newAdapter(cf *config.AdapterFlags) *adapter {
57 var a adapter
58 a.instanceId = cf.InstanceID
59 a.config = cf
60 a.halted = false
61 a.exitChannel = make(chan int, 1)
62 a.receiverChannels = make([]<-chan *ic.InterContainerMessage, 0)
63 return &a
64}
65
66func (a *adapter) start(ctx context.Context) {
67 log.Info("Starting Core Adapter components")
68 var err error
69
70 // Setup KV Client
71 log.Debugw("create-kv-client", log.Fields{"kvstore": a.config.KVStoreType})
72 if err := a.setKVClient(); err != nil {
73 log.Fatal("error-setting-kv-client")
74 }
75
76 // Setup Kafka Client
77 if a.kafkaClient, err = newKafkaClient("sarama", a.config.KafkaAdapterHost, a.config.KafkaAdapterPort); err != nil {
78 log.Fatal("Unsupported-common-client")
79 }
80
81 // Start the common InterContainer Proxy - retries indefinitely
82 if a.kip, err = a.startInterContainerProxy(-1); err != nil {
83 log.Fatal("error-starting-inter-container-proxy")
84 }
85
86 // Create the core proxy to handle requests to the Core
87 a.coreProxy = com.NewCoreProxy(a.kip, a.config.Topic, a.config.CoreTopic)
88
89 // Create the adaptor proxy to handle request between olt and onu
90 a.adapterProxy = com.NewAdapterProxy(a.kip, "brcm_openomci_onu", a.config.CoreTopic)
91
92 // Create the open OLT adapter
manikkaraj kbf256be2019-03-25 00:13:48 +053093 if a.iAdapter, err = a.startOpenOLT(ctx, a.kip, a.coreProxy, a.adapterProxy, a.config.OnuNumber, a.config.KVStoreHost, a.config.KVStorePort, a.config.KVStoreType); err != nil {
cuilin20187b2a8c32019-03-26 19:52:28 -070094 log.Fatal("error-starting-inter-container-proxy")
95 }
96
97 // Register the core request handler
98 if err = a.setupRequestHandler(a.instanceId, a.iAdapter); err != nil {
99 log.Fatal("error-setting-core-request-handler")
100 }
101
102 // Register this adapter to the Core - retries indefinitely
103 if err = a.registerWithCore(-1); err != nil {
104 log.Fatal("error-registering-with-core")
105 }
106}
107
108func (rw *adapter) stop() {
109 // Stop leadership tracking
110 rw.halted = true
111
112 // send exit signal
113 rw.exitChannel <- 0
114
115 // Cleanup - applies only if we had a kvClient
116 if rw.kvClient != nil {
117 // Release all reservations
118 if err := rw.kvClient.ReleaseAllReservations(); err != nil {
119 log.Infow("fail-to-release-all-reservations", log.Fields{"error": err})
120 }
121 // Close the DB connection
122 rw.kvClient.Close()
123 }
124
125 // TODO: More cleanup
126}
127
128func newKVClient(storeType string, address string, timeout int) (kvstore.Client, error) {
129
130 log.Infow("kv-store-type", log.Fields{"store": storeType})
131 switch storeType {
132 case "consul":
133 return kvstore.NewConsulClient(address, timeout)
134 case "etcd":
135 return kvstore.NewEtcdClient(address, timeout)
136 }
137 return nil, errors.New("unsupported-kv-store")
138}
139
140func newKafkaClient(clientType string, host string, port int) (kafka.Client, error) {
141
142 log.Infow("common-client-type", log.Fields{"client": clientType})
143 switch clientType {
144 case "sarama":
145 return kafka.NewSaramaClient(
146 kafka.Host(host),
147 kafka.Port(port),
148 kafka.ProducerReturnOnErrors(true),
149 kafka.ProducerReturnOnSuccess(true),
150 kafka.ProducerMaxRetries(6),
151 kafka.ProducerRetryBackoff(time.Millisecond*30)), nil
152 }
153 return nil, errors.New("unsupported-client-type")
154}
155
156func (a *adapter) setKVClient() error {
157 addr := a.config.KVStoreHost + ":" + strconv.Itoa(a.config.KVStorePort)
158 client, err := newKVClient(a.config.KVStoreType, addr, a.config.KVStoreTimeout)
159 if err != nil {
160 a.kvClient = nil
161 log.Error(err)
162 return err
163 }
164 a.kvClient = client
165 return nil
166}
167
168func toString(value interface{}) (string, error) {
169 switch t := value.(type) {
170 case []byte:
171 return string(value.([]byte)), nil
172 case string:
173 return value.(string), nil
174 default:
175 return "", fmt.Errorf("unexpected-type-%T", t)
176 }
177}
178
179func (a *adapter) startInterContainerProxy(retries int) (*kafka.InterContainerProxy, error) {
180 log.Infow("starting-intercontainer-messaging-proxy", log.Fields{"host": a.config.KafkaAdapterHost,
181 "port": a.config.KafkaAdapterPort, "topic": a.config.Topic})
182 var err error
183 var kip *kafka.InterContainerProxy
184 if kip, err = kafka.NewInterContainerProxy(
185 kafka.InterContainerHost(a.config.KafkaAdapterHost),
186 kafka.InterContainerPort(a.config.KafkaAdapterPort),
187 kafka.MsgClient(a.kafkaClient),
188 kafka.DefaultTopic(&kafka.Topic{Name: a.config.Topic})); err != nil {
189 log.Errorw("fail-to-create-common-proxy", log.Fields{"error": err})
190 return nil, err
191 }
192 count := 0
193 for {
194 if err = kip.Start(); err != nil {
195 log.Warnw("error-starting-messaging-proxy", log.Fields{"error": err})
196 if retries == count {
197 return nil, err
198 }
199 count = +1
200 // Take a nap before retrying
201 time.Sleep(2 * time.Second)
202 } else {
203 break
204 }
205 }
206
207 log.Info("common-messaging-proxy-created")
208 return kip, nil
209}
210
manikkaraj kbf256be2019-03-25 00:13:48 +0530211func (a *adapter) startOpenOLT(ctx context.Context, kip *kafka.InterContainerProxy, cp *com.CoreProxy, ap *com.AdapterProxy, onuNumber int, kvStoreHost string, kvStorePort int, KVStoreType string) (*ac.OpenOLT, error) {
cuilin20187b2a8c32019-03-26 19:52:28 -0700212 log.Info("starting-open-olt")
213 var err error
manikkaraj kbf256be2019-03-25 00:13:48 +0530214 sOLT := ac.NewOpenOLT(ctx, a.kip, cp, ap, onuNumber, kvStoreHost, kvStorePort, KVStoreType)
cuilin20187b2a8c32019-03-26 19:52:28 -0700215
216 if err = sOLT.Start(ctx); err != nil {
217 log.Fatalw("error-starting-messaging-proxy", log.Fields{"error": err})
218 return nil, err
219 }
220
221 log.Info("open-olt-started")
222 return sOLT, nil
223}
224
225func (a *adapter) setupRequestHandler(coreInstanceId string, iadapter adapters.IAdapter) error {
226 log.Info("setting-request-handler")
227 requestProxy := com.NewRequestHandlerProxy(coreInstanceId, iadapter, a.coreProxy)
228 if err := a.kip.SubscribeWithRequestHandlerInterface(kafka.Topic{Name: a.config.Topic}, requestProxy); err != nil {
229 log.Errorw("request-handler-setup-failed", log.Fields{"error": err})
230 return err
231
232 }
233 log.Info("request-handler-setup-done")
234 return nil
235}
236
237func (a *adapter) registerWithCore(retries int) error {
238 log.Info("registering-with-core")
Girish Gowdru0c588b22019-04-23 23:24:56 -0400239 adapterDescription := &voltha.Adapter{Id: "openolt", // Unique name for the device type
240 Vendor: "simulation Enterprise Inc"}
241 types := []*voltha.DeviceType{{Id: "openolt",
242 Adapter: "openolt", //Name of the adapter that handles device type
243 AcceptsBulkFlowUpdate: false, // Currently openolt adapter does not support bulk flow handling
244 AcceptsAddRemoveFlowUpdates: true}}
cuilin20187b2a8c32019-03-26 19:52:28 -0700245 deviceTypes := &voltha.DeviceTypes{Items: types}
246 count := 0
247 for {
248 if err := a.coreProxy.RegisterAdapter(nil, adapterDescription, deviceTypes); err != nil {
249 log.Warnw("registering-with-core-failed", log.Fields{"error": err})
250 if retries == count {
251 return err
252 }
253 count += 1
254 // Take a nap before retrying
255 time.Sleep(2 * time.Second)
256 } else {
257 break
258 }
259 }
260 log.Info("registered-with-core")
261 return nil
262}
263
264func waitForExit() int {
265 signalChannel := make(chan os.Signal, 1)
266 signal.Notify(signalChannel,
267 syscall.SIGHUP,
268 syscall.SIGINT,
269 syscall.SIGTERM,
270 syscall.SIGQUIT)
271
272 exitChannel := make(chan int)
273
274 go func() {
275 s := <-signalChannel
276 switch s {
277 case syscall.SIGHUP,
278 syscall.SIGINT,
279 syscall.SIGTERM,
280 syscall.SIGQUIT:
281 log.Infow("closing-signal-received", log.Fields{"signal": s})
282 exitChannel <- 0
283 default:
284 log.Infow("unexpected-signal-received", log.Fields{"signal": s})
285 exitChannel <- 1
286 }
287 }()
288
289 code := <-exitChannel
290 return code
291}
292
293func printBanner() {
294 fmt.Println(" ____ ____ _ _______ ")
295 fmt.Println(" / _ \\ / __\\| | |__ __|")
296 fmt.Println(" | | | |_ __ ___ _ __ | | | | | | | ")
297 fmt.Println(" | | | | '_\\ / _\\ '_\\ | | | | | | | ")
298 fmt.Println(" | |__| | |_) | __/ | | || |__| | |____| | ")
299 fmt.Println(" \\____/| .__/\\___|_| |_|\\____/|______|_| ")
300 fmt.Println(" | | ")
301 fmt.Println(" |_| ")
302 fmt.Println(" ")
303}
304
305func main() {
306 start := time.Now()
307
308 cf := config.NewAdapterFlags()
309 cf.ParseCommandArguments()
310
311 //// Setup logging
312
313 //Setup default logger - applies for packages that do not have specific logger set
314 if _, err := log.SetDefaultLogger(log.JSON, cf.LogLevel, log.Fields{"instanceId": cf.InstanceID}); err != nil {
315 log.With(log.Fields{"error": err}).Fatal("Cannot setup logging")
316 }
317
318 // Update all loggers (provisionned via init) with a common field
319 if err := log.UpdateAllLoggers(log.Fields{"instanceId": cf.InstanceID}); err != nil {
320 log.With(log.Fields{"error": err}).Fatal("Cannot setup logging")
321 }
322
323 log.SetPackageLogLevel("github.com/opencord/voltha-go/adapters/common", log.DebugLevel)
324
325 defer log.CleanUp()
326
327 // Print banner if specified
328 if cf.Banner {
329 printBanner()
330 }
331
332 log.Infow("config", log.Fields{"config": *cf})
333
334 ctx, cancel := context.WithCancel(context.Background())
335 defer cancel()
336
337 ad := newAdapter(cf)
338 go ad.start(ctx)
339
340 code := waitForExit()
341 log.Infow("received-a-closing-signal", log.Fields{"code": code})
342
343 // Cleanup before leaving
344 ad.stop()
345
346 elapsed := time.Since(start)
347 log.Infow("run-time", log.Fields{"instanceId": ad.config.InstanceID, "time": elapsed / time.Second})
348}