1 # Copyright (c) 2019 AT&T Intellectual Property.
2 # Copyright (c) 2018-2019 Nokia.
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
8 # http://www.apache.org/licenses/LICENSE-2.0
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.
17 # This source code is part of the near-RT RIC (RAN Intelligent Controller)
18 # platform project (RICP).
23 Examples how to use synchronous API functions of the Shared Data Layer (SDL).
24 Execution of these examples requires:
25 * Following Redis extension commands have been installed to runtime environment:
33 Redis v4.0 or greater is required. Older versions do not support extension modules.
34 Implementation of above commands is produced by RIC DBaaS:
35 https://gerrit.o-ran-sc.org/r/admin/repos/ric-plt/dbaas
36 In official RIC deployments these commands are installed by `dbaas` service to Redis
38 In development environment you may want install commands manually to pod/container, which is
40 * Following environment variables are needed to set to the pod/container where the application
41 utilizing SDL is going to be run.
42 DBAAS_SERVICE_HOST = [DB service address]
43 DBAAS_SERVICE_PORT= [DB service port]
44 DBAAS_MASTER_NAME = [DB name]. Needed to set only if Redis sentinel is used to provide high
45 availability for Redis DB solution.
46 DBAAS_SERVICE_SENTINEL_PORT = [Redis sentinel port number]. Needed to set only if Redis
48 DBASS_CLUSTER_ADDR_LIST = [list of DB service addresses]. Is set only if more than one
49 Redis sentinel groups are in use.
50 In official RIC deployments four first environment variables are defined in Helm configMaps
51 of the DBaaS and these configurations can be loaded automatically as environment variables
52 into application pods via `envFrom dbaas-appconfig` statement in an application Helm Charts.
53 The last environment variable is not for time being in use in official RIC deployments, only
54 in Nokia SEP deployments.
56 from ricsdl.syncstorage import SyncStorage
57 from ricsdl.exceptions import RejectedByBackend, NotConnected, BackendError
60 # Constants used in the examples below.
62 MY_GRP_NS = 'my_group_ns'
63 MY_LOCK_NS = 'my_group_ns'
66 def _try_func_return(func):
68 Generic wrapper function to call SDL API function and handle exceptions if they are raised.
72 except RejectedByBackend as exp:
73 print(f'SDL function {func.__name__} failed: {str(exp)}')
74 # Permanent failure, just forward the exception
76 except (NotConnected, BackendError) as exp:
77 print(f'SDL function {func.__name__} failed for a temporal error: {str(exp)}')
78 # Here we could have a retry logic
81 # Creates SDL instance. The call creates connection to the SDL database backend.
82 mysdl = _try_func_return(SyncStorage)
84 # Creates SDL instance what utilizes a fake database backend. Fake database is meant to
85 # be used only at development phase of SDL clients. It does not provide more advanced
87 # mysdl = _try_func_return(lambda: SyncStorage(fake_db_backend='dict'))
89 # Checks if SDL is operational. Note that it is not necessary to call `is_active()` after each
90 # SDL instance creation. Below example is here just to show how to call it spontaneously
91 # when SDL healthiness is needed to check.
92 is_active = mysdl.is_active()
93 assert is_active is True
95 # Sets a value 'my_value' for a key 'my_key' under given namespace. Note that value
96 # type must be bytes and multiple key values can be set in one set function call.
97 _try_func_return(lambda: mysdl.set(MY_NS, {'my_key': b'my_value'}))
100 # Gets the value of 'my_value' under given namespace.
101 # Note that the type of returned value is bytes.
102 my_ret_dict = _try_func_return(lambda: mysdl.get(MY_NS, {'my_key', 'someting not existing'}))
103 for key, val in my_ret_dict.items():
104 assert val.decode("utf-8") == u'my_value'
107 # Sets a value 'my_value2' for a key 'my_key' under given namespace only if the old value is
109 # Note that value types must be bytes.
110 was_set = _try_func_return(lambda: mysdl.set_if(MY_NS, 'my_key', b'my_value', b'my_value2'))
111 assert was_set is True
112 # Try again. This time value 'my_value2' won't be set, because the key has already 'my_value2'
114 was_set = _try_func_return(lambda: mysdl.set_if(MY_NS, 'my_key', b'my_value', b'my_value2'))
115 assert was_set is False
118 # Sets a value 'my_value' for a key 'my_key2' under given namespace only if the key doesn't exist.
119 # Note that value types must be bytes.
120 was_set = _try_func_return(lambda: mysdl.set_if_not_exists(MY_NS, 'my_key2', b'my_value'))
121 assert was_set is True
122 # Try again. This time the key 'my_key2' already exists.
123 was_set = _try_func_return(lambda: mysdl.set_if_not_exists(MY_NS, 'my_key2', b'my_value'))
124 assert was_set is False
127 # Removes a key 'my_key' under given namespace.
128 _try_func_return(lambda: mysdl.remove(MY_NS, 'my_key'))
129 my_ret_dict = _try_func_return(lambda: mysdl.get(MY_NS, 'my_key'))
130 assert my_ret_dict == {}
133 # Removes a key 'my_key' under given namespace only if the old value is 'my_value'.
134 was_removed = _try_func_return(lambda: mysdl.remove_if(MY_NS, 'my_key2', b'my_value'))
135 assert was_removed is True
136 # Try again to remove not anymore existing key 'my_key'.
137 was_removed = _try_func_return(lambda: mysdl.remove_if(MY_NS, 'my_key2', b'my_value'))
138 assert was_removed is False
141 # Removes all the keys under given namespace.
142 _try_func_return(lambda: mysdl.set(MY_NS, {'my_key': b'something'}))
143 my_ret_dict = _try_func_return(lambda: mysdl.get(MY_NS, {'my_key'}))
144 assert my_ret_dict != {}
146 _try_func_return(lambda: mysdl.remove_all(MY_NS))
147 my_ret_dict = _try_func_return(lambda: mysdl.get(MY_NS, {'my_key'}))
148 assert my_ret_dict == {}
151 # Finds keys under given namespace that are matching to given key prefix 'my_k'.
152 _try_func_return(lambda: mysdl.set(MY_NS, {'my_key': b'my_value'}))
153 ret_keys = _try_func_return(lambda: mysdl.find_keys(MY_NS, 'my_k*'))
154 assert ret_keys == ['my_key']
157 # Finds keys and their values under given namespace that are matching to given key search
159 # Note that the type of returned value is bytes.
160 ret_key_values = _try_func_return(lambda: mysdl.find_and_get(MY_NS, 'my_k*'))
161 assert ret_key_values == {'my_key': b'my_value'}
163 _try_func_return(lambda: mysdl.remove_all(MY_NS))
166 # Adds a member 'a' to a group 'my_group' under given namespace. A group is a unique collection of
168 # Note that member type must be bytes and multiple members can be set in one set function call.
169 _try_func_return(lambda: mysdl.add_member(MY_GRP_NS, 'my_group', {b'a'}))
170 # Try again to add a member 'a'. This time 'a' won't be added, because 'a' belongs already to
172 _try_func_return(lambda: mysdl.add_member(MY_GRP_NS, 'my_group', {b'a'}))
175 # Gets group 'my_group' members under given namespace.
176 # Note that the type of returned member is bytes.
177 ret_members = _try_func_return(lambda: mysdl.get_members(MY_GRP_NS, 'my_group'))
178 assert ret_members == {b'a'}
181 # Checks if 'a' is a member of the group 'my_group' under given namespace.
182 was_member = _try_func_return(lambda: mysdl.is_member(MY_GRP_NS, 'my_group', b'a'))
183 assert was_member is True
184 was_member = _try_func_return(lambda: mysdl.is_member(MY_GRP_NS, 'my_group', b'not a member'))
185 assert was_member is False
188 # Returns the count of members of a group 'my_group' under given namespace.
189 ret_count = _try_func_return(lambda: mysdl.group_size(MY_GRP_NS, 'my_group'))
190 assert ret_count == 1
193 # Removes the member 'a' of the group 'my_group' under given namespace.
194 _try_func_return(lambda: mysdl.remove_member(MY_GRP_NS, 'my_group', {b'a', b'not exists'}))
195 ret_count = _try_func_return(lambda: mysdl.group_size(MY_GRP_NS, 'my_group'))
196 assert ret_count == 0
199 # Removes the group 'my_group' under given namespace.
200 _try_func_return(lambda: mysdl.add_member(MY_GRP_NS, 'my_group', {b'a', b'b', b'c'}))
201 ret_count = _try_func_return(lambda: mysdl.group_size(MY_GRP_NS, 'my_group'))
202 assert ret_count == 3
204 _try_func_return(lambda: mysdl.remove_group(MY_GRP_NS, 'my_group'))
205 ret_count = _try_func_return(lambda: mysdl.group_size(MY_GRP_NS, 'my_group'))
206 ret_members = _try_func_return(lambda: mysdl.get_members(MY_GRP_NS, 'my_group'))
207 assert ret_count == 0
208 assert ret_members == set()
211 # Gets a lock 'my_lock' resource under given namespace.
212 # Note that this function does not take a lock, you need to call 'acquire' function to take
213 # the lock to yourself.
214 my_lock = _try_func_return(lambda: mysdl.get_lock_resource(MY_LOCK_NS, "my_lock", expiration=5.5))
215 assert my_lock is not None
218 # Acquires a lock from the lock resource. Return True if lock was taken within given retry limits.
219 was_acquired = _try_func_return(lambda: my_lock.acquire(retry_interval=0.5, retry_timeout=2))
220 assert was_acquired is True
221 # Try again. This time a lock won't be acquired successfully, because we have a lock already.
222 was_acquired = _try_func_return(lambda: my_lock.acquire(retry_interval=0.1, retry_timeout=0.2))
223 assert was_acquired is False
226 # Refreshs the remaining validity time of the existing lock back to the initial value.
227 _try_func_return(my_lock.refresh)
230 # Gets the remaining validity time of the lock.
231 ret_time = _try_func_return(my_lock.get_validity_time)
236 _try_func_return(my_lock.release)
239 # Locking example what utilizes python 'with' statement with SDL lock.
240 # The lock is released automatically when we are out of the scope of
241 # 'the with my_lock' statement.
242 my_lock = _try_func_return(lambda: mysdl.get_lock_resource(MY_LOCK_NS, "my_lock", 2.5))
244 # Just an example how to use lock API
245 time_left = _try_func_return(my_lock.get_validity_time)
247 # Add here operations what needs to be done under a lock, for example some
248 # operations with a shared resources what needs to be done in a mutually
251 # Lock is not anymore hold here
254 # Closes the SDL connection.