blob: b44e5c06925503ebdde7c9e92b475eec19d21509 [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 */
16package core
17
18import (
19 "context"
20 "fmt"
khenaidoo1ce37ad2019-03-24 22:07:24 -040021 "github.com/opencord/voltha-go/rw_core/utils"
Scott Baker807addd2019-10-24 15:16:21 -070022 "github.com/opencord/voltha-lib-go/v2/pkg/db/kvstore"
23 "github.com/opencord/voltha-lib-go/v2/pkg/log"
Scott Baker555307d2019-11-04 08:58:01 -080024 "github.com/opencord/voltha-protos/v2/go/voltha"
khenaidoofffcc8a2019-03-13 11:54:41 -040025 "google.golang.org/grpc/codes"
26 "google.golang.org/grpc/status"
27 "sync"
28 "time"
29)
30
khenaidoo1ce37ad2019-03-24 22:07:24 -040031func init() {
32 log.AddPackage(log.JSON, log.WarnLevel, nil)
33}
34
khenaidoofffcc8a2019-03-13 11:54:41 -040035type ownership struct {
36 id string
37 owned bool
38 chnl chan int
39}
40
41type DeviceOwnership struct {
42 instanceId string
43 exitChannel chan int
44 kvClient kvstore.Client
45 reservationTimeout int64 // Duration in seconds
46 ownershipPrefix string
khenaidoo1ce37ad2019-03-24 22:07:24 -040047 deviceMgr *DeviceManager
48 logicalDeviceMgr *LogicalDeviceManager
khenaidoofffcc8a2019-03-13 11:54:41 -040049 deviceMap map[string]*ownership
khenaidoo631fe542019-05-31 15:44:43 -040050 deviceMapLock sync.RWMutex
khenaidoo1ce37ad2019-03-24 22:07:24 -040051 deviceToKeyMap map[string]string
khenaidoo631fe542019-05-31 15:44:43 -040052 deviceToKeyMapLock sync.RWMutex
53 ownershipLock sync.RWMutex
khenaidoofffcc8a2019-03-13 11:54:41 -040054}
55
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
58 deviceOwnership.instanceId = id
59 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
73func (da *DeviceOwnership) Start(ctx context.Context) {
74 log.Info("starting-deviceOwnership", log.Fields{"instanceId": da.instanceId})
75 log.Info("deviceOwnership-started")
76}
77
78func (da *DeviceOwnership) Stop(ctx context.Context) {
79 log.Info("stopping-deviceOwnership")
80 da.exitChannel <- 1
81 // Need to flush all device reservations
khenaidoo1ce37ad2019-03-24 22:07:24 -040082 da.abandonAllDevices()
khenaidoofffcc8a2019-03-13 11:54:41 -040083 log.Info("deviceOwnership-stopped")
84}
85
86func (da *DeviceOwnership) tryToReserveKey(id string) bool {
87 var currOwner string
khenaidoo1ce37ad2019-03-24 22:07:24 -040088 //Try to reserve the key
khenaidoofffcc8a2019-03-13 11:54:41 -040089 kvKey := fmt.Sprintf("%s_%s", da.ownershipPrefix, id)
90 value, err := da.kvClient.Reserve(kvKey, da.instanceId, da.reservationTimeout)
khenaidoo1ce37ad2019-03-24 22:07:24 -040091 if err != nil {
92 log.Errorw("error", log.Fields{"error": err, "id": id, "instanceId": da.instanceId})
93 }
khenaidoofffcc8a2019-03-13 11:54:41 -040094 if value != nil {
95 if currOwner, err = kvstore.ToString(value); err != nil {
96 log.Error("unexpected-owner-type")
97 }
98 return currOwner == da.instanceId
99 }
100 return false
101}
102
khenaidoo1ce37ad2019-03-24 22:07:24 -0400103func (da *DeviceOwnership) renewReservation(id string) bool {
104 // Try to reserve the key
105 kvKey := fmt.Sprintf("%s_%s", da.ownershipPrefix, id)
106 if err := da.kvClient.RenewReservation(kvKey); err != nil {
107 log.Errorw("reservation-renewal-error", log.Fields{"error": err, "instance": da.instanceId})
108 return false
109 }
110 return true
111}
Richard Jankowski199fd862019-03-18 14:49:51 -0400112
khenaidoo09771ef2019-10-11 14:25:02 -0400113func (da *DeviceOwnership) monitorOwnership(id string, chnl chan int) {
khenaidoo4554f7c2019-05-29 22:13:15 -0400114 log.Debugw("start-device-monitoring", log.Fields{"id": id})
khenaidoo1ce37ad2019-03-24 22:07:24 -0400115 op := "starting"
116 exit := false
117 ticker := time.NewTicker(time.Duration(da.reservationTimeout) / 3 * time.Second)
khenaidoofffcc8a2019-03-13 11:54:41 -0400118 for {
khenaidoo1ce37ad2019-03-24 22:07:24 -0400119 select {
120 case <-da.exitChannel:
khenaidoo4554f7c2019-05-29 22:13:15 -0400121 log.Debugw("closing-monitoring", log.Fields{"Id": id})
khenaidoo1ce37ad2019-03-24 22:07:24 -0400122 exit = true
123 case <-ticker.C:
124 log.Debugw(fmt.Sprintf("%s-reservation", op), log.Fields{"Id": id})
125 case <-chnl:
khenaidoo4554f7c2019-05-29 22:13:15 -0400126 log.Debugw("closing-device-monitoring", log.Fields{"Id": id})
khenaidoo1ce37ad2019-03-24 22:07:24 -0400127 exit = true
128 }
129 if exit {
khenaidoo4554f7c2019-05-29 22:13:15 -0400130 log.Infow("exiting-device-monitoring", log.Fields{"Id": id})
khenaidoo1ce37ad2019-03-24 22:07:24 -0400131 ticker.Stop()
132 break
133 }
134 deviceOwned, ownedByMe := da.getOwnership(id)
135 if deviceOwned && ownedByMe {
Richard Jankowski199fd862019-03-18 14:49:51 -0400136 // Device owned; renew reservation
137 op = "renew"
khenaidoo1ce37ad2019-03-24 22:07:24 -0400138 if da.renewReservation(id) {
139 log.Debugw("reservation-renewed", log.Fields{"id": id, "instanceId": da.instanceId})
140 } else {
141 log.Debugw("reservation-not-renewed", log.Fields{"id": id, "instanceId": da.instanceId})
Richard Jankowski199fd862019-03-18 14:49:51 -0400142 }
143 } else {
khenaidoo1ce37ad2019-03-24 22:07:24 -0400144 // Device not owned or not owned by me; try to seize ownership
Richard Jankowski199fd862019-03-18 14:49:51 -0400145 op = "retry"
146 if err := da.setOwnership(id, da.tryToReserveKey(id)); err != nil {
147 log.Errorw("unexpected-error", log.Fields{"error": err})
148 }
khenaidoofffcc8a2019-03-13 11:54:41 -0400149 }
khenaidoofffcc8a2019-03-13 11:54:41 -0400150 }
khenaidoo4554f7c2019-05-29 22:13:15 -0400151 log.Debugw("device-monitoring-stopped", log.Fields{"id": id})
khenaidoofffcc8a2019-03-13 11:54:41 -0400152}
153
khenaidoo1ce37ad2019-03-24 22:07:24 -0400154func (da *DeviceOwnership) getOwnership(id string) (bool, bool) {
khenaidoofffcc8a2019-03-13 11:54:41 -0400155 da.deviceMapLock.RLock()
156 defer da.deviceMapLock.RUnlock()
157 if val, exist := da.deviceMap[id]; exist {
khenaidoo1ce37ad2019-03-24 22:07:24 -0400158 return true, val.owned
khenaidoofffcc8a2019-03-13 11:54:41 -0400159 }
khenaidoo1ce37ad2019-03-24 22:07:24 -0400160 return false, false
khenaidoofffcc8a2019-03-13 11:54:41 -0400161}
162
163func (da *DeviceOwnership) setOwnership(id string, owner bool) error {
164 da.deviceMapLock.Lock()
165 defer da.deviceMapLock.Unlock()
166 if _, exist := da.deviceMap[id]; exist {
167 if da.deviceMap[id].owned != owner {
168 log.Debugw("ownership-changed", log.Fields{"Id": id, "owner": owner})
169 }
170 da.deviceMap[id].owned = owner
171 return nil
172 }
173 return status.Error(codes.NotFound, fmt.Sprintf("id-inexistent-%s", id))
174}
175
khenaidooba6b6c42019-08-02 09:11:56 -0400176// getAllDeviceIdsOwnedByMe returns all the deviceIds (root device Ids) that is managed by this Core
177func (da *DeviceOwnership) GetAllDeviceIdsOwnedByMe() []string {
178 deviceIds := []string{}
179 da.deviceMapLock.Lock()
180 defer da.deviceMapLock.Unlock()
181 for _, ownership := range da.deviceMap {
182 if ownership.owned {
183 deviceIds = append(deviceIds, ownership.id)
184 }
185 }
186 return deviceIds
187}
188
khenaidoo09771ef2019-10-11 14:25:02 -0400189// OwnedByMe returns whether this Core instance active owns this device. This function will automatically
khenaidoofffcc8a2019-03-13 11:54:41 -0400190// trigger the process to monitor the device and update the device ownership regularly.
khenaidoo09771ef2019-10-11 14:25:02 -0400191func (da *DeviceOwnership) OwnedByMe(id interface{}) (bool, error) {
khenaidoo1ce37ad2019-03-24 22:07:24 -0400192 // Retrieve the ownership key based on the id
193 var ownershipKey string
194 var err error
khenaidoo6d62c002019-05-15 21:57:03 -0400195 var idStr string
196 var cache bool
197 if ownershipKey, idStr, cache, err = da.getOwnershipKey(id); err != nil {
khenaidoo1ce37ad2019-03-24 22:07:24 -0400198 log.Warnw("no-ownershipkey", log.Fields{"error": err})
khenaidoo09771ef2019-10-11 14:25:02 -0400199 return false, err
khenaidoo1ce37ad2019-03-24 22:07:24 -0400200 }
201
khenaidoo6d62c002019-05-15 21:57:03 -0400202 // Update the deviceToKey map, if not from cache
203 if !cache {
204 da.deviceToKeyMapLock.Lock()
205 da.deviceToKeyMap[idStr] = ownershipKey
206 da.deviceToKeyMapLock.Unlock()
207 }
208
khenaidoo4554f7c2019-05-29 22:13:15 -0400209 // Add a lock to prevent creation of two separate monitoring routines for the same device. When a NB request for a
210 // device not in memory is received this results in this function being called in rapid succession, once when
211 // loading the device and once when handling the NB request.
212 da.ownershipLock.Lock()
213 defer da.ownershipLock.Unlock()
214
khenaidoo1ce37ad2019-03-24 22:07:24 -0400215 deviceOwned, ownedByMe := da.getOwnership(ownershipKey)
216 if deviceOwned {
khenaidoo3d3b8c22019-05-22 18:10:39 -0400217 log.Debugw("ownership", log.Fields{"Id": ownershipKey, "owned": ownedByMe})
khenaidoo09771ef2019-10-11 14:25:02 -0400218 return ownedByMe, nil
khenaidoo1ce37ad2019-03-24 22:07:24 -0400219 }
khenaidoo6d62c002019-05-15 21:57:03 -0400220 // Not owned by me or maybe nobody else. Try to reserve it
khenaidoo1ce37ad2019-03-24 22:07:24 -0400221 reservedByMe := da.tryToReserveKey(ownershipKey)
222 myChnl := make(chan int)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400223
224 da.deviceMapLock.Lock()
khenaidoo6d62c002019-05-15 21:57:03 -0400225 da.deviceMap[ownershipKey] = &ownership{
226 id: ownershipKey,
227 owned: reservedByMe,
228 chnl: myChnl}
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400229 da.deviceMapLock.Unlock()
230
khenaidoo1ce37ad2019-03-24 22:07:24 -0400231 log.Debugw("set-new-ownership", log.Fields{"Id": ownershipKey, "owned": reservedByMe})
khenaidoo09771ef2019-10-11 14:25:02 -0400232 go da.monitorOwnership(ownershipKey, myChnl)
233 return reservedByMe, nil
khenaidoofffcc8a2019-03-13 11:54:41 -0400234}
235
236//AbandonDevice must be invoked whenever a device is deleted from the Core
237func (da *DeviceOwnership) AbandonDevice(id string) error {
khenaidoo6d62c002019-05-15 21:57:03 -0400238 if id == "" {
239 return status.Error(codes.FailedPrecondition, "id-nil")
240 }
khenaidoofffcc8a2019-03-13 11:54:41 -0400241 da.deviceMapLock.Lock()
242 defer da.deviceMapLock.Unlock()
khenaidoo0a822f92019-05-08 15:15:57 -0400243 if o, exist := da.deviceMap[id]; exist { // id is ownership key
244 // Need to clean up all deviceToKeyMap entries using this device as key
245 da.deviceToKeyMapLock.Lock()
246 defer da.deviceToKeyMapLock.Unlock()
247 for k, v := range da.deviceToKeyMap {
248 if id == v {
249 delete(da.deviceToKeyMap, k)
250 }
251 }
khenaidoo6d62c002019-05-15 21:57:03 -0400252 // Remove the device reference from the deviceMap
khenaidoo0a822f92019-05-08 15:15:57 -0400253 delete(da.deviceMap, id)
254
khenaidoofffcc8a2019-03-13 11:54:41 -0400255 // Stop the Go routine monitoring the device
256 close(o.chnl)
257 delete(da.deviceMap, id)
258 log.Debugw("abandoning-device", log.Fields{"Id": id})
259 return nil
khenaidoo0a822f92019-05-08 15:15:57 -0400260 } else { // id is not ownership key
khenaidoo6d62c002019-05-15 21:57:03 -0400261 da.deleteDeviceKey(id)
khenaidoofffcc8a2019-03-13 11:54:41 -0400262 }
khenaidoo0a822f92019-05-08 15:15:57 -0400263 return nil
Richard Jankowski199fd862019-03-18 14:49:51 -0400264}
khenaidoo1ce37ad2019-03-24 22:07:24 -0400265
266//abandonAllDevices must be invoked whenever a device is deleted from the Core
267func (da *DeviceOwnership) abandonAllDevices() {
268 da.deviceMapLock.Lock()
269 defer da.deviceMapLock.Unlock()
khenaidoo0a822f92019-05-08 15:15:57 -0400270 da.deviceToKeyMapLock.Lock()
271 defer da.deviceToKeyMapLock.Unlock()
272 for k, _ := range da.deviceToKeyMap {
273 delete(da.deviceToKeyMap, k)
274 }
khenaidoo1ce37ad2019-03-24 22:07:24 -0400275 for _, val := range da.deviceMap {
276 close(val.chnl)
277 }
278}
279
280func (da *DeviceOwnership) getDeviceKey(id string) (string, error) {
281 da.deviceToKeyMapLock.RLock()
282 defer da.deviceToKeyMapLock.RUnlock()
283 if val, exist := da.deviceToKeyMap[id]; exist {
284 return val, nil
285 }
286 return "", status.Error(codes.NotFound, fmt.Sprintf("not-present-%s", id))
287}
288
289func (da *DeviceOwnership) updateDeviceKey(id string, key string) error {
290 da.deviceToKeyMapLock.Lock()
291 defer da.deviceToKeyMapLock.Unlock()
292 if _, exist := da.deviceToKeyMap[id]; exist {
293 return status.Error(codes.AlreadyExists, fmt.Sprintf("already-present-%s", id))
294 }
295 da.deviceToKeyMap[id] = key
296 return nil
297}
298
khenaidoo6d62c002019-05-15 21:57:03 -0400299func (da *DeviceOwnership) deleteDeviceKey(id string) {
khenaidoo0a822f92019-05-08 15:15:57 -0400300 da.deviceToKeyMapLock.Lock()
301 defer da.deviceToKeyMapLock.Unlock()
302 if _, exist := da.deviceToKeyMap[id]; exist {
303 delete(da.deviceToKeyMap, id)
khenaidoo0a822f92019-05-08 15:15:57 -0400304 }
khenaidoo0a822f92019-05-08 15:15:57 -0400305}
306
khenaidoo6d62c002019-05-15 21:57:03 -0400307// getOwnershipKey returns the ownership key that the id param uses. Ownership key is the parent
308// device Id of a child device or the rootdevice of a logical device. This function also returns the
309// id in string format of the id param via the ref output as well as if the data was retrieved from cache
310func (da *DeviceOwnership) getOwnershipKey(id interface{}) (ownershipKey string, ref string, cached bool, err error) {
khenaidoo3d3b8c22019-05-22 18:10:39 -0400311
khenaidoo1ce37ad2019-03-24 22:07:24 -0400312 if id == nil {
khenaidoo6d62c002019-05-15 21:57:03 -0400313 return "", "", false, status.Error(codes.InvalidArgument, "nil-id")
khenaidoo1ce37ad2019-03-24 22:07:24 -0400314 }
khenaidoo6d62c002019-05-15 21:57:03 -0400315 da.deviceToKeyMapLock.RLock()
316 defer da.deviceToKeyMapLock.RUnlock()
khenaidoo1ce37ad2019-03-24 22:07:24 -0400317 var device *voltha.Device
318 var lDevice *voltha.LogicalDevice
319 // The id can either be a device Id or a logical device id.
320 if dId, ok := id.(*utils.DeviceID); ok {
321 // Use cache if present
khenaidoo8f474192019-04-03 17:20:44 -0400322 if val, exist := da.deviceToKeyMap[dId.Id]; exist {
khenaidoo6d62c002019-05-15 21:57:03 -0400323 return val, dId.Id, true, nil
khenaidoo1ce37ad2019-03-24 22:07:24 -0400324 }
325 if device, _ = da.deviceMgr.GetDevice(dId.Id); device == nil {
khenaidoo6d62c002019-05-15 21:57:03 -0400326 return "", dId.Id, false, status.Errorf(codes.NotFound, "id-absent-%s", dId)
khenaidoo1ce37ad2019-03-24 22:07:24 -0400327 }
328 if device.Root {
khenaidoo6d62c002019-05-15 21:57:03 -0400329 return device.Id, dId.Id, false, nil
khenaidoo1ce37ad2019-03-24 22:07:24 -0400330 } else {
khenaidoo6d62c002019-05-15 21:57:03 -0400331 return device.ParentId, dId.Id, false, nil
khenaidoo1ce37ad2019-03-24 22:07:24 -0400332 }
333 } else if ldId, ok := id.(*utils.LogicalDeviceID); ok {
334 // Use cache if present
khenaidoo8f474192019-04-03 17:20:44 -0400335 if val, exist := da.deviceToKeyMap[ldId.Id]; exist {
khenaidoo6d62c002019-05-15 21:57:03 -0400336 return val, ldId.Id, true, nil
khenaidoo1ce37ad2019-03-24 22:07:24 -0400337 }
338 if lDevice, _ = da.logicalDeviceMgr.getLogicalDevice(ldId.Id); lDevice == nil {
khenaidoo6d62c002019-05-15 21:57:03 -0400339 return "", ldId.Id, false, status.Errorf(codes.NotFound, "id-absent-%s", dId)
khenaidoo1ce37ad2019-03-24 22:07:24 -0400340 }
khenaidoo6d62c002019-05-15 21:57:03 -0400341 return lDevice.RootDeviceId, ldId.Id, false, nil
khenaidoo1ce37ad2019-03-24 22:07:24 -0400342 }
khenaidoo6d62c002019-05-15 21:57:03 -0400343 return "", "", false, status.Error(codes.NotFound, fmt.Sprintf("id-%v", id))
khenaidoo1ce37ad2019-03-24 22:07:24 -0400344}