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