blob: f8710cc906dec244cefcef4eb33e72bbf5f47fad [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"
25 "github.com/opencord/voltha-lib-go/v4/pkg/log"
26 "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
61 if have, err := loader.dbProxy.Get(ctx, loader.deviceTransientState.deviceID, &deviceTransientState); err != nil || !have {
62 logger.Errorw(ctx, "failed-to-get-device-transient-state-from-cluster-data-proxy", log.Fields{"error": err})
63 return
64 }
65 loader.deviceTransientState.transientState = deviceTransientState.TransientState
66}
67
68// Lock acquires the lock for deviceTransientStateLoader, and returns a handle
69// which can be used to access it until it's unlocked.
70// This handle ensures that the deviceTransientState cannot be accessed if the lock is not held.
71// TODO: consider accepting a ctx and aborting the lock attempt on cancellation
72func (loader *Loader) Lock() *Handle {
73 loader.lock.Lock()
74 dataTransientState := loader.deviceTransientState
75 return &Handle{loader: loader, data: dataTransientState}
76}
77
78// Handle is allocated for each Lock() call, all modifications are made using it, and it is invalidated by Unlock()
79// This enforces correct Lock()-Usage()-Unlock() ordering.
80type Handle struct {
81 loader *Loader
82 data *data
83}
84
85// GetReadOnly returns device transient which MUST NOT be modified externally, but which is safe to keep indefinitely
86func (h *Handle) GetReadOnly() voltha.DeviceTransientState_Types {
87 return h.data.transientState
88}
89
90// Update updates device transient state in KV store
91// The provided device transient state must not be modified afterwards.
92func (h *Handle) Update(ctx context.Context, state voltha.DeviceTransientState_Types) error {
93 var tState voltha.DeviceTransientState
94 tState.TransientState = state
95 if err := h.loader.dbProxy.Set(ctx, fmt.Sprint(h.data.deviceID), &tState); err != nil {
96 return status.Errorf(codes.Internal, "failed-to-update-device-%v-transient-state: %s", h.data.deviceID, err)
97 }
98 h.data.transientState = state
99 return nil
100}
101
102// Delete deletes device transient state from KV store
103func (h *Handle) Delete(ctx context.Context) error {
104 if err := h.loader.dbProxy.Remove(ctx, fmt.Sprint(h.data.deviceID)); err != nil {
105 return status.Errorf(codes.Internal, "failed-to-delete-device-%v-transient-state: %s", h.data.deviceID, err)
106 }
107 return nil
108}
109
110// UnLock releases the lock on the device transient state.
111func (h *Handle) UnLock() {
112 defer h.loader.lock.Unlock()
113 h.data = nil
114}