blob: 997ebe45b2a1bdf5ed49f4d65601656e7a830aec [file] [log] [blame]
khenaidoobf6e7bb2018-08-14 22:27:29 -04001/*
2 * Copyright 2018-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 */
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 Hagerman4f355f52020-03-30 16:01:33 -040023 "github.com/gogo/protobuf/proto"
24 "github.com/opencord/voltha-lib-go/v3/pkg/db"
serkant.uluderya2ae470f2020-01-21 11:13:09 -080025 "github.com/opencord/voltha-lib-go/v3/pkg/log"
Kent Hagerman4f355f52020-03-30 16:01:33 -040026 "reflect"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040027)
28
Kent Hagerman4f355f52020-03-30 16:01:33 -040029// RequestTimestamp attribute used to store a timestamp in the context object
30const RequestTimestamp contextKey = "request-timestamp"
Stephane Barbarieec0919b2018-09-05 14:14:29 -040031
Kent Hagerman4f355f52020-03-30 16:01:33 -040032type contextKey string
Stephane Barbarieec0919b2018-09-05 14:14:29 -040033
Stephane Barbariedc5022d2018-11-19 15:21:44 -050034// Proxy holds the information for a specific location with the data model
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040035type Proxy struct {
Kent Hagerman4f355f52020-03-30 16:01:33 -040036 kvStore *db.Backend
37 path string
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040038}
39
Stephane Barbariedc5022d2018-11-19 15:21:44 -050040// NewProxy instantiates a new proxy to a specific location
Kent Hagerman4f355f52020-03-30 16:01:33 -040041func NewProxy(kvStore *db.Backend, path string) *Proxy {
Stephane Barbarieaa467942019-02-06 14:09:44 -050042 if path == "/" {
Kent Hagerman4f355f52020-03-30 16:01:33 -040043 path = ""
Stephane Barbarieaa467942019-02-06 14:09:44 -050044 }
Kent Hagerman4f355f52020-03-30 16:01:33 -040045 return &Proxy{
46 kvStore: kvStore,
47 path: path,
48 }
49}
Stephane Barbarieaa467942019-02-06 14:09:44 -050050
Kent Hagerman4f355f52020-03-30 16:01:33 -040051// List will retrieve information from the data model at the specified path location, and write it to the target slice
52// target must be a type of the form *[]<proto.Message Type> For example: *[]*voltha.Device
53func (p *Proxy) List(ctx context.Context, path string, target interface{}) error {
54 completePath := p.path + path
Stephane Barbarieef6650d2019-07-18 12:15:09 -040055
Girish Kumarf56a4682020-03-20 20:07:46 +000056 logger.Debugw("proxy-list", log.Fields{
Kent Hagerman4f355f52020-03-30 16:01:33 -040057 "path": completePath,
Stephane Barbarie7512fc82019-05-07 12:25:46 -040058 })
Stephane Barbarieaa467942019-02-06 14:09:44 -050059
Kent Hagerman4f355f52020-03-30 16:01:33 -040060 // verify type of target is *[]*<type>
61 pointerType := reflect.TypeOf(target) // *[]*<type>
62 if pointerType.Kind() != reflect.Ptr {
63 return errors.New("target is not of type *[]*<type>")
64 }
65 sliceType := pointerType.Elem() // []*type
66 if sliceType.Kind() != reflect.Slice {
67 return errors.New("target is not of type *[]*<type>")
68 }
69 elemType := sliceType.Elem() // *type
70 if sliceType.Implements(reflect.TypeOf((*proto.Message)(nil)).Elem()) {
71 return errors.New("target slice does not contain elements of type proto.Message")
72 }
73 dataType := elemType.Elem() // type
74
75 blobs, err := p.kvStore.List(ctx, completePath)
76 if err != nil {
77 return fmt.Errorf("failed to retrieve %s from kvstore: %s", path, err)
Stephane Barbariea188d942018-10-16 16:43:04 -040078 }
79
Kent Hagerman4f355f52020-03-30 16:01:33 -040080 logger.Debugw("parsing-data-blobs", log.Fields{
81 "path": path,
82 "size": len(blobs),
Stephane Barbarie7512fc82019-05-07 12:25:46 -040083 })
Stephane Barbariea188d942018-10-16 16:43:04 -040084
Kent Hagerman4f355f52020-03-30 16:01:33 -040085 ret := reflect.MakeSlice(sliceType, len(blobs), len(blobs))
86 i := 0
87 for _, blob := range blobs {
88 data := reflect.New(dataType)
89 if err := proto.Unmarshal(blob.Value.([]byte), data.Interface().(proto.Message)); err != nil {
90 return fmt.Errorf("failed to unmarshal %s: %s", blob.Key, err)
91 }
92 ret.Index(i).Set(data)
93 i++
94 }
95 reflect.ValueOf(target).Elem().Set(ret)
96 return nil
97}
98
99// Get will retrieve information from the data model at the specified path location, and write it to target
100func (p *Proxy) Get(ctx context.Context, path string, target proto.Message) (bool, error) {
101 completePath := p.path + path
102
103 logger.Debugw("proxy-get", log.Fields{
104 "path": completePath,
105 })
106
107 blob, err := p.kvStore.Get(ctx, completePath)
108 if err != nil {
109 return false, fmt.Errorf("failed to retrieve %s from kvstore: %s", path, err)
110 } else if blob == nil {
111 return false, nil // this blob does not exist
112 }
113
114 logger.Debugw("parsing-data-blobs", log.Fields{
115 "path": path,
116 })
117
118 if err := proto.Unmarshal(blob.Value.([]byte), target); err != nil {
119 return false, fmt.Errorf("failed to unmarshal %s: %s", blob.Key, err)
120 }
121 return true, nil
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400122}
123
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500124// Update will modify information in the data model at the specified location with the provided data
Kent Hagerman4f355f52020-03-30 16:01:33 -0400125func (p *Proxy) Update(ctx context.Context, path string, data proto.Message) error {
126 return p.add(ctx, path, data)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400127}
128
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500129// AddWithID will insert new data at specified location.
Kent Hagerman4f355f52020-03-30 16:01:33 -0400130// This method also allows the user to specify the ID.
131func (p *Proxy) AddWithID(ctx context.Context, path string, id string, data proto.Message) error {
132 return p.add(ctx, path+"/"+id, data)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500133}
134
Kent Hagerman4f355f52020-03-30 16:01:33 -0400135// add will insert new data at specified location.
136func (p *Proxy) add(ctx context.Context, path string, data proto.Message) error {
137 completePath := p.path + path
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400138
Girish Kumarf56a4682020-03-20 20:07:46 +0000139 logger.Debugw("proxy-add", log.Fields{
Kent Hagerman4f355f52020-03-30 16:01:33 -0400140 "path": completePath,
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400141 })
Stephane Barbariea188d942018-10-16 16:43:04 -0400142
Kent Hagerman4f355f52020-03-30 16:01:33 -0400143 blob, err := proto.Marshal(data)
144 if err != nil {
145 return fmt.Errorf("unable to save to kvStore, error marshalling: %s", err)
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400146 }
Stephane Barbariec53a2752019-03-08 17:50:10 -0500147
Kent Hagerman4f355f52020-03-30 16:01:33 -0400148 if err := p.kvStore.Put(ctx, completePath, blob); err != nil {
149 return fmt.Errorf("unable to write to kvStore: %s", err)
150 }
151 return nil
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400152}
153
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500154// Remove will delete an entry at the specified location
Kent Hagerman4f355f52020-03-30 16:01:33 -0400155func (p *Proxy) Remove(ctx context.Context, path string) error {
156 completePath := p.path + path
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400157
Girish Kumarf56a4682020-03-20 20:07:46 +0000158 logger.Debugw("proxy-remove", log.Fields{
Kent Hagerman4f355f52020-03-30 16:01:33 -0400159 "path": completePath,
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400160 })
Stephane Barbariea188d942018-10-16 16:43:04 -0400161
Kent Hagerman4f355f52020-03-30 16:01:33 -0400162 if err := p.kvStore.Delete(ctx, completePath); err != nil {
163 return fmt.Errorf("unable to delete %s in kvStore: %s", completePath, err)
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400164 }
Kent Hagerman4f355f52020-03-30 16:01:33 -0400165 return nil
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400166}