blob: a09f0776a5df13b7c219eee9f402aba65f01b6e6 [file] [log] [blame]
Naveen Sampath04696f72022-06-13 15:19:14 +05301/*
2* Copyright 2022-present Open Networking Foundation
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.
14 */
15
16package main
17
18import (
19 "context"
20 "errors"
21 "fmt"
22 "os"
23 "os/signal"
24 "syscall"
25 "time"
26
27 pc "voltha-go-controller/infra/pprofcontroller"
28
29 "voltha-go-controller/database"
30 db "voltha-go-controller/database"
31 app "voltha-go-controller/internal/pkg/application"
32 "voltha-go-controller/internal/pkg/controller"
33 "voltha-go-controller/internal/pkg/vpagent"
34 "voltha-go-controller/voltha-go-controller/nbi"
35
36 "github.com/opencord/voltha-lib-go/v7/pkg/db/kvstore"
37 "github.com/opencord/voltha-lib-go/v7/pkg/log"
38 "github.com/opencord/voltha-lib-go/v7/pkg/probe"
39)
40
41// VgcInfo structure
42type VgcInfo struct {
43 Name string
44 Version string
45 kvClient kvstore.Client
46}
47
48var vgcInfo = VgcInfo{Name: "VGC"}
49var dbHandler *database.Database
50
51func printBanner() {
52 fmt.Println("## ## ###### ###### ")
53 fmt.Println("## ## ## ## ## ## ")
54 fmt.Println("## ## ## ## ")
55 fmt.Println("## ## ## #### ## ")
56 fmt.Println(" ## ## ## ## ## ")
57 fmt.Println(" ## ## ## ## ## ## ")
58 fmt.Println(" ### ###### ###### ")
59}
60
61func stop(ctx context.Context, kvClient kvstore.Client, vpa *vpagent.VPAgent) {
62 // Cleanup - applies only if we had a kvClient
63 if kvClient != nil {
64 // Release all reservations
65 if err := kvClient.ReleaseAllReservations(ctx); err != nil {
66 logger.Infow(ctx, "fail-to-release-all-reservations", log.Fields{"error": err})
67 }
68 // Close the DB connection
69 kvClient.Close(ctx)
70 }
71 //Closet voltha connection
72 vpa.CloseConnectionToVoltha()
73
74}
75
76func newKVClient(storeType, address string, timeout int) (kvstore.Client, error) {
77 logger.Infow(ctx, "kv-store-type", log.Fields{"store": storeType})
78 switch storeType {
79 case "redis":
80 return kvstore.NewRedisClient(address, time.Duration(timeout), false)
81 }
82 return nil, errors.New("unsupported-kv-store")
83}
84
85// waitUntilKVStoreReachableOrMaxTries will wait until it can connect to a KV store or until maxtries has been reached
86func waitUntilKVStoreReachableOrMaxTries(ctx context.Context, config *VGCFlags) error {
87 count := 0
88 for {
89 if !vgcInfo.kvClient.IsConnectionUp(ctx) {
90 logger.Infow(ctx, "KV-store-unreachable", log.Fields{"KVStoreType": config.KVStoreType, "Address": config.KVStoreEndPoint})
91 if config.ConnectionMaxRetries != -1 {
92 if count >= config.ConnectionMaxRetries {
93 logger.Errorw(ctx, "kv store unreachable", log.Fields{})
94 return errors.New("kv store unreachable")
95 }
96 }
97 count++
98 // Take a nap before retrying
99 time.Sleep(time.Duration(config.ConnectionRetryDelay) * time.Second)
100 logger.Infow(ctx, "retry-KV-store-connectivity", log.Fields{"retryCount": count,
101 "maxRetries": config.ConnectionMaxRetries, "retryInterval": config.ConnectionRetryDelay})
102
103 } else {
104 break
105 }
106 }
107 return nil
108}
109
110func main() {
111 // Enviornment variables processing
112 config := newVGCFlags()
113 config.parseEnvironmentVariables()
114
115 if config.Banner {
116 printBanner()
117 }
118
119 pc.Init()
120
121 // Setup logging for the program
122 // Read the loglevel configured first
123 // Setup default logger - applies for packages that do not have specific logger set
124 var logLevel log.LogLevel
125 var err error
126 if logLevel, err = log.StringToLogLevel(config.LogLevel); err != nil {
127 logLevel = log.DebugLevel
128 }
129 if _, err = log.SetDefaultLogger(log.JSON, logLevel, log.Fields{"instanceId": config.InstanceID}); err != nil {
130 logger.With(log.Fields{"error": err}).Fatal(ctx, "Cannot setup logging")
131 }
132 // Update all loggers (provisionned via init) with a common field
133 if err := log.UpdateAllLoggers(log.Fields{"instanceId": config.InstanceID}); err != nil {
134 logger.With(log.Fields{"error": err}).Fatal(ctx, "Cannot setup logging")
135 }
136 log.SetAllLogLevel(logLevel)
137
138 if vgcInfo.kvClient, err = newKVClient(config.KVStoreType, config.KVStoreEndPoint, config.KVStoreTimeout); err != nil {
139 logger.Errorw(ctx, "KVClient Establishment Failure", log.Fields{"Reason": err})
140 }
141
142 if dbHandler, err = db.Initialize(config.KVStoreType, config.KVStoreEndPoint, config.KVStoreTimeout); err != nil {
143 logger.Errorw(ctx, "unable-to-connect-to-db", log.Fields{"error": err})
144 return
145 }
146
147 db.SetDatabase(dbHandler)
148 logger.Infow(ctx, "verifying-KV-store-connectivity", log.Fields{"host": config.KVStoreHost,
149 "port": config.KVStorePort, "retries": config.ConnectionMaxRetries,
150 "retryInterval": config.ConnectionRetryDelay})
151
152 // Create a context adding the status update channel
153 p := &probe.Probe{}
154 ctx := context.WithValue(context.Background(), probe.ProbeContextKey, p)
155
156 err = waitUntilKVStoreReachableOrMaxTries(ctx, config)
157 if err != nil {
158 logger.Fatalw(ctx, "Unable-to-connect-to-KV-store", log.Fields{"KVStoreType": config.KVStoreType, "Address": config.KVStoreEndPoint})
159 }
160
161 logger.Info(ctx, "KV-store-reachable")
162 //Read if log-level is stored in DB
163 if logLevel, err := dbHandler.Get(db.GetKeyPath(db.LogLevelPath)); err == nil {
164 logger.Info(ctx, "Read log-level from db", log.Fields{"logLevel": logLevel})
165 storedLogLevel, _ := log.StringToLogLevel(logLevel)
166 log.SetAllLogLevel(storedLogLevel)
167 log.SetDefaultLogLevel(storedLogLevel)
168 }
169
170 // Check if Data Migration is required
171 // Migration has to be done before Initialzing the Kafka
172 if app.CheckIfMigrationRequired(ctx) {
173 logger.Debug(ctx, "Migration Initiated")
174 app.InitiateDataMigration(ctx)
175 }
176
177 defer func() {
178 err := log.CleanUp()
179 if err != nil {
180 logger.Errorw(ctx, "unable-to-flush-any-buffered-log-entries", log.Fields{"error": err})
181 }
182 }()
183
184 // TODO: Wrap it up properly and monitor the KV store to check for faults
185
186 /*
187 * Create and start the liveness and readiness container management probes. This
188 * is done in the main function so just in case the main starts multiple other
189 * objects there can be a single probe end point for the process.
190 */
191 go p.ListenAndServe(ctx, config.ProbeEndPoint)
192
193 app.GetApplication().ReadAllFromDb()
194 app.GetApplication().InitStaticConfig()
195 app.GetApplication().SetVendorID(config.VendorID)
196 ofca := controller.NewController(ctx, app.GetApplication())
197 controller.SetDeviceTableSyncDuration(config.DeviceSyncDuration)
198 vpa, err1 := vpagent.NewVPAgent(&vpagent.VPAgent{
199 VolthaAPIEndPoint: config.VolthaAPIEndPoint,
200 DeviceListRefreshInterval: time.Duration(config.DeviceListRefreshInterval) * time.Second,
201 ConnectionMaxRetries: config.ConnectionMaxRetries,
202 ConnectionRetryDelay: time.Duration(config.ConnectionRetryDelay) * time.Second,
203 VPClientAgent: ofca,
204 })
205 if err1 != nil {
206 logger.Fatalw(ctx, "failed-to-create-vpagent",
207 log.Fields{
208 "error": err})
209 }
210 // starts go routine which verifies dhcp server connectivity for requests
211 app.StartDhcpServerHandler()
212 logger.Error(ctx, "Trigger Rest Server...")
213 go nbi.RestStart()
214 go vpa.Run(ctx)
215 //FIXME: Need to enhance CLI to use in docker environment
216 //go ProcessCli()
217 //go handler.MsgHandler()
218 //go app.StartCollector()
219 waitForExit()
220 app.StopTimer()
221 stop(ctx, vgcInfo.kvClient, vpa)
222}
223
224func waitForExit() int {
225 signalChannel := make(chan os.Signal, 1)
226 signal.Notify(signalChannel,
227 syscall.SIGHUP,
228 syscall.SIGINT,
229 syscall.SIGTERM,
230 syscall.SIGQUIT)
231
232 exitChannel := make(chan int)
233
234 go func() {
235 s := <-signalChannel
236 switch s {
237 case syscall.SIGHUP,
238 syscall.SIGINT,
239 syscall.SIGTERM,
240 syscall.SIGQUIT:
241 logger.Infow(ctx, "closing-signal-received", log.Fields{"signal": s})
242 exitChannel <- 0
243 default:
244 logger.Infow(ctx, "unexpected-signal-received", log.Fields{"signal": s})
245 exitChannel <- 1
246 }
247 }()
248
249 code := <-exitChannel
250 return code
251}