9ccd99d4f75704d64a2f9eacea5f0d1b6e12e08c
[ric-plt/sdlpy.git] / ricsdl-package / ricsdl / syncstorage_abc.py
1 # Copyright (c) 2019 AT&T Intellectual Property.
2 # Copyright (c) 2018-2019 Nokia.
3 #
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
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
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.
15
16 #
17 # This source code is part of the near-RT RIC (RAN Intelligent Controller)
18 # platform project (RICP).
19 #
20
21
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 (
26     RejectedByBackend
27 )
28
29 __all__ = [
30     'SyncStorageAbc',
31     'SyncLockAbc'
32 ]
33
34
35 class SyncLockAbc(ABC):
36     """
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.
40
41     A lock instance is created per namespace and it is identified by its `name` within a
42     namespace.
43
44     A concrete implementation subclass 'SyncLock' derives from this abstract class.
45
46     Args:
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.
51
52     """
53     def __init__(self, ns: str, name: str, expiration: Union[int, float]) -> None:
54         super().__init__()
55         self._ns = ns
56         self._name = name
57         self._expiration = expiration
58
59     def __enter__(self, *args, **kwargs):
60         if self.acquire(*args, **kwargs):
61             return self
62         raise RejectedByBackend("Unable to acquire lock within the time specified")
63
64     def __exit__(self, exception_type, exception_value, traceback):
65         self.release()
66
67     def acquire(self, retry_interval: Union[int, float] = 0.1,
68                 retry_timeout: Union[int, float] = 10) -> bool:
69         """
70         Acquire a shared, distributed lock atomically.
71
72         A lock can be used as a mutual exclusion locking entry for a shared resources.
73
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.
78
79         Args:
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
84                                         numbers.
85
86         Returns:
87             bool: True for successful lock acquiring, false otherwise.
88
89         Raises:
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.
94         """
95         pass
96
97     def release(self) -> None:
98         """
99         Release a lock atomically.
100
101         Release the already acquired lock.
102
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.
105
106         Args:
107             None
108
109         Returns:
110             None
111
112         Raises:
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.
116         """
117         pass
118
119     def refresh(self) -> None:
120         """
121         Refresh the remaining validity time of the existing lock back to an initial value.
122
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.
125
126         Args:
127             None
128
129         Returns:
130             None
131
132         Raises:
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.
136         """
137         pass
138
139     def get_validity_time(self) -> Union[int, float]:
140         """
141         Get atomically the remaining validity time of the lock in seconds.
142
143         Return atomically time in seconds until the lock expires.
144
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.
147
148         Args:
149             None
150
151         Returns:
152             (int, float): Validity time of the lock in seconds.
153
154         Raises:
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.
158         """
159         pass
160
161
162 class SyncStorageAbc(ABC):
163     """
164     An abstract class providing synchronous access to Shared Data Layer (SDL) storage.
165
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.
169
170     A concrete implementation subclass 'SyncStorage' derives from this abstract class.
171     """
172
173     @abstractmethod
174     def close(self):
175         """
176         Close the connection to SDL storage.
177
178         Args:
179             None
180
181         Returns:
182             None
183
184         Raises:
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.
188         """
189         pass
190
191     @abstractmethod
192     def set(self, ns: str, data_map: Dict[str, bytes]) -> None:
193         """
194         Write data to SDL storage.
195
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.
201
202         Args:
203             ns (str): Namespace under which this operation is targeted.
204             data_map (dict of str: bytes): Data to be written.
205
206         Returns:
207             None
208
209         Raises:
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.
214         """
215         pass
216
217     @abstractmethod
218     def set_if(self, ns: str, key: str, old_data: bytes, new_data: bytes) -> bool:
219         """
220         Conditionally modify the value of a key if the current value in data storage matches the
221         user's last known value.
222
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.
227
228         Args:
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.
233
234         Returns:
235             bool: True for successful modification, false if the user's last known data did not
236                   match the current value in data storage.
237
238         Raises:
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.
243         """
244         pass
245
246     @abstractmethod
247     def set_if_not_exists(self, ns: str, key: str, data: bytes) -> bool:
248         """
249         Write data to SDL storage if key does not exist.
250
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
253         operation.
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.
258
259         Args:
260             ns (str): Namespace under which this operation is targeted.
261             key (str): Key to be set.
262             data (bytes): Data to be written.
263
264         Returns:
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.
267
268         Raises:
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.
273         """
274         pass
275
276     @abstractmethod
277     def get(self, ns: str, keys: Union[str, Set[str]]) -> Dict[str, bytes]:
278         """
279         Read data from SDL storage.
280
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.
286
287         Args:
288             ns (str): Namespace under which this operation is targeted.
289             keys (str or set of str): One or multiple keys to be read.
290
291         Returns:
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.
294
295         Raises:
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.
300         """
301         pass
302
303     @abstractmethod
304     def find_keys(self, ns: str, key_pattern: str) -> List[str]:
305         r"""
306         Find all keys matching search pattern under the namespace.
307
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
313                     Bat.
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.
318
319         If searched key itself contains a special character, use a backslash (\) character to
320         escape the special character to match it verbatim.
321
322         NOTE: `find_keys` function is not guaranteed to be atomic or isolated.
323
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.
328
329         Args:
330             ns (str): Namespace under which this operation is targeted.
331             key_pattern (str): Key search pattern.
332
333         Returns:
334             (list of str): A list of found keys.
335
336         Raises:
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.
341         """
342         pass
343
344     @abstractmethod
345     def find_and_get(self, ns: str, key_pattern: str) -> Dict[str, bytes]:
346         r"""
347         Find keys and get their respective data from SDL storage.
348
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
354                     Bat.
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.
359
360         If searched key itself contains a special character, use a backslash (\) character to
361         escape the special character to match it verbatim.
362
363         NOTE: `find_and_get` function is not guaranteed to be atomic or isolated.
364
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.
369
370         Args:
371             ns (str): Namespace under which this operation is targeted.
372             key_pattern (str): Key search pattern.
373
374         Returns:
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.
377
378         Raises:
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.
383         """
384         pass
385
386     @abstractmethod
387     def remove(self, ns: str, keys: Union[str, Set[str]]) -> None:
388         """
389         Remove data from SDL storage. Existing keys are removed.
390
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.
396
397         Args:
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.
400
401         Returns:
402             None
403
404         Raises:
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.
409         """
410         pass
411
412     @abstractmethod
413     def remove_if(self, ns: str, key: str, data: bytes) -> bool:
414         """
415         Conditionally remove data from SDL storage if the current data value matches the user's
416         last known value.
417
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.
422
423         Args:
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
427
428         Returns:
429             bool: True if successful removal, false if the user's last known data did not match the
430                   current value in data storage.
431
432         Raises:
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.
437         """
438         pass
439
440     @abstractmethod
441     def remove_all(self, ns: str) -> None:
442         """
443         Remove all keys under the namespace.
444
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.
451
452         Args:
453             ns (str): Namespace under which this operation is targeted.
454
455         Returns:
456             None
457
458         Raises:
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.
463         """
464         pass
465
466     @abstractmethod
467     def add_member(self, ns: str, group: str, members: Union[bytes, Set[bytes]]) -> None:
468         """
469         Add new members to a SDL group under the namespace.
470
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.
479
480         Args:
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.
484
485         Returns:
486             None
487
488         Raises:
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.
493         """
494         pass
495
496     @abstractmethod
497     def remove_member(self, ns: str, group: str, members: Union[bytes, Set[bytes]]) -> None:
498         """
499         Remove members from a SDL group.
500
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.
508
509         Args:
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.
513
514         Returns:
515             None
516
517         Raises:
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.
522         """
523         pass
524
525     @abstractmethod
526     def remove_group(self, ns: str, group: str) -> None:
527         """
528         Remove a SDL group along with its members.
529
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.
536
537         Args:
538             ns (str): Namespace under which this operation is targeted.
539             group (str): Group name to be removed.
540
541         Returns:
542             None
543
544         Raises:
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.
549         """
550         pass
551
552     @abstractmethod
553     def get_members(self, ns: str, group: str) -> Set[bytes]:
554         """
555         Get all the members of a SDL group.
556
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.
563
564         Args:
565             ns (str): Namespace under which this operation is targeted.
566             group (str): Group name of which members are to be returned.
567
568         Returns:
569             (set of bytes): A set of the members of the group.
570             None
571
572         Raises:
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.
577         """
578         pass
579
580     @abstractmethod
581     def is_member(self, ns: str, group: str, member: bytes) -> bool:
582         """
583         Validate if a given member is in the SDL group.
584
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.
591
592         Args:
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.
596
597         Returns:
598             bool: True if member was in the group, false otherwise.
599
600         Raises:
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.
605         """
606         pass
607
608     @abstractmethod
609     def group_size(self, ns: str, group: str) -> int:
610         """
611         Return the number of members in a group.
612
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.
619
620         Args:
621             ns (str): Namespace under which this operation is targeted.
622             group (str): Group name of which members count is queried.
623
624         Returns:
625             int: Number of members in a group.
626
627         Raises:
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.
632         """
633         pass
634
635     @abstractmethod
636     def get_lock_resource(self, ns: str, resource: str,
637                           expiration: Union[int, float]) -> SyncLockAbc:
638         """
639         Return a lock resource for SDL.
640
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.
649
650         Args:
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
654
655         Returns:
656             SyncLockAbc: Lock resource instance.
657
658         Raises:
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.
663         """
664         pass