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).
22 """The module provides synchronous Shared Data Layer (SDL) interface."""
23 from typing import (Dict, Set, List, Union)
24 from abc import ABC, abstractmethod
25 from ricsdl.exceptions import (
35 class SyncLockAbc(ABC):
37 An abstract synchronous Shared Data Layer (SDL) lock class providing a shared, distributed
38 locking mechanism, which can be utilized by clients to be able to operate with a shared
39 resource in a mutually exclusive way.
41 A lock instance is created per namespace and it is identified by its `name` within a
44 A concrete implementation subclass 'SyncLock' derives from this abstract class.
47 ns (str): Namespace under which this lock is targeted.
48 name (str): Lock name, identifies the lock key in SDL storage.
49 expiration (int, float): Lock expiration time after which the lock is removed if it hasn't
50 been released earlier by a 'release' method.
53 def __init__(self, ns: str, name: str, expiration: Union[int, float]) -> None:
57 self._expiration = expiration
59 def __enter__(self, *args, **kwargs):
60 if self.acquire(*args, **kwargs):
62 raise RejectedByBackend("Unable to acquire lock within the time specified")
64 def __exit__(self, exception_type, exception_value, traceback):
67 def acquire(self, retry_interval: Union[int, float] = 0.1,
68 retry_timeout: Union[int, float] = 10) -> bool:
70 Acquire a shared, distributed lock atomically.
72 A lock can be used as a mutual exclusion locking entry for a shared resources.
74 All the exceptions except SdlTypeError are derived from SdlException base class. Client
75 can catch only that exception if separate handling for different SDL error situations is
76 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
77 misuse of the SDL API.
80 retry_interval (int, float): Lock acquiring retry interval in seconds. Supports both
81 integer and float numbers.
82 retry_timeout (int, float): Lock acquiring timeout after which retries are stopped and
83 error status is returned. Supports both integer and float
87 bool: True for successful lock acquiring, false otherwise.
90 SdlTypeError: If function's argument is of an inappropriate type.
91 NotConnected: If SDL is not connected to the backend data storage.
92 RejectedByBackend: If backend data storage rejects the request.
93 BackendError: If the backend data storage fails to process the request.
97 def release(self) -> None:
99 Release a lock atomically.
101 Release the already acquired lock.
103 Exceptions thrown are all derived from SdlException base class. Client can catch only that
104 exception if separate handling for different SDL error situations is not needed.
113 NotConnected: If SDL is not connected to the backend data storage.
114 RejectedByBackend: If backend data storage rejects the request.
115 BackendError: If the backend data storage fails to process the request.
119 def refresh(self) -> None:
121 Refresh the remaining validity time of the existing lock back to an initial value.
123 Exceptions thrown are all derived from SdlException base class. Client can catch only that
124 exception if separate handling for different SDL error situations is not needed.
133 NotConnected: If SDL is not connected to the backend data storage.
134 RejectedByBackend: If backend data storage rejects the request.
135 BackendError: If the backend data storage fails to process the request.
139 def get_validity_time(self) -> Union[int, float]:
141 Get atomically the remaining validity time of the lock in seconds.
143 Return atomically time in seconds until the lock expires.
145 Exceptions thrown are all derived from SdlException base class. Client can catch only that
146 exception if separate handling for different SDL error situations is not needed.
152 (int, float): Validity time of the lock in seconds.
155 NotConnected: If SDL is not connected to the backend data storage.
156 RejectedByBackend: If backend data storage rejects the request.
157 BackendError: If the backend data storage fails to process the request.
162 class SyncStorageAbc(ABC):
164 An abstract class providing synchronous access to Shared Data Layer (SDL) storage.
166 This class provides synchronous access to all the namespaces in SDL storage.
167 Data can be written, read and removed based on keys known to clients. Keys are unique within
168 a namespace, namespace identifier is passed as a parameter to all the operations.
170 A concrete implementation subclass 'SyncStorage' derives from this abstract class.
176 Close the connection to SDL storage.
185 NotConnected: If SDL is not connected to the backend data storage.
186 RejectedByBackend: If backend data storage rejects the request.
187 BackendError: If the backend data storage fails to process the request.
192 def set(self, ns: str, data_map: Dict[str, bytes]) -> None:
194 Write data to SDL storage.
196 Writing is done atomically, i.e. either all succeeds, or all fails.
197 All the exceptions except SdlTypeError are derived from SdlException base class. Client
198 can catch only that exception if separate handling for different SDL error situations is
199 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
200 misuse of the SDL API.
203 ns (str): Namespace under which this operation is targeted.
204 data_map (dict of str: bytes): Data to be written.
210 SdlTypeError: If function's argument is of an inappropriate type.
211 NotConnected: If SDL is not connected to the backend data storage.
212 RejectedByBackend: If backend data storage rejects the request.
213 BackendError: If the backend data storage fails to process the request.
218 def set_if(self, ns: str, key: str, old_data: bytes, new_data: bytes) -> bool:
220 Conditionally modify the value of a key if the current value in data storage matches the
221 user's last known value.
223 All the exceptions except SdlTypeError are derived from SdlException base class. Client
224 can catch only that exception if separate handling for different SDL error situations is
225 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
226 misuse of the SDL API.
229 ns (str): Namespace under which this operation is targeted.
230 key (str): Key for which data modification will be executed.
231 old_data (bytes): Last known data.
232 new_data (bytes): Data to be written.
235 bool: True for successful modification, false if the user's last known data did not
236 match the current value in data storage.
239 SdlTypeError: If function's argument is of an inappropriate type.
240 NotConnected: If SDL is not connected to the backend data storage.
241 RejectedByBackend: If backend data storage rejects the request.
242 BackendError: If the backend data storage fails to process the request.
247 def set_if_not_exists(self, ns: str, key: str, data: bytes) -> bool:
249 Write data to SDL storage if key does not exist.
251 Conditionally set the value of a key. If key already exists, then its value is not
252 modified. Checking the key existence and potential set operation is done as a one atomic
254 All the exceptions except SdlTypeError are derived from SdlException base class. Client
255 can catch only that exception if separate handling for different SDL error situations is
256 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
257 misuse of the SDL API.
260 ns (str): Namespace under which this operation is targeted.
261 key (str): Key to be set.
262 data (bytes): Data to be written.
265 bool: True if key didn't exist yet and set operation was executed, false if key already
266 existed and thus its value was left untouched.
269 SdlTypeError: If function's argument is of an inappropriate type.
270 NotConnected: If SDL is not connected to the backend data storage.
271 RejectedByBackend: If backend data storage rejects the request.
272 BackendError: If the backend data storage fails to process the request.
277 def get(self, ns: str, keys: Union[str, Set[str]]) -> Dict[str, bytes]:
279 Read data from SDL storage.
281 Only those entries that are found will be returned.
282 All the exceptions except SdlTypeError are derived from SdlException base class. Client
283 can catch only that exception if separate handling for different SDL error situations is
284 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
285 misuse of the SDL API.
288 ns (str): Namespace under which this operation is targeted.
289 keys (str or set of str): One or multiple keys to be read.
292 (dict of str: bytes): A dictionary mapping of a key to the read data from the storage.
295 SdlTypeError: If function's argument is of an inappropriate type.
296 NotConnected: If SDL is not connected to the backend data storage.
297 RejectedByBackend: If backend data storage rejects the request.
298 BackendError: If the backend data storage fails to process the request.
303 def find_keys(self, ns: str, key_prefix: str) -> List[str]:
305 Find all keys matching search pattern under the namespace.
307 No prior knowledge about the keys in the given namespace exists, thus operation is not
308 guaranteed to be atomic or isolated.
309 All the exceptions except SdlTypeError are derived from SdlException base class. Client
310 can catch only that exception if separate handling for different SDL error situations is
311 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
312 misuse of the SDL API.
315 ns (str): Namespace under which this operation is targeted.
316 key_prefix (str): Only keys starting with given keyPrefix are returned. Passing empty
317 string as keyPrefix will return all the keys.
320 (list of str): A list of found keys.
323 SdlTypeError: If function's argument is of an inappropriate type.
324 NotConnected: If SDL is not connected to the backend data storage.
325 RejectedByBackend: If backend data storage rejects the request.
326 BackendError: If the backend data storage fails to process the request.
331 def find_and_get(self, ns: str, key_prefix: str, atomic: bool) -> Dict[str, bytes]:
333 Find keys and get their respective data from SDL storage.
335 Only those entries that are matching prefix will be returned.
336 NOTE: In atomic action, if the prefix produces huge number of matches, that can have
337 a severe impact on system performance, due to DB is blocked for long time.
338 All the exceptions except SdlTypeError are derived from SdlException base class. Client
339 can catch only that exception if separate handling for different SDL error situations is
340 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
341 misuse of the SDL API.
344 ns (str): Namespace under which this operation is targeted.
345 key_prefix (str): Only keys starting with given keyPrefix are returned. Passing empty
346 string as keyPrefix will return all the keys.
347 atomic (bool): True to find keys and get their respective data in one atomic operation,
348 false to find keys and get their respective data non-atomically.
351 (dict of str: bytes): A dictionary mapping of a key to the read data from the storage.
354 SdlTypeError: If function's argument is of an inappropriate type.
355 NotConnected: If SDL is not connected to the backend data storage.
356 RejectedByBackend: If backend data storage rejects the request.
357 BackendError: If the backend data storage fails to process the request.
362 def remove(self, ns: str, keys: Union[str, Set[str]]) -> None:
364 Remove data from SDL storage. Existing keys are removed.
366 Removing is done atomically, i.e. either all succeeds, or all fails.
367 All the exceptions except SdlTypeError are derived from SdlException base class. Client
368 can catch only that exception if separate handling for different SDL error situations is
369 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
370 misuse of the SDL API.
373 ns (str): Namespace under which this operation is targeted.
374 keys (str or set of str): One key or multiple keys, which data is to be removed.
380 SdlTypeError: If function's argument is of an inappropriate type.
381 NotConnected: If SDL is not connected to the backend data storage.
382 RejectedByBackend: If backend data storage rejects the request.
383 BackendError: If the backend data storage fails to process the request.
388 def remove_if(self, ns: str, key: str, data: bytes) -> bool:
390 Conditionally remove data from SDL storage if the current data value matches the user's
393 All the exceptions except SdlTypeError are derived from SdlException base class. Client
394 can catch only that exception if separate handling for different SDL error situations is
395 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
396 misuse of the SDL API.
399 ns (str): Namespace under which this operation is targeted.
400 key (str): Key, which data is to be removed.
401 data (bytes): Last known value of data
404 bool: True if successful removal, false if the user's last known data did not match the
405 current value in data storage.
408 SdlTypeError: If function's argument is of an inappropriate type.
409 NotConnected: If SDL is not connected to the backend data storage.
410 RejectedByBackend: If backend data storage rejects the request.
411 BackendError: If the backend data storage fails to process the request.
416 def remove_all(self, ns: str) -> None:
418 Remove all keys under the namespace.
420 No prior knowledge about the keys in the given namespace exists, thus operation is not
421 guaranteed to be atomic or isolated.
422 All the exceptions except SdlTypeError are derived from SdlException base class. Client
423 can catch only that exception if separate handling for different SDL error situations is
424 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
425 misuse of the SDL API.
428 ns (str): Namespace under which this operation is targeted.
434 SdlTypeError: If function's argument is of an inappropriate type.
435 NotConnected: If SDL is not connected to the backend data storage.
436 RejectedByBackend: If backend data storage rejects the request.
437 BackendError: If the backend data storage fails to process the request.
442 def add_member(self, ns: str, group: str, members: Union[bytes, Set[bytes]]) -> None:
444 Add new members to a SDL group under the namespace.
446 SDL groups are identified by their name, which is a key in storage. SDL groups are
447 unordered collections of members where each member is unique. If a member to be added is
448 already a member of the group, its addition is silently ignored. If the group does not
449 exist, it is created, and specified members are added to the group.
450 All the exceptions except SdlTypeError are derived from SdlException base class. Client
451 can catch only that exception if separate handling for different SDL error situations is
452 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
453 misuse of the SDL API.
456 ns (str): Namespace under which this operation is targeted.
457 group (str): Group name.
458 members (bytes or set of bytes): One or multiple members to be added.
464 SdlTypeError: If function's argument is of an inappropriate type.
465 NotConnected: If SDL is not connected to the backend data storage.
466 RejectedByBackend: If backend data storage rejects the request.
467 BackendError: If the backend data storage fails to process the request.
472 def remove_member(self, ns: str, group: str, members: Union[bytes, Set[bytes]]) -> None:
474 Remove members from a SDL group.
476 SDL groups are unordered collections of members where each member is unique. If a member to
477 be removed does not exist in the group, its removal is silently ignored. If a group does
478 not exist, it is treated as an empty group and hence members removal is silently ignored.
479 All the exceptions except SdlTypeError are derived from SdlException base class. Client
480 can catch only that exception if separate handling for different SDL error situations is
481 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
482 misuse of the SDL API.
485 ns (str): Namespace under which this operation is targeted.
486 group (str): Group name.
487 members (bytes or set of bytes): One or multiple members to be removed.
493 SdlTypeError: If function's argument is of an inappropriate type.
494 NotConnected: If SDL is not connected to the backend data storage.
495 RejectedByBackend: If backend data storage rejects the request.
496 BackendError: If the backend data storage fails to process the request.
501 def remove_group(self, ns: str, group: str) -> None:
503 Remove a SDL group along with its members.
505 SDL groups are unordered collections of members where each member is unique. If a group to
506 be removed does not exist, its removal is silently ignored.
507 All the exceptions except SdlTypeError are derived from SdlException base class. Client
508 can catch only that exception if separate handling for different SDL error situations is
509 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
510 misuse of the SDL API.
513 ns (str): Namespace under which this operation is targeted.
514 group (str): Group name to be removed.
520 SdlTypeError: If function's argument is of an inappropriate type.
521 NotConnected: If SDL is not connected to the backend data storage.
522 RejectedByBackend: If backend data storage rejects the request.
523 BackendError: If the backend data storage fails to process the request.
528 def get_members(self, ns: str, group: str) -> Set[bytes]:
530 Get all the members of a SDL group.
532 SDL groups are unordered collections of members where each member is unique. If the group
533 does not exist, empty set is returned.
534 All the exceptions except SdlTypeError are derived from SdlException base class. Client
535 can catch only that exception if separate handling for different SDL error situations is
536 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
537 misuse of the SDL API.
540 ns (str): Namespace under which this operation is targeted.
541 group (str): Group name of which members are to be returned.
544 (set of bytes): A set of the members of the group.
548 SdlTypeError: If function's argument is of an inappropriate type.
549 NotConnected: If SDL is not connected to the backend data storage.
550 RejectedByBackend: If backend data storage rejects the request.
551 BackendError: If the backend data storage fails to process the request.
556 def is_member(self, ns: str, group: str, member: bytes) -> bool:
558 Validate if a given member is in the SDL group.
560 SDL groups are unordered collections of members where each member is unique. If the group
561 does not exist, false is returned.
562 All the exceptions except SdlTypeError are derived from SdlException base class. Client
563 can catch only that exception if separate handling for different SDL error situations is
564 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
565 misuse of the SDL API.
568 ns (str): Namespace under which this operation is targeted.
569 group (str): Group name of which member existence is to be validated.
570 member (bytes): A member, which existence is to be validated.
573 bool: True if member was in the group, false otherwise.
576 SdlTypeError: If function's argument is of an inappropriate type.
577 NotConnected: If SDL is not connected to the backend data storage.
578 RejectedByBackend: If backend data storage rejects the request.
579 BackendError: If the backend data storage fails to process the request.
584 def group_size(self, ns: str, group: str) -> int:
586 Return the number of members in a group.
588 SDL groups are unordered collections of members where each member is unique. If the group
589 does not exist, value 0 is returned.
590 All the exceptions except SdlTypeError are derived from SdlException base class. Client
591 can catch only that exception if separate handling for different SDL error situations is
592 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
593 misuse of the SDL API.
596 ns (str): Namespace under which this operation is targeted.
597 group (str): Group name of which members count is queried.
600 int: Number of members in a group.
603 SdlTypeError: If function's argument is of an inappropriate type.
604 NotConnected: If SDL is not connected to the backend data storage.
605 RejectedByBackend: If backend data storage rejects the request.
606 BackendError: If the backend data storage fails to process the request.
611 def get_lock_resource(self, ns: str, resource: str,
612 expiration: Union[int, float]) -> SyncLockAbc:
614 Return a lock resource for SDL.
616 A lock resource instance is created per namespace and it is identified by its `name` within
617 a namespace. A `get_lock_resource` returns a lock resource instance, it does not acquire
618 a lock. Lock resource provides lock handling methods such as acquiring a lock, extend
619 expiration time and releasing a lock.
620 All the exceptions except SdlTypeError are derived from SdlException base class. Client
621 can catch only that exception if separate handling for different SDL error situations is
622 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
623 misuse of the SDL API.
626 ns (str): Namespace under which this operation is targeted.
627 resource (str): Resource is used within namespace as a key for a lock entry in SDL.
628 expiration (int, float): Expiration time of a lock
631 SyncLockAbc: Lock resource instance.
634 SdlTypeError: If function's argument is of an inappropriate type.
635 NotConnected: If SDL is not connected to the backend data storage.
636 RejectedByBackend: If backend data storage rejects the request.
637 BackendError: If the backend data storage fails to process the request.