Use blocking get call w/ timeout to read msg queue
[ric-plt/xapp-frame-py.git] / ricxappframe / xapp_sdl.py
1 # ==================================================================================
2 #       Copyright (c) 2020 Nokia
3 #       Copyright (c) 2020 AT&T Intellectual Property.
4 #
5 #   Licensed under the Apache License, Version 2.0 (the "License");
6 #   you may not use this file except in compliance with the License.
7 #   You may obtain a copy of the License at
8 #
9 #          http://www.apache.org/licenses/LICENSE-2.0
10 #
11 #   Unless required by applicable law or agreed to in writing, software
12 #   distributed under the License is distributed on an "AS IS" BASIS,
13 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 #   See the License for the specific language governing permissions and
15 #   limitations under the License.
16 # ==================================================================================
17
18 """
19 sdl functionality
20 """
21
22 import msgpack
23 from ricsdl.syncstorage import SyncStorage
24
25
26 class SDLWrapper:
27     """
28     This is a wrapper around the SDL Python interface.
29
30     We do not embed the below directly in the Xapp classes because
31     this SDL wrapper is useful for other python apps, for example A1
32     Mediator uses this verbatim. Therefore, we leave this here as a
33     seperate instantiable object so it can be used outside of xapps
34     too.  One could argue this get moved into *sdl itself*.
35
36     We currently use msgpack for binary (de)serialization:
37     https://msgpack.org/index.html
38     """
39
40     def __init__(self, use_fake_sdl=False):
41         """
42         init
43
44         Parameters
45         ----------
46         use_fake_sdl: bool
47             if this is True (default: False), then SDLs "fake dict
48             backend" is used, which is very useful for testing since
49             it allows you to use SDL without any SDL or Redis deployed at
50             all. This can be used while developing your xapp, and also
51             for monkeypatching during unit testing (e.g., the xapp
52             framework unit tests do this).
53         """
54         if use_fake_sdl:
55             self._sdl = SyncStorage(fake_db_backend="dict")
56         else:
57             self._sdl = SyncStorage()
58
59     def set(self, ns, key, value, usemsgpack=True):
60         """
61         sets a key
62
63         TODO: discuss whether usemsgpack should *default* to True or
64         False here. This seems like a usage statistic question (that we
65         don't have enough data for yet). Are more uses for an xapp to
66         write/read their own data, or will more xapps end up reading data
67         written by some other thing? I think it's too early to know
68         this. So we go with True as the very first user of this, a1, does
69         this. I'm open to changing this default to False later with
70         evidence.
71
72         Parameters
73         ----------
74         ns: string
75         the sdl namespace
76         key: string
77         the sdl key
78         value:
79         if usemsgpack is True, value can be anything serializable by msgpack
80         if usemsgpack is False, value must be bytes
81         usemsgpack: boolean (optional)
82         determines whether the value is serialized using msgpack
83         """
84         if usemsgpack:
85             self._sdl.set(ns, {key: msgpack.packb(value, use_bin_type=True)})
86         else:
87             self._sdl.set(ns, {key: value})
88
89     def get(self, ns, key, usemsgpack=True):
90         """
91         get a key
92
93         Parameters
94         ----------
95         ns: string
96            the sdl namespace
97         key: string
98             the sdl key
99         usemsgpack: boolean (optional)
100             if usemsgpack is True, the value is deserialized using msgpack
101             if usemsgpack is False, the value is returned as raw bytes
102
103         Returns
104         -------
105         None (if not exist) or see above; depends on usemsgpack
106         """
107         ret_dict = self._sdl.get(ns, {key})
108         if key in ret_dict:
109             if usemsgpack:
110                 return msgpack.unpackb(ret_dict[key], raw=False)
111             return ret_dict[key]
112
113         return None
114
115     def find_and_get(self, ns, prefix, usemsgpack=True):
116         """
117         get all k v pairs that start with prefix
118
119         Parameters
120         ----------
121         ns: string
122            the sdl namespace
123         key: string
124             the sdl key
125         prefix: string
126             the prefix
127         usemsgpack: boolean (optional)
128             if usemsgpack is True, the value returned is a dict where each value has been deserialized using msgpack
129             if usemsgpack is False, the value returned is as a dict mapping keys to raw bytes
130
131         Returns
132         -------
133         {} (if no keys match) or see above; depends on usemsgpack
134         """
135
136         # note: SDL "*" usage is inconsistent with real python regex, where it would be ".*"
137         ret_dict = self._sdl.find_and_get(ns, "{0}*".format(prefix))
138         if usemsgpack:
139             return {k: msgpack.unpackb(v, raw=False) for k, v in ret_dict.items()}
140         return ret_dict
141
142     def delete(self, ns, key):
143         """
144         delete a key
145
146         Parameters
147         ----------
148         ns: string
149            the sdl namespace
150         key: string
151             the sdl key
152         """
153         self._sdl.remove(ns, {key})
154
155     def healthcheck(self):
156         """
157         checks if the sdl connection is healthy
158
159         Returns
160         -------
161         bool
162         """
163         return self._sdl.is_active()