blob: 2ffa534942a020adbd16ad117d34a0fb3a84d36e [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 */
Girish Gowdru6a80bbd2019-07-02 07:36:09 -070016
17//Package main invokes the application
cuilin20187b2a8c32019-03-26 19:52:28 -070018package main
19
20import (
21 "context"
22 "errors"
23 "fmt"
kdarapu381c6902019-07-31 18:23:16 +053024 "os"
25 "os/signal"
26 "strconv"
27 "syscall"
28 "time"
29
Scott Baker51290152019-10-24 14:23:20 -070030 "github.com/opencord/voltha-lib-go/v2/pkg/adapters/adapterif"
kdarapu381c6902019-07-31 18:23:16 +053031
Scott Baker51290152019-10-24 14:23:20 -070032 "github.com/opencord/voltha-lib-go/v2/pkg/adapters"
33 com "github.com/opencord/voltha-lib-go/v2/pkg/adapters/common"
34 "github.com/opencord/voltha-lib-go/v2/pkg/db/kvstore"
35 "github.com/opencord/voltha-lib-go/v2/pkg/kafka"
36 "github.com/opencord/voltha-lib-go/v2/pkg/log"
37 "github.com/opencord/voltha-lib-go/v2/pkg/probe"
Girish Gowdru0c588b22019-04-23 23:24:56 -040038 ac "github.com/opencord/voltha-openolt-adapter/adaptercore"
39 "github.com/opencord/voltha-openolt-adapter/config"
Matt Jeanneret0c9ae282019-07-18 18:14:28 -040040 "github.com/opencord/voltha-openolt-adapter/config/version"
Scott Bakerc6e54cb2019-11-04 09:31:25 -080041 ic "github.com/opencord/voltha-protos/v2/go/inter_container"
42 "github.com/opencord/voltha-protos/v2/go/voltha"
cuilin20187b2a8c32019-03-26 19:52:28 -070043)
44
45type adapter struct {
Girish Gowdru6a80bbd2019-07-02 07:36:09 -070046 instanceID string
cuilin20187b2a8c32019-03-26 19:52:28 -070047 config *config.AdapterFlags
48 iAdapter adapters.IAdapter
49 kafkaClient kafka.Client
50 kvClient kvstore.Client
51 kip *kafka.InterContainerProxy
kdarapu381c6902019-07-31 18:23:16 +053052 coreProxy adapterif.CoreProxy
53 adapterProxy adapterif.AdapterProxy
54 eventProxy adapterif.EventProxy
cuilin20187b2a8c32019-03-26 19:52:28 -070055 halted bool
56 exitChannel chan int
57 receiverChannels []<-chan *ic.InterContainerMessage
58}
59
60func init() {
Girish Gowdru6a80bbd2019-07-02 07:36:09 -070061 _, _ = log.AddPackage(log.JSON, log.DebugLevel, nil)
cuilin20187b2a8c32019-03-26 19:52:28 -070062}
63
cbabu95f21522019-11-13 14:25:18 +010064const (
65 // DefaultLivelinessCheck to check the liveliness
66 DefaultLivelinessCheck = 30 * time.Second
67 // DefaultTimeout to close the connection
68 DefaultTimeout = 10
69)
70
cuilin20187b2a8c32019-03-26 19:52:28 -070071func newAdapter(cf *config.AdapterFlags) *adapter {
72 var a adapter
Girish Gowdru6a80bbd2019-07-02 07:36:09 -070073 a.instanceID = cf.InstanceID
cuilin20187b2a8c32019-03-26 19:52:28 -070074 a.config = cf
75 a.halted = false
76 a.exitChannel = make(chan int, 1)
77 a.receiverChannels = make([]<-chan *ic.InterContainerMessage, 0)
78 return &a
79}
80
81func (a *adapter) start(ctx context.Context) {
82 log.Info("Starting Core Adapter components")
83 var err error
84
Rohan Agrawal828bf4e2019-10-22 10:13:19 +000085 var p *probe.Probe
86 if value := ctx.Value(probe.ProbeContextKey); value != nil {
87 if _, ok := value.(*probe.Probe); ok {
88 p = value.(*probe.Probe)
89 p.RegisterService(
90 "message-bus",
91 "kv-store",
92 "container-proxy",
93 "core-request-handler",
94 "register-with-core",
95 )
96 }
97 }
98
cuilin20187b2a8c32019-03-26 19:52:28 -070099 // Setup KV Client
100 log.Debugw("create-kv-client", log.Fields{"kvstore": a.config.KVStoreType})
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700101 if err = a.setKVClient(); err != nil {
cuilin20187b2a8c32019-03-26 19:52:28 -0700102 log.Fatal("error-setting-kv-client")
103 }
104
Rohan Agrawal828bf4e2019-10-22 10:13:19 +0000105 if p != nil {
106 p.UpdateStatus("kv-store", probe.ServiceStatusRunning)
107 }
108
cuilin20187b2a8c32019-03-26 19:52:28 -0700109 // Setup Kafka Client
110 if a.kafkaClient, err = newKafkaClient("sarama", a.config.KafkaAdapterHost, a.config.KafkaAdapterPort); err != nil {
111 log.Fatal("Unsupported-common-client")
112 }
113
Rohan Agrawal828bf4e2019-10-22 10:13:19 +0000114 if p != nil {
115 p.UpdateStatus("message-bus", probe.ServiceStatusRunning)
116 }
117
cuilin20187b2a8c32019-03-26 19:52:28 -0700118 // Start the common InterContainer Proxy - retries indefinitely
Rohan Agrawal828bf4e2019-10-22 10:13:19 +0000119 if a.kip, err = a.startInterContainerProxy(ctx, -1); err != nil {
cuilin20187b2a8c32019-03-26 19:52:28 -0700120 log.Fatal("error-starting-inter-container-proxy")
121 }
122
123 // Create the core proxy to handle requests to the Core
124 a.coreProxy = com.NewCoreProxy(a.kip, a.config.Topic, a.config.CoreTopic)
125
126 // Create the adaptor proxy to handle request between olt and onu
127 a.adapterProxy = com.NewAdapterProxy(a.kip, "brcm_openomci_onu", a.config.CoreTopic)
128
Devmalya Paulfb990a52019-07-09 10:01:49 -0400129 // Create the event proxy to post events to KAFKA
130 a.eventProxy = com.NewEventProxy(com.MsgClient(a.kafkaClient), com.MsgTopic(kafka.Topic{Name: a.config.EventTopic}))
131
cuilin20187b2a8c32019-03-26 19:52:28 -0700132 // Create the open OLT adapter
kdarapu381c6902019-07-31 18:23:16 +0530133 if a.iAdapter, err = a.startOpenOLT(ctx, a.kip, a.coreProxy, a.adapterProxy, a.eventProxy,
134 a.config.OnuNumber,
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700135 a.config.KVStoreHost, a.config.KVStorePort, a.config.KVStoreType); err != nil {
cuilin20187b2a8c32019-03-26 19:52:28 -0700136 log.Fatal("error-starting-inter-container-proxy")
137 }
138
139 // Register the core request handler
Rohan Agrawal828bf4e2019-10-22 10:13:19 +0000140 if err = a.setupRequestHandler(ctx, a.instanceID, a.iAdapter); err != nil {
cuilin20187b2a8c32019-03-26 19:52:28 -0700141 log.Fatal("error-setting-core-request-handler")
142 }
143
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700144 // Register this adapter to the Core - retries indefinitely
Rohan Agrawal828bf4e2019-10-22 10:13:19 +0000145 if err = a.registerWithCore(ctx, -1); err != nil {
cuilin20187b2a8c32019-03-26 19:52:28 -0700146 log.Fatal("error-registering-with-core")
147 }
cbabu95f21522019-11-13 14:25:18 +0100148
149 // go routine to check the readiness and liveliness and update the probe status
150 go a.checKServicesReadiness(ctx)
151}
152
153/**
154This function checks the liveliness and readiness of the kakfa and kv-client services
155and update the status in the probe.
156*/
157func (a *adapter) checKServicesReadiness(ctx context.Context) {
158 for {
159 // checks the connectivity of the kv-client
160 if a.kvClient.IsConnectionUp(DefaultTimeout) {
161 probe.UpdateStatusFromContext(ctx, "kv-store", probe.ServiceStatusRunning)
162 log.Debugf("kv- store is reachable")
163 } else {
164 probe.UpdateStatusFromContext(ctx, "kv-store", probe.ServiceStatusNotReady)
165 log.Debugf("kv-store is not reachable")
166 }
167 //checks the connectivity of the kafka
168 a.kafkaClient.SendLiveness()
169 isKafkaReady := <-a.kafkaClient.EnableLivenessChannel(true)
170 if isKafkaReady {
171 probe.UpdateStatusFromContext(ctx, "message-bus", probe.ServiceStatusRunning)
172 log.Debugf("kafka is reachable")
173 } else {
174 probe.UpdateStatusFromContext(ctx, "message-bus", probe.ServiceStatusNotReady)
175 log.Debugf("kafka is not reachable")
176 }
177 time.Sleep(DefaultLivelinessCheck)
178 }
cuilin20187b2a8c32019-03-26 19:52:28 -0700179}
180
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700181func (a *adapter) stop() {
cuilin20187b2a8c32019-03-26 19:52:28 -0700182 // Stop leadership tracking
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700183 a.halted = true
cuilin20187b2a8c32019-03-26 19:52:28 -0700184
185 // send exit signal
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700186 a.exitChannel <- 0
cuilin20187b2a8c32019-03-26 19:52:28 -0700187
188 // Cleanup - applies only if we had a kvClient
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700189 if a.kvClient != nil {
cuilin20187b2a8c32019-03-26 19:52:28 -0700190 // Release all reservations
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700191 if err := a.kvClient.ReleaseAllReservations(); err != nil {
cuilin20187b2a8c32019-03-26 19:52:28 -0700192 log.Infow("fail-to-release-all-reservations", log.Fields{"error": err})
193 }
194 // Close the DB connection
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700195 a.kvClient.Close()
cuilin20187b2a8c32019-03-26 19:52:28 -0700196 }
197
198 // TODO: More cleanup
199}
200
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700201func newKVClient(storeType, address string, timeout int) (kvstore.Client, error) {
cuilin20187b2a8c32019-03-26 19:52:28 -0700202
203 log.Infow("kv-store-type", log.Fields{"store": storeType})
204 switch storeType {
205 case "consul":
206 return kvstore.NewConsulClient(address, timeout)
207 case "etcd":
208 return kvstore.NewEtcdClient(address, timeout)
209 }
210 return nil, errors.New("unsupported-kv-store")
211}
212
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700213func newKafkaClient(clientType, host string, port int) (kafka.Client, error) {
cuilin20187b2a8c32019-03-26 19:52:28 -0700214
215 log.Infow("common-client-type", log.Fields{"client": clientType})
216 switch clientType {
217 case "sarama":
218 return kafka.NewSaramaClient(
219 kafka.Host(host),
220 kafka.Port(port),
221 kafka.ProducerReturnOnErrors(true),
222 kafka.ProducerReturnOnSuccess(true),
223 kafka.ProducerMaxRetries(6),
Abhilash S.L3b494632019-07-16 15:51:09 +0530224 kafka.ProducerRetryBackoff(time.Millisecond*30),
225 kafka.MetadatMaxRetries(15)), nil
cuilin20187b2a8c32019-03-26 19:52:28 -0700226 }
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700227
cuilin20187b2a8c32019-03-26 19:52:28 -0700228 return nil, errors.New("unsupported-client-type")
229}
230
231func (a *adapter) setKVClient() error {
232 addr := a.config.KVStoreHost + ":" + strconv.Itoa(a.config.KVStorePort)
233 client, err := newKVClient(a.config.KVStoreType, addr, a.config.KVStoreTimeout)
234 if err != nil {
235 a.kvClient = nil
236 log.Error(err)
237 return err
238 }
239 a.kvClient = client
240 return nil
241}
242
Rohan Agrawal828bf4e2019-10-22 10:13:19 +0000243func (a *adapter) startInterContainerProxy(ctx context.Context, retries int) (*kafka.InterContainerProxy, error) {
cuilin20187b2a8c32019-03-26 19:52:28 -0700244 log.Infow("starting-intercontainer-messaging-proxy", log.Fields{"host": a.config.KafkaAdapterHost,
245 "port": a.config.KafkaAdapterPort, "topic": a.config.Topic})
246 var err error
247 var kip *kafka.InterContainerProxy
248 if kip, err = kafka.NewInterContainerProxy(
249 kafka.InterContainerHost(a.config.KafkaAdapterHost),
250 kafka.InterContainerPort(a.config.KafkaAdapterPort),
251 kafka.MsgClient(a.kafkaClient),
252 kafka.DefaultTopic(&kafka.Topic{Name: a.config.Topic})); err != nil {
253 log.Errorw("fail-to-create-common-proxy", log.Fields{"error": err})
254 return nil, err
255 }
256 count := 0
257 for {
258 if err = kip.Start(); err != nil {
259 log.Warnw("error-starting-messaging-proxy", log.Fields{"error": err})
260 if retries == count {
261 return nil, err
262 }
263 count = +1
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700264 // Take a nap before retrying
cuilin20187b2a8c32019-03-26 19:52:28 -0700265 time.Sleep(2 * time.Second)
266 } else {
267 break
268 }
269 }
Rohan Agrawal828bf4e2019-10-22 10:13:19 +0000270 probe.UpdateStatusFromContext(ctx, "container-proxy", probe.ServiceStatusRunning)
cuilin20187b2a8c32019-03-26 19:52:28 -0700271 log.Info("common-messaging-proxy-created")
272 return kip, nil
273}
274
kdarapu381c6902019-07-31 18:23:16 +0530275func (a *adapter) startOpenOLT(ctx context.Context, kip *kafka.InterContainerProxy,
276 cp adapterif.CoreProxy, ap adapterif.AdapterProxy, ep adapterif.EventProxy, onuNumber int, kvStoreHost string,
277 kvStorePort int, KVStoreType string) (*ac.OpenOLT, error) {
cuilin20187b2a8c32019-03-26 19:52:28 -0700278 log.Info("starting-open-olt")
279 var err error
Devmalya Paulfb990a52019-07-09 10:01:49 -0400280 sOLT := ac.NewOpenOLT(ctx, a.kip, cp, ap, ep, onuNumber, kvStoreHost, kvStorePort, KVStoreType)
cuilin20187b2a8c32019-03-26 19:52:28 -0700281
282 if err = sOLT.Start(ctx); err != nil {
283 log.Fatalw("error-starting-messaging-proxy", log.Fields{"error": err})
284 return nil, err
285 }
286
287 log.Info("open-olt-started")
288 return sOLT, nil
289}
290
Rohan Agrawal828bf4e2019-10-22 10:13:19 +0000291func (a *adapter) setupRequestHandler(ctx context.Context, coreInstanceID string, iadapter adapters.IAdapter) error {
cuilin20187b2a8c32019-03-26 19:52:28 -0700292 log.Info("setting-request-handler")
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700293 requestProxy := com.NewRequestHandlerProxy(coreInstanceID, iadapter, a.coreProxy)
cuilin20187b2a8c32019-03-26 19:52:28 -0700294 if err := a.kip.SubscribeWithRequestHandlerInterface(kafka.Topic{Name: a.config.Topic}, requestProxy); err != nil {
295 log.Errorw("request-handler-setup-failed", log.Fields{"error": err})
296 return err
297
298 }
Rohan Agrawal828bf4e2019-10-22 10:13:19 +0000299 probe.UpdateStatusFromContext(ctx, "core-request-handler", probe.ServiceStatusRunning)
cuilin20187b2a8c32019-03-26 19:52:28 -0700300 log.Info("request-handler-setup-done")
301 return nil
302}
303
Rohan Agrawal828bf4e2019-10-22 10:13:19 +0000304func (a *adapter) registerWithCore(ctx context.Context, retries int) error {
cuilin20187b2a8c32019-03-26 19:52:28 -0700305 log.Info("registering-with-core")
Girish Gowdru0c588b22019-04-23 23:24:56 -0400306 adapterDescription := &voltha.Adapter{Id: "openolt", // Unique name for the device type
Matt Jeanneretf880eb62019-07-16 20:08:03 -0400307 Vendor: "VOLTHA OpenOLT",
308 Version: version.VersionInfo.Version}
Girish Gowdru0c588b22019-04-23 23:24:56 -0400309 types := []*voltha.DeviceType{{Id: "openolt",
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700310 Adapter: "openolt", // Name of the adapter that handles device type
Girish Gowdru0c588b22019-04-23 23:24:56 -0400311 AcceptsBulkFlowUpdate: false, // Currently openolt adapter does not support bulk flow handling
312 AcceptsAddRemoveFlowUpdates: true}}
cuilin20187b2a8c32019-03-26 19:52:28 -0700313 deviceTypes := &voltha.DeviceTypes{Items: types}
314 count := 0
315 for {
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700316 if err := a.coreProxy.RegisterAdapter(context.TODO(), adapterDescription, deviceTypes); err != nil {
cuilin20187b2a8c32019-03-26 19:52:28 -0700317 log.Warnw("registering-with-core-failed", log.Fields{"error": err})
318 if retries == count {
319 return err
320 }
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700321 count++
322 // Take a nap before retrying
cuilin20187b2a8c32019-03-26 19:52:28 -0700323 time.Sleep(2 * time.Second)
324 } else {
325 break
326 }
327 }
Rohan Agrawal828bf4e2019-10-22 10:13:19 +0000328 probe.UpdateStatusFromContext(ctx, "register-with-core", probe.ServiceStatusRunning)
cuilin20187b2a8c32019-03-26 19:52:28 -0700329 log.Info("registered-with-core")
330 return nil
331}
332
333func waitForExit() int {
334 signalChannel := make(chan os.Signal, 1)
335 signal.Notify(signalChannel,
336 syscall.SIGHUP,
337 syscall.SIGINT,
338 syscall.SIGTERM,
339 syscall.SIGQUIT)
340
341 exitChannel := make(chan int)
342
343 go func() {
344 s := <-signalChannel
345 switch s {
346 case syscall.SIGHUP,
347 syscall.SIGINT,
348 syscall.SIGTERM,
349 syscall.SIGQUIT:
350 log.Infow("closing-signal-received", log.Fields{"signal": s})
351 exitChannel <- 0
352 default:
353 log.Infow("unexpected-signal-received", log.Fields{"signal": s})
354 exitChannel <- 1
355 }
356 }()
357
358 code := <-exitChannel
359 return code
360}
361
362func printBanner() {
363 fmt.Println(" ____ ____ _ _______ ")
364 fmt.Println(" / _ \\ / __\\| | |__ __|")
365 fmt.Println(" | | | |_ __ ___ _ __ | | | | | | | ")
366 fmt.Println(" | | | | '_\\ / _\\ '_\\ | | | | | | | ")
367 fmt.Println(" | |__| | |_) | __/ | | || |__| | |____| | ")
368 fmt.Println(" \\____/| .__/\\___|_| |_|\\____/|______|_| ")
369 fmt.Println(" | | ")
370 fmt.Println(" |_| ")
371 fmt.Println(" ")
372}
373
Matt Jeanneretf880eb62019-07-16 20:08:03 -0400374func printVersion() {
375 fmt.Println("VOLTHA OpenOLT Adapter")
376 fmt.Println(version.VersionInfo.String(" "))
377}
378
cuilin20187b2a8c32019-03-26 19:52:28 -0700379func main() {
380 start := time.Now()
381
382 cf := config.NewAdapterFlags()
383 cf.ParseCommandArguments()
384
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700385 // Setup logging
cuilin20187b2a8c32019-03-26 19:52:28 -0700386
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700387 // Setup default logger - applies for packages that do not have specific logger set
Hardik Windlassb9c869b2019-10-10 08:34:32 +0000388 if _, err := log.SetDefaultLogger(log.JSON, cf.LogLevel, log.Fields{"instanceId": cf.InstanceID}); err != nil {
cuilin20187b2a8c32019-03-26 19:52:28 -0700389 log.With(log.Fields{"error": err}).Fatal("Cannot setup logging")
390 }
391
392 // Update all loggers (provisionned via init) with a common field
Hardik Windlassb9c869b2019-10-10 08:34:32 +0000393 if err := log.UpdateAllLoggers(log.Fields{"instanceId": cf.InstanceID}); err != nil {
cuilin20187b2a8c32019-03-26 19:52:28 -0700394 log.With(log.Fields{"error": err}).Fatal("Cannot setup logging")
395 }
396
Scott Baker51290152019-10-24 14:23:20 -0700397 log.SetPackageLogLevel("github.com/opencord/voltha-lib-go/v2/pkg/adapters/common", log.DebugLevel)
cuilin20187b2a8c32019-03-26 19:52:28 -0700398
399 defer log.CleanUp()
400
Matt Jeanneretf880eb62019-07-16 20:08:03 -0400401 // Print version / build information and exit
402 if cf.DisplayVersionOnly {
403 printVersion()
404 return
405 }
406
cuilin20187b2a8c32019-03-26 19:52:28 -0700407 // Print banner if specified
408 if cf.Banner {
409 printBanner()
410 }
411
412 log.Infow("config", log.Fields{"config": *cf})
413
414 ctx, cancel := context.WithCancel(context.Background())
415 defer cancel()
416
417 ad := newAdapter(cf)
Rohan Agrawal828bf4e2019-10-22 10:13:19 +0000418
419 p := &probe.Probe{}
420 go p.ListenAndServe(fmt.Sprintf("%s:%d", ad.config.ProbeHost, ad.config.ProbePort))
421
422 probeCtx := context.WithValue(ctx, probe.ProbeContextKey, p)
423
424 go ad.start(probeCtx)
cuilin20187b2a8c32019-03-26 19:52:28 -0700425
426 code := waitForExit()
427 log.Infow("received-a-closing-signal", log.Fields{"code": code})
428
429 // Cleanup before leaving
430 ad.stop()
431
432 elapsed := time.Since(start)
Hardik Windlassb9c869b2019-10-10 08:34:32 +0000433 log.Infow("run-time", log.Fields{"instanceId": ad.config.InstanceID, "time": elapsed / time.Second})
cuilin20187b2a8c32019-03-26 19:52:28 -0700434}