blob: 4a6fddd9943c57a701b6a9b10162666bee952d1b [file] [log] [blame]
khenaidoobf6e7bb2018-08-14 22:27:29 -04001/*
Joey Armstrong7a9af442024-01-03 19:26:36 -05002 * Copyright 2018-2024 Open Networking Foundation (ONF) and the ONF Contributors
khenaidoobf6e7bb2018-08-14 22:27:29 -04003
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 */
Stephane Barbariedc5022d2018-11-19 15:21:44 -050016
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040017package model
18
19import (
Stephane Barbarieef6650d2019-07-18 12:15:09 -040020 "context"
Stephane Barbarie694e2b92018-09-07 12:17:36 -040021 "errors"
khenaidoob9203542018-09-17 22:56:37 -040022 "fmt"
Kent Hagermanf5a67352020-04-30 15:15:26 -040023 "reflect"
24 "strings"
25
khenaidood948f772021-08-11 17:49:24 -040026 "github.com/opencord/voltha-lib-go/v7/pkg/db"
27 "github.com/opencord/voltha-lib-go/v7/pkg/log"
bseenivab18867a2026-02-12 19:15:25 +053028 "google.golang.org/protobuf/proto"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040029)
30
Kent Hagerman4f355f52020-03-30 16:01:33 -040031// RequestTimestamp attribute used to store a timestamp in the context object
32const RequestTimestamp contextKey = "request-timestamp"
Stephane Barbarieec0919b2018-09-05 14:14:29 -040033
Kent Hagerman4f355f52020-03-30 16:01:33 -040034type contextKey string
Stephane Barbarieec0919b2018-09-05 14:14:29 -040035
Kent Hagermanf5a67352020-04-30 15:15:26 -040036// Path holds the information for a specific location within the data model
37type Path struct {
Kent Hagerman4f355f52020-03-30 16:01:33 -040038 kvStore *db.Backend
39 path string
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040040}
41
Kent Hagermanf5a67352020-04-30 15:15:26 -040042// NewDBPath returns a path to the default db location
43func NewDBPath(kvStore *db.Backend) *Path {
44 return &Path{kvStore: kvStore}
45}
46
47// SubPath returns a path which points to a more specific db location
48func (p *Path) SubPath(path string) *Path {
49 path = strings.TrimRight(strings.TrimLeft(path, "/"), "/")
50 return &Path{
51 kvStore: p.kvStore,
52 path: p.path + path + "/",
Kent Hagerman4f355f52020-03-30 16:01:33 -040053 }
54}
Stephane Barbarieaa467942019-02-06 14:09:44 -050055
Kent Hagermanf5a67352020-04-30 15:15:26 -040056// Proxy contains all the information needed to reference a specific resource within the kv
57type Proxy Path
Stephane Barbarieef6650d2019-07-18 12:15:09 -040058
Kent Hagermanf5a67352020-04-30 15:15:26 -040059// Proxy returns a new proxy which references the specified resource
60func (p *Path) Proxy(resource string) *Proxy {
61 resource = strings.TrimRight(strings.TrimLeft(resource, "/"), "/")
62 return &Proxy{
63 kvStore: p.kvStore,
64 path: p.path + resource + "/",
65 }
66}
67
68// List will retrieve information from the data model at the proxy's path location, and write it to the target slice
69// target must be a type of the form *[]<proto.Message Type> For example: *[]*voltha.Device
akashreddykc8a64012026-02-06 17:05:44 +053070func (p *Proxy) KeyExists(ctx context.Context, id string) (bool, error) {
71 completePath := p.path + id
72
73 logger.Debugw(ctx, "proxy-key-exists", log.Fields{
74 "path": completePath,
75 })
76
77 keyExists, err := p.kvStore.KeyExists(ctx, completePath)
78 if err != nil {
79 return false, fmt.Errorf("failed to retrieve %s from kvstore: %s", p.path, err)
80 }
81 return keyExists, nil
82}
83
84// List will retrieve information from the data model at the proxy's path location, and write it to the target slice
85// target must be a type of the form *[]<proto.Message Type> For example: *[]*voltha.Device
Kent Hagermanf5a67352020-04-30 15:15:26 -040086func (p *Proxy) List(ctx context.Context, target interface{}) error {
Rohan Agrawal31f21802020-06-12 05:38:46 +000087 logger.Debugw(ctx, "proxy-list", log.Fields{
Kent Hagermanf5a67352020-04-30 15:15:26 -040088 "path": p.path,
Stephane Barbarie7512fc82019-05-07 12:25:46 -040089 })
Stephane Barbarieaa467942019-02-06 14:09:44 -050090
Kent Hagerman4f355f52020-03-30 16:01:33 -040091 // verify type of target is *[]*<type>
92 pointerType := reflect.TypeOf(target) // *[]*<type>
93 if pointerType.Kind() != reflect.Ptr {
94 return errors.New("target is not of type *[]*<type>")
95 }
96 sliceType := pointerType.Elem() // []*type
97 if sliceType.Kind() != reflect.Slice {
98 return errors.New("target is not of type *[]*<type>")
99 }
100 elemType := sliceType.Elem() // *type
101 if sliceType.Implements(reflect.TypeOf((*proto.Message)(nil)).Elem()) {
102 return errors.New("target slice does not contain elements of type proto.Message")
103 }
104 dataType := elemType.Elem() // type
105
Kent Hagermanf5a67352020-04-30 15:15:26 -0400106 blobs, err := p.kvStore.List(ctx, p.path)
Kent Hagerman4f355f52020-03-30 16:01:33 -0400107 if err != nil {
Kent Hagermanf5a67352020-04-30 15:15:26 -0400108 return fmt.Errorf("failed to retrieve %s from kvstore: %s", p.path, err)
Stephane Barbariea188d942018-10-16 16:43:04 -0400109 }
110
Rohan Agrawal31f21802020-06-12 05:38:46 +0000111 logger.Debugw(ctx, "parsing-data-blobs", log.Fields{
Kent Hagermanf5a67352020-04-30 15:15:26 -0400112 "path": p.path,
Kent Hagerman4f355f52020-03-30 16:01:33 -0400113 "size": len(blobs),
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400114 })
Stephane Barbariea188d942018-10-16 16:43:04 -0400115
Kent Hagerman4f355f52020-03-30 16:01:33 -0400116 ret := reflect.MakeSlice(sliceType, len(blobs), len(blobs))
117 i := 0
118 for _, blob := range blobs {
119 data := reflect.New(dataType)
120 if err := proto.Unmarshal(blob.Value.([]byte), data.Interface().(proto.Message)); err != nil {
121 return fmt.Errorf("failed to unmarshal %s: %s", blob.Key, err)
122 }
123 ret.Index(i).Set(data)
124 i++
125 }
126 reflect.ValueOf(target).Elem().Set(ret)
127 return nil
128}
129
Kent Hagermanf5a67352020-04-30 15:15:26 -0400130// Get will retrieve information from the data model at the proxy's path location, and write it to target
131func (p *Proxy) Get(ctx context.Context, id string, target proto.Message) (bool, error) {
132 completePath := p.path + id
Kent Hagerman4f355f52020-03-30 16:01:33 -0400133
Rohan Agrawal31f21802020-06-12 05:38:46 +0000134 logger.Debugw(ctx, "proxy-get", log.Fields{
Kent Hagerman4f355f52020-03-30 16:01:33 -0400135 "path": completePath,
136 })
137
138 blob, err := p.kvStore.Get(ctx, completePath)
139 if err != nil {
Kent Hagermanf5a67352020-04-30 15:15:26 -0400140 return false, fmt.Errorf("failed to retrieve %s from kvstore: %s", completePath, err)
Kent Hagerman4f355f52020-03-30 16:01:33 -0400141 } else if blob == nil {
142 return false, nil // this blob does not exist
143 }
144
Rohan Agrawal31f21802020-06-12 05:38:46 +0000145 logger.Debugw(ctx, "parsing-data-blobs", log.Fields{
Kent Hagermanf5a67352020-04-30 15:15:26 -0400146 "path": completePath,
Kent Hagerman4f355f52020-03-30 16:01:33 -0400147 })
148
149 if err := proto.Unmarshal(blob.Value.([]byte), target); err != nil {
150 return false, fmt.Errorf("failed to unmarshal %s: %s", blob.Key, err)
151 }
152 return true, nil
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400153}
154
Kent Hagermanf5a67352020-04-30 15:15:26 -0400155// Set will add new or update existing entry at the proxy's path location
156func (p *Proxy) Set(ctx context.Context, id string, data proto.Message) error {
157 completePath := p.path + id
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400158
Rohan Agrawal31f21802020-06-12 05:38:46 +0000159 logger.Debugw(ctx, "proxy-add", log.Fields{
Kent Hagerman4f355f52020-03-30 16:01:33 -0400160 "path": completePath,
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400161 })
Stephane Barbariea188d942018-10-16 16:43:04 -0400162
Kent Hagerman4f355f52020-03-30 16:01:33 -0400163 blob, err := proto.Marshal(data)
164 if err != nil {
Akash Reddy Kankanala929cc002025-04-08 15:05:21 +0530165 return fmt.Errorf("unable to save to kvStore, error marshaling: %s", err)
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400166 }
Stephane Barbariec53a2752019-03-08 17:50:10 -0500167
Kent Hagerman4f355f52020-03-30 16:01:33 -0400168 if err := p.kvStore.Put(ctx, completePath, blob); err != nil {
169 return fmt.Errorf("unable to write to kvStore: %s", err)
170 }
171 return nil
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400172}
173
Kent Hagermanf5a67352020-04-30 15:15:26 -0400174// Remove will delete an entry at the proxy's path location
175func (p *Proxy) Remove(ctx context.Context, id string) error {
176 completePath := p.path + id
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400177
Rohan Agrawal31f21802020-06-12 05:38:46 +0000178 logger.Debugw(ctx, "proxy-remove", log.Fields{
Kent Hagerman4f355f52020-03-30 16:01:33 -0400179 "path": completePath,
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400180 })
Stephane Barbariea188d942018-10-16 16:43:04 -0400181
Kent Hagerman4f355f52020-03-30 16:01:33 -0400182 if err := p.kvStore.Delete(ctx, completePath); err != nil {
183 return fmt.Errorf("unable to delete %s in kvStore: %s", completePath, err)
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400184 }
Kent Hagerman4f355f52020-03-30 16:01:33 -0400185 return nil
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400186}