blob: 5deb1cbeed2c1c040f4109cfa562cc83b356b03e [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"
21 "github.com/opencord/voltha-go/common/log"
22 "github.com/opencord/voltha-go/db/kvstore"
khenaidoo1ce37ad2019-03-24 22:07:24 -040023 "github.com/opencord/voltha-go/rw_core/utils"
khenaidoo2c6a0992019-04-29 13:46:56 -040024 "github.com/opencord/voltha-protos/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
50 deviceMapLock *sync.RWMutex
khenaidoo1ce37ad2019-03-24 22:07:24 -040051 deviceToKeyMap map[string]string
52 deviceToKeyMapLock *sync.RWMutex
khenaidoofffcc8a2019-03-13 11:54:41 -040053}
54
khenaidoo1ce37ad2019-03-24 22:07:24 -040055func NewDeviceOwnership(id string, kvClient kvstore.Client, deviceMgr *DeviceManager, logicalDeviceMgr *LogicalDeviceManager, ownershipPrefix string, reservationTimeout int64) *DeviceOwnership {
khenaidoofffcc8a2019-03-13 11:54:41 -040056 var deviceOwnership DeviceOwnership
57 deviceOwnership.instanceId = id
58 deviceOwnership.exitChannel = make(chan int, 1)
59 deviceOwnership.kvClient = kvClient
khenaidoo1ce37ad2019-03-24 22:07:24 -040060 deviceOwnership.deviceMgr = deviceMgr
61 deviceOwnership.logicalDeviceMgr = logicalDeviceMgr
khenaidoofffcc8a2019-03-13 11:54:41 -040062 deviceOwnership.ownershipPrefix = ownershipPrefix
63 deviceOwnership.reservationTimeout = reservationTimeout
64 deviceOwnership.deviceMap = make(map[string]*ownership)
65 deviceOwnership.deviceMapLock = &sync.RWMutex{}
khenaidoo1ce37ad2019-03-24 22:07:24 -040066 deviceOwnership.deviceToKeyMap = make(map[string]string)
67 deviceOwnership.deviceToKeyMapLock = &sync.RWMutex{}
khenaidoofffcc8a2019-03-13 11:54:41 -040068 return &deviceOwnership
69}
70
71func (da *DeviceOwnership) Start(ctx context.Context) {
72 log.Info("starting-deviceOwnership", log.Fields{"instanceId": da.instanceId})
73 log.Info("deviceOwnership-started")
74}
75
76func (da *DeviceOwnership) Stop(ctx context.Context) {
77 log.Info("stopping-deviceOwnership")
78 da.exitChannel <- 1
79 // Need to flush all device reservations
khenaidoo1ce37ad2019-03-24 22:07:24 -040080 da.abandonAllDevices()
khenaidoofffcc8a2019-03-13 11:54:41 -040081 log.Info("deviceOwnership-stopped")
82}
83
84func (da *DeviceOwnership) tryToReserveKey(id string) bool {
85 var currOwner string
khenaidoo1ce37ad2019-03-24 22:07:24 -040086 //Try to reserve the key
khenaidoofffcc8a2019-03-13 11:54:41 -040087 kvKey := fmt.Sprintf("%s_%s", da.ownershipPrefix, id)
88 value, err := da.kvClient.Reserve(kvKey, da.instanceId, da.reservationTimeout)
khenaidoo1ce37ad2019-03-24 22:07:24 -040089 if err != nil {
90 log.Errorw("error", log.Fields{"error": err, "id": id, "instanceId": da.instanceId})
91 }
khenaidoofffcc8a2019-03-13 11:54:41 -040092 if value != nil {
93 if currOwner, err = kvstore.ToString(value); err != nil {
94 log.Error("unexpected-owner-type")
95 }
96 return currOwner == da.instanceId
97 }
98 return false
99}
100
khenaidoo1ce37ad2019-03-24 22:07:24 -0400101func (da *DeviceOwnership) renewReservation(id string) bool {
102 // Try to reserve the key
103 kvKey := fmt.Sprintf("%s_%s", da.ownershipPrefix, id)
104 if err := da.kvClient.RenewReservation(kvKey); err != nil {
105 log.Errorw("reservation-renewal-error", log.Fields{"error": err, "instance": da.instanceId})
106 return false
107 }
108 return true
109}
Richard Jankowski199fd862019-03-18 14:49:51 -0400110
khenaidoo1ce37ad2019-03-24 22:07:24 -0400111func (da *DeviceOwnership) MonitorOwnership(id string, chnl chan int) {
112 op := "starting"
113 exit := false
114 ticker := time.NewTicker(time.Duration(da.reservationTimeout) / 3 * time.Second)
khenaidoofffcc8a2019-03-13 11:54:41 -0400115 for {
khenaidoo1ce37ad2019-03-24 22:07:24 -0400116 select {
117 case <-da.exitChannel:
118 log.Infow("closing-monitoring", log.Fields{"Id": id})
119 exit = true
120 case <-ticker.C:
121 log.Debugw(fmt.Sprintf("%s-reservation", op), log.Fields{"Id": id})
122 case <-chnl:
123 log.Infow("closing-device-monitoring", log.Fields{"Id": id})
124 exit = true
125 }
126 if exit {
127 ticker.Stop()
128 break
129 }
130 deviceOwned, ownedByMe := da.getOwnership(id)
131 if deviceOwned && ownedByMe {
Richard Jankowski199fd862019-03-18 14:49:51 -0400132 // Device owned; renew reservation
133 op = "renew"
khenaidoo1ce37ad2019-03-24 22:07:24 -0400134 if da.renewReservation(id) {
135 log.Debugw("reservation-renewed", log.Fields{"id": id, "instanceId": da.instanceId})
136 } else {
137 log.Debugw("reservation-not-renewed", log.Fields{"id": id, "instanceId": da.instanceId})
Richard Jankowski199fd862019-03-18 14:49:51 -0400138 }
139 } else {
khenaidoo1ce37ad2019-03-24 22:07:24 -0400140 // Device not owned or not owned by me; try to seize ownership
Richard Jankowski199fd862019-03-18 14:49:51 -0400141 op = "retry"
142 if err := da.setOwnership(id, da.tryToReserveKey(id)); err != nil {
143 log.Errorw("unexpected-error", log.Fields{"error": err})
144 }
khenaidoofffcc8a2019-03-13 11:54:41 -0400145 }
khenaidoofffcc8a2019-03-13 11:54:41 -0400146 }
147}
148
khenaidoo1ce37ad2019-03-24 22:07:24 -0400149func (da *DeviceOwnership) getOwnership(id string) (bool, bool) {
khenaidoofffcc8a2019-03-13 11:54:41 -0400150 da.deviceMapLock.RLock()
151 defer da.deviceMapLock.RUnlock()
152 if val, exist := da.deviceMap[id]; exist {
khenaidoo1ce37ad2019-03-24 22:07:24 -0400153 return true, val.owned
khenaidoofffcc8a2019-03-13 11:54:41 -0400154 }
khenaidoo1ce37ad2019-03-24 22:07:24 -0400155 return false, false
khenaidoofffcc8a2019-03-13 11:54:41 -0400156}
157
158func (da *DeviceOwnership) setOwnership(id string, owner bool) error {
159 da.deviceMapLock.Lock()
160 defer da.deviceMapLock.Unlock()
161 if _, exist := da.deviceMap[id]; exist {
162 if da.deviceMap[id].owned != owner {
163 log.Debugw("ownership-changed", log.Fields{"Id": id, "owner": owner})
164 }
165 da.deviceMap[id].owned = owner
166 return nil
167 }
168 return status.Error(codes.NotFound, fmt.Sprintf("id-inexistent-%s", id))
169}
170
171// OwnedByMe returns where this Core instance active owns this device. This function will automatically
172// trigger the process to monitor the device and update the device ownership regularly.
khenaidoo1ce37ad2019-03-24 22:07:24 -0400173func (da *DeviceOwnership) OwnedByMe(id interface{}) bool {
174 // Retrieve the ownership key based on the id
175 var ownershipKey string
176 var err error
177 if ownershipKey, err = da.getOwnershipKey(id); err != nil {
178 log.Warnw("no-ownershipkey", log.Fields{"error": err})
179 return false
180 }
181
182 deviceOwned, ownedByMe := da.getOwnership(ownershipKey)
183 if deviceOwned {
184 return ownedByMe
185 }
186 // Not owned by me or maybe anybody else. Try to reserve it
187 reservedByMe := da.tryToReserveKey(ownershipKey)
188 myChnl := make(chan int)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400189
190 da.deviceMapLock.Lock()
khenaidoo1ce37ad2019-03-24 22:07:24 -0400191 da.deviceMap[ownershipKey] = &ownership{id: ownershipKey, owned: reservedByMe, chnl: myChnl}
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400192 da.deviceMapLock.Unlock()
193
khenaidoo1ce37ad2019-03-24 22:07:24 -0400194 log.Debugw("set-new-ownership", log.Fields{"Id": ownershipKey, "owned": reservedByMe})
195 go da.MonitorOwnership(ownershipKey, myChnl)
196 return reservedByMe
khenaidoofffcc8a2019-03-13 11:54:41 -0400197}
198
199//AbandonDevice must be invoked whenever a device is deleted from the Core
200func (da *DeviceOwnership) AbandonDevice(id string) error {
201 da.deviceMapLock.Lock()
202 defer da.deviceMapLock.Unlock()
khenaidoo0a822f92019-05-08 15:15:57 -0400203 if o, exist := da.deviceMap[id]; exist { // id is ownership key
204 // Need to clean up all deviceToKeyMap entries using this device as key
205 da.deviceToKeyMapLock.Lock()
206 defer da.deviceToKeyMapLock.Unlock()
207 for k, v := range da.deviceToKeyMap {
208 if id == v {
209 delete(da.deviceToKeyMap, k)
210 }
211 }
212 // Remove the device reference from the devicMap
213 delete(da.deviceMap, id)
214
khenaidoofffcc8a2019-03-13 11:54:41 -0400215 // Stop the Go routine monitoring the device
216 close(o.chnl)
217 delete(da.deviceMap, id)
218 log.Debugw("abandoning-device", log.Fields{"Id": id})
219 return nil
khenaidoo0a822f92019-05-08 15:15:57 -0400220 } else { // id is not ownership key
221 if err := da.deleteDeviceKey(id); err != nil {
222 log.Errorw("failed-deleting-key", log.Fields{"id": id})
223 }
khenaidoofffcc8a2019-03-13 11:54:41 -0400224 }
khenaidoo0a822f92019-05-08 15:15:57 -0400225 return nil
Richard Jankowski199fd862019-03-18 14:49:51 -0400226}
khenaidoo1ce37ad2019-03-24 22:07:24 -0400227
228//abandonAllDevices must be invoked whenever a device is deleted from the Core
229func (da *DeviceOwnership) abandonAllDevices() {
230 da.deviceMapLock.Lock()
231 defer da.deviceMapLock.Unlock()
khenaidoo0a822f92019-05-08 15:15:57 -0400232 da.deviceToKeyMapLock.Lock()
233 defer da.deviceToKeyMapLock.Unlock()
234 for k, _ := range da.deviceToKeyMap {
235 delete(da.deviceToKeyMap, k)
236 }
khenaidoo1ce37ad2019-03-24 22:07:24 -0400237 for _, val := range da.deviceMap {
238 close(val.chnl)
239 }
240}
241
242func (da *DeviceOwnership) getDeviceKey(id string) (string, error) {
243 da.deviceToKeyMapLock.RLock()
244 defer da.deviceToKeyMapLock.RUnlock()
245 if val, exist := da.deviceToKeyMap[id]; exist {
246 return val, nil
247 }
248 return "", status.Error(codes.NotFound, fmt.Sprintf("not-present-%s", id))
249}
250
251func (da *DeviceOwnership) updateDeviceKey(id string, key string) error {
252 da.deviceToKeyMapLock.Lock()
253 defer da.deviceToKeyMapLock.Unlock()
254 if _, exist := da.deviceToKeyMap[id]; exist {
255 return status.Error(codes.AlreadyExists, fmt.Sprintf("already-present-%s", id))
256 }
257 da.deviceToKeyMap[id] = key
258 return nil
259}
260
khenaidoo0a822f92019-05-08 15:15:57 -0400261func (da *DeviceOwnership) deleteDeviceKey(id string) error {
262 da.deviceToKeyMapLock.Lock()
263 defer da.deviceToKeyMapLock.Unlock()
264 if _, exist := da.deviceToKeyMap[id]; exist {
265 delete(da.deviceToKeyMap, id)
266 return nil
267 }
268 log.Warnw("device-not-owned", log.Fields{"deviceId": id})
269 return nil
270}
271
khenaidoo1ce37ad2019-03-24 22:07:24 -0400272func (da *DeviceOwnership) getOwnershipKey(id interface{}) (string, error) {
273 if id == nil {
274 return "", status.Error(codes.InvalidArgument, "nil-id")
275 }
khenaidoo8f474192019-04-03 17:20:44 -0400276 da.deviceToKeyMapLock.Lock()
277 defer da.deviceToKeyMapLock.Unlock()
khenaidoo1ce37ad2019-03-24 22:07:24 -0400278 var device *voltha.Device
279 var lDevice *voltha.LogicalDevice
280 // The id can either be a device Id or a logical device id.
281 if dId, ok := id.(*utils.DeviceID); ok {
282 // Use cache if present
khenaidoo8f474192019-04-03 17:20:44 -0400283 if val, exist := da.deviceToKeyMap[dId.Id]; exist {
khenaidoo1ce37ad2019-03-24 22:07:24 -0400284 return val, nil
285 }
286 if device, _ = da.deviceMgr.GetDevice(dId.Id); device == nil {
287 return "", status.Error(codes.NotFound, fmt.Sprintf("id-absent-%s", dId))
288 }
289 if device.Root {
khenaidoo8f474192019-04-03 17:20:44 -0400290 da.deviceToKeyMap[dId.Id] = device.Id
khenaidoo1ce37ad2019-03-24 22:07:24 -0400291 } else {
khenaidoo8f474192019-04-03 17:20:44 -0400292 da.deviceToKeyMap[dId.Id] = device.ParentId
khenaidoo1ce37ad2019-03-24 22:07:24 -0400293 }
khenaidoo8f474192019-04-03 17:20:44 -0400294 return da.deviceToKeyMap[dId.Id], nil
khenaidoo1ce37ad2019-03-24 22:07:24 -0400295 } else if ldId, ok := id.(*utils.LogicalDeviceID); ok {
296 // Use cache if present
khenaidoo8f474192019-04-03 17:20:44 -0400297 if val, exist := da.deviceToKeyMap[ldId.Id]; exist {
khenaidoo1ce37ad2019-03-24 22:07:24 -0400298 return val, nil
299 }
300 if lDevice, _ = da.logicalDeviceMgr.getLogicalDevice(ldId.Id); lDevice == nil {
301 return "", status.Error(codes.NotFound, fmt.Sprintf("id-absent-%s", ldId))
302 }
khenaidoo8f474192019-04-03 17:20:44 -0400303 da.deviceToKeyMap[ldId.Id] = lDevice.RootDeviceId
khenaidoo1ce37ad2019-03-24 22:07:24 -0400304 return lDevice.RootDeviceId, nil
305 }
306 return "", status.Error(codes.NotFound, fmt.Sprintf("id-%s", id))
307}