blob: 2389c78adb8d9f63aef907df8a811701f437abce [file] [log] [blame]
khenaidoofffcc8a2019-03-13 11:54:41 -04001/*
2 * Copyright 2019-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 */
npujar1d86a522019-11-14 17:11:16 +053016
khenaidoofffcc8a2019-03-13 11:54:41 -040017package core
18
19import (
20 "context"
21 "fmt"
npujar1d86a522019-11-14 17:11:16 +053022 "sync"
23 "time"
24
khenaidoo1ce37ad2019-03-24 22:07:24 -040025 "github.com/opencord/voltha-go/rw_core/utils"
serkant.uluderya2ae470f2020-01-21 11:13:09 -080026 "github.com/opencord/voltha-lib-go/v3/pkg/db/kvstore"
27 "github.com/opencord/voltha-lib-go/v3/pkg/log"
28 "github.com/opencord/voltha-protos/v3/go/voltha"
khenaidoofffcc8a2019-03-13 11:54:41 -040029 "google.golang.org/grpc/codes"
30 "google.golang.org/grpc/status"
khenaidoofffcc8a2019-03-13 11:54:41 -040031)
32
33type ownership struct {
34 id string
35 owned bool
36 chnl chan int
37}
38
npujar1d86a522019-11-14 17:11:16 +053039// DeviceOwnership represent device ownership attributes
khenaidoofffcc8a2019-03-13 11:54:41 -040040type DeviceOwnership struct {
npujar1d86a522019-11-14 17:11:16 +053041 instanceID string
khenaidoofffcc8a2019-03-13 11:54:41 -040042 exitChannel chan int
43 kvClient kvstore.Client
44 reservationTimeout int64 // Duration in seconds
45 ownershipPrefix string
khenaidoo1ce37ad2019-03-24 22:07:24 -040046 deviceMgr *DeviceManager
47 logicalDeviceMgr *LogicalDeviceManager
khenaidoofffcc8a2019-03-13 11:54:41 -040048 deviceMap map[string]*ownership
khenaidoo631fe542019-05-31 15:44:43 -040049 deviceMapLock sync.RWMutex
khenaidoo1ce37ad2019-03-24 22:07:24 -040050 deviceToKeyMap map[string]string
khenaidoo631fe542019-05-31 15:44:43 -040051 deviceToKeyMapLock sync.RWMutex
52 ownershipLock sync.RWMutex
khenaidoofffcc8a2019-03-13 11:54:41 -040053}
54
npujar1d86a522019-11-14 17:11:16 +053055// NewDeviceOwnership creates device ownership instance
khenaidoo1ce37ad2019-03-24 22:07:24 -040056func NewDeviceOwnership(id string, kvClient kvstore.Client, deviceMgr *DeviceManager, logicalDeviceMgr *LogicalDeviceManager, ownershipPrefix string, reservationTimeout int64) *DeviceOwnership {
khenaidoofffcc8a2019-03-13 11:54:41 -040057 var deviceOwnership DeviceOwnership
npujar1d86a522019-11-14 17:11:16 +053058 deviceOwnership.instanceID = id
khenaidoofffcc8a2019-03-13 11:54:41 -040059 deviceOwnership.exitChannel = make(chan int, 1)
60 deviceOwnership.kvClient = kvClient
khenaidoo1ce37ad2019-03-24 22:07:24 -040061 deviceOwnership.deviceMgr = deviceMgr
62 deviceOwnership.logicalDeviceMgr = logicalDeviceMgr
khenaidoofffcc8a2019-03-13 11:54:41 -040063 deviceOwnership.ownershipPrefix = ownershipPrefix
64 deviceOwnership.reservationTimeout = reservationTimeout
65 deviceOwnership.deviceMap = make(map[string]*ownership)
khenaidoo631fe542019-05-31 15:44:43 -040066 deviceOwnership.deviceMapLock = sync.RWMutex{}
khenaidoo1ce37ad2019-03-24 22:07:24 -040067 deviceOwnership.deviceToKeyMap = make(map[string]string)
khenaidoo631fe542019-05-31 15:44:43 -040068 deviceOwnership.deviceToKeyMapLock = sync.RWMutex{}
69 deviceOwnership.ownershipLock = sync.RWMutex{}
khenaidoofffcc8a2019-03-13 11:54:41 -040070 return &deviceOwnership
71}
72
npujar1d86a522019-11-14 17:11:16 +053073// Start starts device device ownership
khenaidoofffcc8a2019-03-13 11:54:41 -040074func (da *DeviceOwnership) Start(ctx context.Context) {
Girish Kumarf56a4682020-03-20 20:07:46 +000075 logger.Info("starting-deviceOwnership", log.Fields{"instanceId": da.instanceID})
76 logger.Info("deviceOwnership-started")
khenaidoofffcc8a2019-03-13 11:54:41 -040077}
78
npujar1d86a522019-11-14 17:11:16 +053079// Stop stops device ownership
khenaidoofffcc8a2019-03-13 11:54:41 -040080func (da *DeviceOwnership) Stop(ctx context.Context) {
Girish Kumarf56a4682020-03-20 20:07:46 +000081 logger.Info("stopping-deviceOwnership")
khenaidoofffcc8a2019-03-13 11:54:41 -040082 da.exitChannel <- 1
83 // Need to flush all device reservations
khenaidoo1ce37ad2019-03-24 22:07:24 -040084 da.abandonAllDevices()
Girish Kumarf56a4682020-03-20 20:07:46 +000085 logger.Info("deviceOwnership-stopped")
khenaidoofffcc8a2019-03-13 11:54:41 -040086}
87
npujar467fe752020-01-16 20:17:45 +053088func (da *DeviceOwnership) tryToReserveKey(ctx context.Context, id string) bool {
khenaidoofffcc8a2019-03-13 11:54:41 -040089 var currOwner string
khenaidoo1ce37ad2019-03-24 22:07:24 -040090 //Try to reserve the key
khenaidoofffcc8a2019-03-13 11:54:41 -040091 kvKey := fmt.Sprintf("%s_%s", da.ownershipPrefix, id)
npujar467fe752020-01-16 20:17:45 +053092 value, err := da.kvClient.Reserve(ctx, kvKey, da.instanceID, da.reservationTimeout)
khenaidoo1ce37ad2019-03-24 22:07:24 -040093 if err != nil {
Girish Kumarf56a4682020-03-20 20:07:46 +000094 logger.Errorw("error", log.Fields{"error": err, "id": id, "instanceId": da.instanceID})
khenaidoo1ce37ad2019-03-24 22:07:24 -040095 }
khenaidoofffcc8a2019-03-13 11:54:41 -040096 if value != nil {
97 if currOwner, err = kvstore.ToString(value); err != nil {
Girish Kumarf56a4682020-03-20 20:07:46 +000098 logger.Error("unexpected-owner-type")
khenaidoofffcc8a2019-03-13 11:54:41 -040099 }
npujar1d86a522019-11-14 17:11:16 +0530100 return currOwner == da.instanceID
khenaidoofffcc8a2019-03-13 11:54:41 -0400101 }
102 return false
103}
104
npujar467fe752020-01-16 20:17:45 +0530105func (da *DeviceOwnership) renewReservation(ctx context.Context, id string) bool {
khenaidoo1ce37ad2019-03-24 22:07:24 -0400106 // Try to reserve the key
107 kvKey := fmt.Sprintf("%s_%s", da.ownershipPrefix, id)
npujar467fe752020-01-16 20:17:45 +0530108 if err := da.kvClient.RenewReservation(ctx, kvKey); err != nil {
Girish Kumarf56a4682020-03-20 20:07:46 +0000109 logger.Errorw("reservation-renewal-error", log.Fields{"error": err, "instance": da.instanceID})
khenaidoo1ce37ad2019-03-24 22:07:24 -0400110 return false
111 }
112 return true
113}
Richard Jankowski199fd862019-03-18 14:49:51 -0400114
npujar467fe752020-01-16 20:17:45 +0530115func (da *DeviceOwnership) monitorOwnership(ctx context.Context, id string, chnl chan int) {
Girish Kumarf56a4682020-03-20 20:07:46 +0000116 logger.Debugw("start-device-monitoring", log.Fields{"id": id})
khenaidoo1ce37ad2019-03-24 22:07:24 -0400117 op := "starting"
118 exit := false
119 ticker := time.NewTicker(time.Duration(da.reservationTimeout) / 3 * time.Second)
khenaidoofffcc8a2019-03-13 11:54:41 -0400120 for {
khenaidoo1ce37ad2019-03-24 22:07:24 -0400121 select {
122 case <-da.exitChannel:
Girish Kumarf56a4682020-03-20 20:07:46 +0000123 logger.Debugw("closing-monitoring", log.Fields{"Id": id})
khenaidoo1ce37ad2019-03-24 22:07:24 -0400124 exit = true
125 case <-ticker.C:
Girish Kumarf56a4682020-03-20 20:07:46 +0000126 logger.Debugw(fmt.Sprintf("%s-reservation", op), log.Fields{"Id": id})
khenaidoo1ce37ad2019-03-24 22:07:24 -0400127 case <-chnl:
Girish Kumarf56a4682020-03-20 20:07:46 +0000128 logger.Debugw("closing-device-monitoring", log.Fields{"Id": id})
khenaidoo1ce37ad2019-03-24 22:07:24 -0400129 exit = true
130 }
131 if exit {
Girish Kumarf56a4682020-03-20 20:07:46 +0000132 logger.Infow("exiting-device-monitoring", log.Fields{"Id": id})
khenaidoo1ce37ad2019-03-24 22:07:24 -0400133 ticker.Stop()
134 break
135 }
136 deviceOwned, ownedByMe := da.getOwnership(id)
137 if deviceOwned && ownedByMe {
Richard Jankowski199fd862019-03-18 14:49:51 -0400138 // Device owned; renew reservation
139 op = "renew"
npujar467fe752020-01-16 20:17:45 +0530140 if da.renewReservation(ctx, id) {
Girish Kumarf56a4682020-03-20 20:07:46 +0000141 logger.Debugw("reservation-renewed", log.Fields{"id": id, "instanceId": da.instanceID})
khenaidoo1ce37ad2019-03-24 22:07:24 -0400142 } else {
Girish Kumarf56a4682020-03-20 20:07:46 +0000143 logger.Debugw("reservation-not-renewed", log.Fields{"id": id, "instanceId": da.instanceID})
Richard Jankowski199fd862019-03-18 14:49:51 -0400144 }
145 } else {
khenaidoo1ce37ad2019-03-24 22:07:24 -0400146 // Device not owned or not owned by me; try to seize ownership
Richard Jankowski199fd862019-03-18 14:49:51 -0400147 op = "retry"
npujar467fe752020-01-16 20:17:45 +0530148 if err := da.setOwnership(id, da.tryToReserveKey(ctx, id)); err != nil {
Girish Kumarf56a4682020-03-20 20:07:46 +0000149 logger.Errorw("unexpected-error", log.Fields{"error": err})
Richard Jankowski199fd862019-03-18 14:49:51 -0400150 }
khenaidoofffcc8a2019-03-13 11:54:41 -0400151 }
khenaidoofffcc8a2019-03-13 11:54:41 -0400152 }
Girish Kumarf56a4682020-03-20 20:07:46 +0000153 logger.Debugw("device-monitoring-stopped", log.Fields{"id": id})
khenaidoofffcc8a2019-03-13 11:54:41 -0400154}
155
khenaidoo1ce37ad2019-03-24 22:07:24 -0400156func (da *DeviceOwnership) getOwnership(id string) (bool, bool) {
khenaidoofffcc8a2019-03-13 11:54:41 -0400157 da.deviceMapLock.RLock()
158 defer da.deviceMapLock.RUnlock()
159 if val, exist := da.deviceMap[id]; exist {
khenaidoo1ce37ad2019-03-24 22:07:24 -0400160 return true, val.owned
khenaidoofffcc8a2019-03-13 11:54:41 -0400161 }
khenaidoo1ce37ad2019-03-24 22:07:24 -0400162 return false, false
khenaidoofffcc8a2019-03-13 11:54:41 -0400163}
164
165func (da *DeviceOwnership) setOwnership(id string, owner bool) error {
166 da.deviceMapLock.Lock()
167 defer da.deviceMapLock.Unlock()
168 if _, exist := da.deviceMap[id]; exist {
169 if da.deviceMap[id].owned != owner {
Girish Kumarf56a4682020-03-20 20:07:46 +0000170 logger.Debugw("ownership-changed", log.Fields{"Id": id, "owner": owner})
khenaidoofffcc8a2019-03-13 11:54:41 -0400171 }
172 da.deviceMap[id].owned = owner
173 return nil
174 }
175 return status.Error(codes.NotFound, fmt.Sprintf("id-inexistent-%s", id))
176}
177
npujar1d86a522019-11-14 17:11:16 +0530178// GetAllDeviceIdsOwnedByMe returns all the deviceIds (root device Ids) that is managed by this Core
khenaidooba6b6c42019-08-02 09:11:56 -0400179func (da *DeviceOwnership) GetAllDeviceIdsOwnedByMe() []string {
180 deviceIds := []string{}
181 da.deviceMapLock.Lock()
182 defer da.deviceMapLock.Unlock()
183 for _, ownership := range da.deviceMap {
184 if ownership.owned {
185 deviceIds = append(deviceIds, ownership.id)
186 }
187 }
188 return deviceIds
189}
190
khenaidoo09771ef2019-10-11 14:25:02 -0400191// OwnedByMe returns whether this Core instance active owns this device. This function will automatically
khenaidoofffcc8a2019-03-13 11:54:41 -0400192// trigger the process to monitor the device and update the device ownership regularly.
npujar467fe752020-01-16 20:17:45 +0530193func (da *DeviceOwnership) OwnedByMe(ctx context.Context, id interface{}) (bool, error) {
khenaidoo1ce37ad2019-03-24 22:07:24 -0400194 // Retrieve the ownership key based on the id
195 var ownershipKey string
196 var err error
khenaidoo6d62c002019-05-15 21:57:03 -0400197 var idStr string
198 var cache bool
npujar467fe752020-01-16 20:17:45 +0530199 if ownershipKey, idStr, cache, err = da.getOwnershipKey(ctx, id); err != nil {
Girish Kumarf56a4682020-03-20 20:07:46 +0000200 logger.Warnw("no-ownershipkey", log.Fields{"error": err})
khenaidoo09771ef2019-10-11 14:25:02 -0400201 return false, err
khenaidoo1ce37ad2019-03-24 22:07:24 -0400202 }
203
khenaidoo6d62c002019-05-15 21:57:03 -0400204 // Update the deviceToKey map, if not from cache
205 if !cache {
206 da.deviceToKeyMapLock.Lock()
207 da.deviceToKeyMap[idStr] = ownershipKey
208 da.deviceToKeyMapLock.Unlock()
209 }
210
khenaidoo4554f7c2019-05-29 22:13:15 -0400211 // Add a lock to prevent creation of two separate monitoring routines for the same device. When a NB request for a
212 // device not in memory is received this results in this function being called in rapid succession, once when
213 // loading the device and once when handling the NB request.
214 da.ownershipLock.Lock()
215 defer da.ownershipLock.Unlock()
216
khenaidoo1ce37ad2019-03-24 22:07:24 -0400217 deviceOwned, ownedByMe := da.getOwnership(ownershipKey)
218 if deviceOwned {
Girish Kumarf56a4682020-03-20 20:07:46 +0000219 logger.Debugw("ownership", log.Fields{"Id": ownershipKey, "owned": ownedByMe})
khenaidoo09771ef2019-10-11 14:25:02 -0400220 return ownedByMe, nil
khenaidoo1ce37ad2019-03-24 22:07:24 -0400221 }
khenaidoo6d62c002019-05-15 21:57:03 -0400222 // Not owned by me or maybe nobody else. Try to reserve it
npujar467fe752020-01-16 20:17:45 +0530223 reservedByMe := da.tryToReserveKey(ctx, ownershipKey)
khenaidoo1ce37ad2019-03-24 22:07:24 -0400224 myChnl := make(chan int)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400225
226 da.deviceMapLock.Lock()
khenaidoo6d62c002019-05-15 21:57:03 -0400227 da.deviceMap[ownershipKey] = &ownership{
228 id: ownershipKey,
229 owned: reservedByMe,
230 chnl: myChnl}
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400231 da.deviceMapLock.Unlock()
232
Girish Kumarf56a4682020-03-20 20:07:46 +0000233 logger.Debugw("set-new-ownership", log.Fields{"Id": ownershipKey, "owned": reservedByMe})
npujar467fe752020-01-16 20:17:45 +0530234 go da.monitorOwnership(context.Background(), ownershipKey, myChnl)
khenaidoo09771ef2019-10-11 14:25:02 -0400235 return reservedByMe, nil
khenaidoofffcc8a2019-03-13 11:54:41 -0400236}
237
238//AbandonDevice must be invoked whenever a device is deleted from the Core
239func (da *DeviceOwnership) AbandonDevice(id string) error {
khenaidoo6d62c002019-05-15 21:57:03 -0400240 if id == "" {
241 return status.Error(codes.FailedPrecondition, "id-nil")
242 }
khenaidoofffcc8a2019-03-13 11:54:41 -0400243 da.deviceMapLock.Lock()
244 defer da.deviceMapLock.Unlock()
npujar1d86a522019-11-14 17:11:16 +0530245 o, exist := da.deviceMap[id]
246 if exist { // id is ownership key
khenaidoo0a822f92019-05-08 15:15:57 -0400247 // Need to clean up all deviceToKeyMap entries using this device as key
248 da.deviceToKeyMapLock.Lock()
249 defer da.deviceToKeyMapLock.Unlock()
250 for k, v := range da.deviceToKeyMap {
251 if id == v {
252 delete(da.deviceToKeyMap, k)
253 }
254 }
khenaidoo6d62c002019-05-15 21:57:03 -0400255 // Remove the device reference from the deviceMap
khenaidoo0a822f92019-05-08 15:15:57 -0400256 delete(da.deviceMap, id)
257
khenaidoofffcc8a2019-03-13 11:54:41 -0400258 // Stop the Go routine monitoring the device
259 close(o.chnl)
260 delete(da.deviceMap, id)
Girish Kumarf56a4682020-03-20 20:07:46 +0000261 logger.Debugw("abandoning-device", log.Fields{"Id": id})
khenaidoofffcc8a2019-03-13 11:54:41 -0400262 return nil
263 }
npujar1d86a522019-11-14 17:11:16 +0530264 // id is not ownership key
265 da.deleteDeviceKey(id)
khenaidoo0a822f92019-05-08 15:15:57 -0400266 return nil
Richard Jankowski199fd862019-03-18 14:49:51 -0400267}
khenaidoo1ce37ad2019-03-24 22:07:24 -0400268
269//abandonAllDevices must be invoked whenever a device is deleted from the Core
270func (da *DeviceOwnership) abandonAllDevices() {
271 da.deviceMapLock.Lock()
272 defer da.deviceMapLock.Unlock()
khenaidoo0a822f92019-05-08 15:15:57 -0400273 da.deviceToKeyMapLock.Lock()
274 defer da.deviceToKeyMapLock.Unlock()
npujar1d86a522019-11-14 17:11:16 +0530275 for k := range da.deviceToKeyMap {
khenaidoo0a822f92019-05-08 15:15:57 -0400276 delete(da.deviceToKeyMap, k)
277 }
khenaidoo1ce37ad2019-03-24 22:07:24 -0400278 for _, val := range da.deviceMap {
279 close(val.chnl)
280 }
281}
282
khenaidoo6d62c002019-05-15 21:57:03 -0400283func (da *DeviceOwnership) deleteDeviceKey(id string) {
khenaidoo0a822f92019-05-08 15:15:57 -0400284 da.deviceToKeyMapLock.Lock()
285 defer da.deviceToKeyMapLock.Unlock()
Kent Hagermandcd4dcc2020-02-25 17:56:17 -0500286 delete(da.deviceToKeyMap, id)
khenaidoo0a822f92019-05-08 15:15:57 -0400287}
288
khenaidoo6d62c002019-05-15 21:57:03 -0400289// getOwnershipKey returns the ownership key that the id param uses. Ownership key is the parent
290// device Id of a child device or the rootdevice of a logical device. This function also returns the
291// id in string format of the id param via the ref output as well as if the data was retrieved from cache
npujar467fe752020-01-16 20:17:45 +0530292func (da *DeviceOwnership) getOwnershipKey(ctx context.Context, id interface{}) (ownershipKey string, ref string, cached bool, err error) {
khenaidoo3d3b8c22019-05-22 18:10:39 -0400293
khenaidoo1ce37ad2019-03-24 22:07:24 -0400294 if id == nil {
khenaidoo6d62c002019-05-15 21:57:03 -0400295 return "", "", false, status.Error(codes.InvalidArgument, "nil-id")
khenaidoo1ce37ad2019-03-24 22:07:24 -0400296 }
khenaidoo6d62c002019-05-15 21:57:03 -0400297 da.deviceToKeyMapLock.RLock()
298 defer da.deviceToKeyMapLock.RUnlock()
khenaidoo1ce37ad2019-03-24 22:07:24 -0400299 var device *voltha.Device
300 var lDevice *voltha.LogicalDevice
301 // The id can either be a device Id or a logical device id.
npujar1d86a522019-11-14 17:11:16 +0530302 if dID, ok := id.(*utils.DeviceID); ok {
khenaidoo1ce37ad2019-03-24 22:07:24 -0400303 // Use cache if present
npujar1d86a522019-11-14 17:11:16 +0530304 if val, exist := da.deviceToKeyMap[dID.ID]; exist {
305 return val, dID.ID, true, nil
khenaidoo1ce37ad2019-03-24 22:07:24 -0400306 }
npujar467fe752020-01-16 20:17:45 +0530307 if device, _ = da.deviceMgr.GetDevice(ctx, dID.ID); device == nil {
npujar1d86a522019-11-14 17:11:16 +0530308 return "", dID.ID, false, status.Errorf(codes.NotFound, "id-absent-%s", dID)
khenaidoo1ce37ad2019-03-24 22:07:24 -0400309 }
310 if device.Root {
npujar1d86a522019-11-14 17:11:16 +0530311 return device.Id, dID.ID, false, nil
khenaidoo1ce37ad2019-03-24 22:07:24 -0400312 }
npujar1d86a522019-11-14 17:11:16 +0530313 return device.ParentId, dID.ID, false, nil
314 } else if ldID, ok := id.(*utils.LogicalDeviceID); ok {
khenaidoo1ce37ad2019-03-24 22:07:24 -0400315 // Use cache if present
npujar1d86a522019-11-14 17:11:16 +0530316 if val, exist := da.deviceToKeyMap[ldID.ID]; exist {
317 return val, ldID.ID, true, nil
khenaidoo1ce37ad2019-03-24 22:07:24 -0400318 }
npujar467fe752020-01-16 20:17:45 +0530319 if lDevice, _ = da.logicalDeviceMgr.getLogicalDevice(ctx, ldID.ID); lDevice == nil {
npujar1d86a522019-11-14 17:11:16 +0530320 return "", ldID.ID, false, status.Errorf(codes.NotFound, "id-absent-%s", dID)
khenaidoo1ce37ad2019-03-24 22:07:24 -0400321 }
npujar1d86a522019-11-14 17:11:16 +0530322 return lDevice.RootDeviceId, ldID.ID, false, nil
khenaidoo1ce37ad2019-03-24 22:07:24 -0400323 }
khenaidoo6d62c002019-05-15 21:57:03 -0400324 return "", "", false, status.Error(codes.NotFound, fmt.Sprintf("id-%v", id))
khenaidoo1ce37ad2019-03-24 22:07:24 -0400325}