blob: 9a95bd900ae6a3407c5833615fe1532940ca6d8b [file] [log] [blame]
Himani Chawla2ba1c9c2020-10-07 13:19:03 +05301/*
2 * Copyright 2020-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 */
16
17package transientstate
18
19import (
20 "context"
21 "fmt"
22 "sync"
23
24 "github.com/opencord/voltha-go/db/model"
yasin sapli5458a1c2021-06-14 22:24:38 +000025 "github.com/opencord/voltha-lib-go/v5/pkg/log"
Himani Chawla2ba1c9c2020-10-07 13:19:03 +053026 "github.com/opencord/voltha-protos/v4/go/voltha"
27 "google.golang.org/grpc/codes"
28 "google.golang.org/grpc/status"
29)
30
31// Loader hides all low-level locking & synchronization related to device transient state updates
32type Loader struct {
33 dbProxy *model.Proxy
34 // this lock protects the device transient state
35 lock sync.RWMutex
36 deviceTransientState *data
37}
38
39type data struct {
40 transientState voltha.DeviceTransientState_Types
41 deviceID string
42}
43
44func NewLoader(dbProxy *model.Proxy, deviceID string) *Loader {
45 return &Loader{
46 dbProxy: dbProxy,
47 deviceTransientState: &data{
48 transientState: voltha.DeviceTransientState_NONE,
49 deviceID: deviceID,
50 },
51 }
52}
53
54// Load queries existing transient state from the kv,
55// and should only be called once when first created.
56func (loader *Loader) Load(ctx context.Context) {
57 loader.lock.Lock()
58 defer loader.lock.Unlock()
59
60 var deviceTransientState voltha.DeviceTransientState
khenaidoo7585a962021-06-10 16:15:38 -040061 have, err := loader.dbProxy.Get(ctx, loader.deviceTransientState.deviceID, &deviceTransientState)
62 if err != nil {
Himani Chawla2ba1c9c2020-10-07 13:19:03 +053063 logger.Errorw(ctx, "failed-to-get-device-transient-state-from-cluster-data-proxy", log.Fields{"error": err})
64 return
65 }
khenaidoo7585a962021-06-10 16:15:38 -040066 if have {
67 loader.deviceTransientState.transientState = deviceTransientState.TransientState
68 return
69 }
70 loader.deviceTransientState.transientState = voltha.DeviceTransientState_NONE
Himani Chawla2ba1c9c2020-10-07 13:19:03 +053071}
72
73// Lock acquires the lock for deviceTransientStateLoader, and returns a handle
74// which can be used to access it until it's unlocked.
75// This handle ensures that the deviceTransientState cannot be accessed if the lock is not held.
76// TODO: consider accepting a ctx and aborting the lock attempt on cancellation
77func (loader *Loader) Lock() *Handle {
78 loader.lock.Lock()
79 dataTransientState := loader.deviceTransientState
80 return &Handle{loader: loader, data: dataTransientState}
81}
82
83// Handle is allocated for each Lock() call, all modifications are made using it, and it is invalidated by Unlock()
84// This enforces correct Lock()-Usage()-Unlock() ordering.
85type Handle struct {
86 loader *Loader
87 data *data
88}
89
90// GetReadOnly returns device transient which MUST NOT be modified externally, but which is safe to keep indefinitely
91func (h *Handle) GetReadOnly() voltha.DeviceTransientState_Types {
92 return h.data.transientState
93}
94
95// Update updates device transient state in KV store
96// The provided device transient state must not be modified afterwards.
97func (h *Handle) Update(ctx context.Context, state voltha.DeviceTransientState_Types) error {
98 var tState voltha.DeviceTransientState
99 tState.TransientState = state
100 if err := h.loader.dbProxy.Set(ctx, fmt.Sprint(h.data.deviceID), &tState); err != nil {
101 return status.Errorf(codes.Internal, "failed-to-update-device-%v-transient-state: %s", h.data.deviceID, err)
102 }
103 h.data.transientState = state
104 return nil
105}
106
107// Delete deletes device transient state from KV store
108func (h *Handle) Delete(ctx context.Context) error {
109 if err := h.loader.dbProxy.Remove(ctx, fmt.Sprint(h.data.deviceID)); err != nil {
110 return status.Errorf(codes.Internal, "failed-to-delete-device-%v-transient-state: %s", h.data.deviceID, err)
111 }
112 return nil
113}
114
115// UnLock releases the lock on the device transient state.
116func (h *Handle) UnLock() {
117 defer h.loader.lock.Unlock()
118 h.data = nil
119}