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.
293 Dictionary is sorted by key values in alphabetical order.
296 SdlTypeError: If function's argument is of an inappropriate type.
297 NotConnected: If SDL is not connected to the backend data storage.
298 RejectedByBackend: If backend data storage rejects the request.
299 BackendError: If the backend data storage fails to process the request.
304 def find_keys(self, ns: str, key_pattern: str) -> List[str]:
306 Find all keys matching search pattern under the namespace.
308 Supported glob-style patterns:
309 `?` matches any single character. For example `?at` matches Cat, cat, Bat or bat.
310 `*` matches any number of any characters including none. For example `*Law*` matches
311 Law, GrokLaw, or Lawyer.
312 `[abc]` matches one character given in the bracket. For example `[CB]at` matches Cat or
314 `[a-z]` matches one character from the range given in the bracket. For example
315 `Letter[0-9]` matches Letter0 up to Letter9.
316 `[^abc]` matches any single character what is not given in the bracket. For example
317 `h[^e]llo` matches hallo, hillo but not hello.
319 If searched key itself contains a special character, use a backslash (\) character to
320 escape the special character to match it verbatim.
322 NOTE: `find_keys` function is not guaranteed to be atomic or isolated.
324 All the exceptions except SdlTypeError are derived from SdlException base class. Client
325 can catch only that exception if separate handling for different SDL error situations is
326 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
327 misuse of the SDL API.
330 ns (str): Namespace under which this operation is targeted.
331 key_pattern (str): Key search pattern.
334 (list of str): A list of found keys.
337 SdlTypeError: If function's argument is of an inappropriate type.
338 NotConnected: If SDL is not connected to the backend data storage.
339 RejectedByBackend: If backend data storage rejects the request.
340 BackendError: If the backend data storage fails to process the request.
345 def find_and_get(self, ns: str, key_pattern: str) -> Dict[str, bytes]:
347 Find keys and get their respective data from SDL storage.
349 Supported glob-style patterns:
350 `?` matches any single character. For example `?at` matches Cat, cat, Bat or bat.
351 `*` matches any number of any characters including none. For example `*Law*` matches
352 Law, GrokLaw, or Lawyer.
353 `[abc]` matches one character given in the bracket. For example `[CB]at` matches Cat or
355 `[a-z]` matches one character from the range given in the bracket. For example
356 `Letter[0-9]` matches Letter0 up to Letter9.
357 `[^abc]` matches any single character what is not given in the bracket. For example
358 `h[^e]llo` matches hallo, hillo but not hello.
360 If searched key itself contains a special character, use a backslash (\) character to
361 escape the special character to match it verbatim.
363 NOTE: `find_and_get` function is not guaranteed to be atomic or isolated.
365 All the exceptions except SdlTypeError are derived from SdlException base class. Client
366 can catch only that exception if separate handling for different SDL error situations is
367 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
368 misuse of the SDL API.
371 ns (str): Namespace under which this operation is targeted.
372 key_pattern (str): Key search pattern.
375 (dict of str: bytes): A dictionary mapping of a key to the read data from the storage.
376 Dictionary is sorted by key values in alphabetical order.
379 SdlTypeError: If function's argument is of an inappropriate type.
380 NotConnected: If SDL is not connected to the backend data storage.
381 RejectedByBackend: If backend data storage rejects the request.
382 BackendError: If the backend data storage fails to process the request.
387 def remove(self, ns: str, keys: Union[str, Set[str]]) -> None:
389 Remove data from SDL storage. Existing keys are removed.
391 Removing is done atomically, i.e. either all succeeds, or all fails.
392 All the exceptions except SdlTypeError are derived from SdlException base class. Client
393 can catch only that exception if separate handling for different SDL error situations is
394 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
395 misuse of the SDL API.
398 ns (str): Namespace under which this operation is targeted.
399 keys (str or set of str): One key or multiple keys, which data is to be removed.
405 SdlTypeError: If function's argument is of an inappropriate type.
406 NotConnected: If SDL is not connected to the backend data storage.
407 RejectedByBackend: If backend data storage rejects the request.
408 BackendError: If the backend data storage fails to process the request.
413 def remove_if(self, ns: str, key: str, data: bytes) -> bool:
415 Conditionally remove data from SDL storage if the current data value matches the user's
418 All the exceptions except SdlTypeError are derived from SdlException base class. Client
419 can catch only that exception if separate handling for different SDL error situations is
420 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
421 misuse of the SDL API.
424 ns (str): Namespace under which this operation is targeted.
425 key (str): Key, which data is to be removed.
426 data (bytes): Last known value of data
429 bool: True if successful removal, false if the user's last known data did not match the
430 current value in data storage.
433 SdlTypeError: If function's argument is of an inappropriate type.
434 NotConnected: If SDL is not connected to the backend data storage.
435 RejectedByBackend: If backend data storage rejects the request.
436 BackendError: If the backend data storage fails to process the request.
441 def remove_all(self, ns: str) -> None:
443 Remove all keys under the namespace.
445 No prior knowledge about the keys in the given namespace exists, thus operation is not
446 guaranteed to be atomic or isolated.
447 All the exceptions except SdlTypeError are derived from SdlException base class. Client
448 can catch only that exception if separate handling for different SDL error situations is
449 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
450 misuse of the SDL API.
453 ns (str): Namespace under which this operation is targeted.
459 SdlTypeError: If function's argument is of an inappropriate type.
460 NotConnected: If SDL is not connected to the backend data storage.
461 RejectedByBackend: If backend data storage rejects the request.
462 BackendError: If the backend data storage fails to process the request.
467 def add_member(self, ns: str, group: str, members: Union[bytes, Set[bytes]]) -> None:
469 Add new members to a SDL group under the namespace.
471 SDL groups are identified by their name, which is a key in storage. SDL groups are
472 unordered collections of members where each member is unique. If a member to be added is
473 already a member of the group, its addition is silently ignored. If the group does not
474 exist, it is created, and specified members are added to the group.
475 All the exceptions except SdlTypeError are derived from SdlException base class. Client
476 can catch only that exception if separate handling for different SDL error situations is
477 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
478 misuse of the SDL API.
481 ns (str): Namespace under which this operation is targeted.
482 group (str): Group name.
483 members (bytes or set of bytes): One or multiple members to be added.
489 SdlTypeError: If function's argument is of an inappropriate type.
490 NotConnected: If SDL is not connected to the backend data storage.
491 RejectedByBackend: If backend data storage rejects the request.
492 BackendError: If the backend data storage fails to process the request.
497 def remove_member(self, ns: str, group: str, members: Union[bytes, Set[bytes]]) -> None:
499 Remove members from a SDL group.
501 SDL groups are unordered collections of members where each member is unique. If a member to
502 be removed does not exist in the group, its removal is silently ignored. If a group does
503 not exist, it is treated as an empty group and hence members removal is silently ignored.
504 All the exceptions except SdlTypeError are derived from SdlException base class. Client
505 can catch only that exception if separate handling for different SDL error situations is
506 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
507 misuse of the SDL API.
510 ns (str): Namespace under which this operation is targeted.
511 group (str): Group name.
512 members (bytes or set of bytes): One or multiple members to be removed.
518 SdlTypeError: If function's argument is of an inappropriate type.
519 NotConnected: If SDL is not connected to the backend data storage.
520 RejectedByBackend: If backend data storage rejects the request.
521 BackendError: If the backend data storage fails to process the request.
526 def remove_group(self, ns: str, group: str) -> None:
528 Remove a SDL group along with its members.
530 SDL groups are unordered collections of members where each member is unique. If a group to
531 be removed does not exist, its removal is silently ignored.
532 All the exceptions except SdlTypeError are derived from SdlException base class. Client
533 can catch only that exception if separate handling for different SDL error situations is
534 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
535 misuse of the SDL API.
538 ns (str): Namespace under which this operation is targeted.
539 group (str): Group name to be removed.
545 SdlTypeError: If function's argument is of an inappropriate type.
546 NotConnected: If SDL is not connected to the backend data storage.
547 RejectedByBackend: If backend data storage rejects the request.
548 BackendError: If the backend data storage fails to process the request.
553 def get_members(self, ns: str, group: str) -> Set[bytes]:
555 Get all the members of a SDL group.
557 SDL groups are unordered collections of members where each member is unique. If the group
558 does not exist, empty set is returned.
559 All the exceptions except SdlTypeError are derived from SdlException base class. Client
560 can catch only that exception if separate handling for different SDL error situations is
561 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
562 misuse of the SDL API.
565 ns (str): Namespace under which this operation is targeted.
566 group (str): Group name of which members are to be returned.
569 (set of bytes): A set of the members of the group.
573 SdlTypeError: If function's argument is of an inappropriate type.
574 NotConnected: If SDL is not connected to the backend data storage.
575 RejectedByBackend: If backend data storage rejects the request.
576 BackendError: If the backend data storage fails to process the request.
581 def is_member(self, ns: str, group: str, member: bytes) -> bool:
583 Validate if a given member is in the SDL group.
585 SDL groups are unordered collections of members where each member is unique. If the group
586 does not exist, false is returned.
587 All the exceptions except SdlTypeError are derived from SdlException base class. Client
588 can catch only that exception if separate handling for different SDL error situations is
589 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
590 misuse of the SDL API.
593 ns (str): Namespace under which this operation is targeted.
594 group (str): Group name of which member existence is to be validated.
595 member (bytes): A member, which existence is to be validated.
598 bool: True if member was in the group, false otherwise.
601 SdlTypeError: If function's argument is of an inappropriate type.
602 NotConnected: If SDL is not connected to the backend data storage.
603 RejectedByBackend: If backend data storage rejects the request.
604 BackendError: If the backend data storage fails to process the request.
609 def group_size(self, ns: str, group: str) -> int:
611 Return the number of members in a group.
613 SDL groups are unordered collections of members where each member is unique. If the group
614 does not exist, value 0 is returned.
615 All the exceptions except SdlTypeError are derived from SdlException base class. Client
616 can catch only that exception if separate handling for different SDL error situations is
617 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
618 misuse of the SDL API.
621 ns (str): Namespace under which this operation is targeted.
622 group (str): Group name of which members count is queried.
625 int: Number of members in a group.
628 SdlTypeError: If function's argument is of an inappropriate type.
629 NotConnected: If SDL is not connected to the backend data storage.
630 RejectedByBackend: If backend data storage rejects the request.
631 BackendError: If the backend data storage fails to process the request.
636 def get_lock_resource(self, ns: str, resource: str,
637 expiration: Union[int, float]) -> SyncLockAbc:
639 Return a lock resource for SDL.
641 A lock resource instance is created per namespace and it is identified by its `name` within
642 a namespace. A `get_lock_resource` returns a lock resource instance, it does not acquire
643 a lock. Lock resource provides lock handling methods such as acquiring a lock, extend
644 expiration time and releasing a lock.
645 All the exceptions except SdlTypeError are derived from SdlException base class. Client
646 can catch only that exception if separate handling for different SDL error situations is
647 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
648 misuse of the SDL API.
651 ns (str): Namespace under which this operation is targeted.
652 resource (str): Resource is used within namespace as a key for a lock entry in SDL.
653 expiration (int, float): Expiration time of a lock
656 SyncLockAbc: Lock resource instance.
659 SdlTypeError: If function's argument is of an inappropriate type.
660 NotConnected: If SDL is not connected to the backend data storage.
661 RejectedByBackend: If backend data storage rejects the request.
662 BackendError: If the backend data storage fails to process the request.