Initial commit
[ta/config-manager.git] / cmframework / src / cmframework / apis / cmclient.py
1 # Copyright 2019 Nokia
2
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 import re
15
16 from functools import wraps
17 from cmframework import apis as cmapis
18
19
20 def handle_exceptions(func):
21     @wraps(func)
22     def wrapper(self, *arg, **kwargs):
23         try:
24             return func(self, *arg, **kwargs)
25         except cmapis.cmerror.CMError as exp:
26             raise
27         except Exception as exp:
28             raise cmapis.cmerror.CMError(str(exp))
29
30     return wrapper
31
32
33 class CMClient(object):
34     """
35         Usage Example:
36             class VerboseLogger:
37                 def __call__(self, msg):
38                     print(msg)
39
40             logger = VerboseLogger()
41
42             client = CMClient(192.128.254.10, 51110, cmclient.CMClientImpl, logger)
43             try:
44                 value = client.get_property('controller-1.ntp.servers')
45             except cmapis.cmerror.CMError as error:
46                 print('Got exception %s' % str(error))
47     """
48
49     @handle_exceptions
50     def __init__(self, server_ip='config-manager', server_port=61100,
51                  client_lib_impl_module='cmframework.lib.CMClientImpl', verbose_logger=None):
52         """ initialize the client interface
53
54             Arguments:
55
56             server_ip:  The configuration management server ip address.
57
58             server_port: The configuration management server port number.
59
60             client_lib_impl_module: The module implementing the client library.
61
62             verbose_logger: The verbose logging callable. any callable which
63                             takes a string as input argument can be used.
64
65             Raise:
66
67             CMError exception in-case of a failure.
68         """
69
70         import socket
71         try:
72             serverip = socket.gethostbyname(server_ip)
73         except Exception:  # pylint: disable=broad-except
74             # use localhost in-case we cannot resolve the provided hostname
75             serverip = '127.0.0.1'
76
77         self.server_ip = serverip
78         self.server_port = server_port
79         self.client_lib = None
80         self.verbose_logger = verbose_logger
81
82         # Separate class path and module name
83         parts = client_lib_impl_module.rsplit('.', 1)
84         module_path = parts[0]
85         class_name = parts[1]
86         self.verbose_log('module_path = %s' % module_path)
87         self.verbose_log('class_name = %s' % class_name)
88         module = __import__(module_path, fromlist=[module_path])
89         classobj = getattr(module, class_name)
90         self.client_lib = classobj(self.server_ip, self.server_port, verbose_logger)
91
92     def verbose_log(self, msg):
93         if self.verbose_logger:
94             self.verbose_logger(msg)
95
96     @handle_exceptions
97     def get_property(self, prop_name, snapshot_name=None):
98         """get the value assoicated with a property.
99
100            This is the API used to read the value associated with a
101            configuration property.
102
103            Arguments:
104
105            prop_name: The property name
106            (optional) snapshot_name: The snapshot name
107
108            Raise:
109
110            CMError in-case of a failure.
111         """
112         result = self.client_lib.get_property(prop_name, snapshot_name)
113         return result
114
115     @handle_exceptions
116     def get_properties(self, prop_filter, snapshot_name=None):
117         """get a set of properties matching a filter.
118
119            This is the API used to read a group of properties matching some
120            filter.
121
122            Arguments:
123
124            prop_filter: A valid python re describing the filter used when
125                         matching the returned properties.
126            (optional) snapshot_name: The snapshot name
127
128           Raise:
129
130           CMError is raised in-case of a failure.
131         """
132         self._check_filter(prop_filter)
133         result = self.client_lib.get_properties(prop_filter, snapshot_name)
134         return result
135
136     @handle_exceptions
137     def set_property(self, prop_name, prop_value):
138         """set/update the value of a property.
139
140             This is the API used to set/update the value associated with a
141             property.
142
143             Arguments:
144
145             prop_name: A string representing the property name.
146
147             prop_value: A string representing the property value.
148
149             Raise:
150
151             CMError is raised in-case of failure.
152         """
153         return self.client_lib.set_property(prop_name, prop_value)
154
155     @handle_exceptions
156     def set_properties(self, props, overwrite=False):
157         """set/update a group of properties as a whole
158
159            This API is used to set/update the values associated with a group of
160            properties as a whole, the change is either accepted as a whole or
161            rejected as a whole.
162
163            Arguments:
164
165            props: A dictionary containing the changed properties.
166
167            overwrite: Replace the existing configuration dictionary with the new one.
168
169            Raise:
170
171            CMError is raised in-case of a failure.
172         """
173         return self.client_lib.set_properties(props, overwrite)
174
175     @handle_exceptions
176     def delete_property(self, prop_name):
177         """delete a property
178
179            This is the API used to delete a configuration property.
180
181            Arguments:
182
183            prop_name: The name of the property to be deleted.
184
185            Raise:
186
187            CMError is raised in-case of a failure.
188         """
189         return self.client_lib.delete_property(prop_name)
190
191     @handle_exceptions
192     def delete_properties(self, arg):
193         """delete a group of properties as a whole
194
195            This is the API used to delete a group of properties as whole, if the
196            deletion of one of the properties is rejected then the whole delete
197            operation will fail.
198
199            Arguments:
200
201            arg: This can be either a string representing the re used when
202                 matching the properties to be deleted, or it can be a list of
203                 properties names to be deleted.
204
205             Raise:
206
207             CMError is raised in-case of a failure.
208         """
209         if isinstance(arg, str):
210             self._check_filter(arg)
211         return self.client_lib.delete_properties(arg)
212
213     @handle_exceptions
214     def get_changes_states(self, change_uuid):
215         """get the config changes states
216
217            This is the API used to get the changes states
218
219            Arguments:
220
221            arg: This can be either a valid change uuid or None.
222
223            Raise:
224
225             CMError is raised in-case of a failure.
226         """
227         return self.client_lib.get_changes_states(change_uuid)
228
229     @handle_exceptions
230     def wait_activation(self, change_uuid):
231         """wait for activation of config changes to finish
232
233            This is the API used to wait for config changes to finish
234
235            Arguments:
236
237            arg: A valid change uuid.
238
239            Raise:
240
241             CMError is raised in-case of a failure.
242         """
243         return self.client_lib.wait_activation(change_uuid)
244
245     # pylint: disable=no-self-use
246     def _check_filter(self, prop_filter):
247         re.compile(prop_filter)