Initial commit
[ta/config-manager.git] / hostcli / src / cmcli / cm.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
15 import logging
16 import json
17 import os
18 import re
19
20 from cmdatahandlers.api.configmanager import ConfigManager
21 from cmframework.apis import cmmanage
22
23 from cliff import command
24 from cliff.show import ShowOne
25 from cliff.lister import Lister
26 from cliff.formatters.table import TableFormatter
27
28
29
30 class VerboseLogger(object):
31     def __init__(self, logger):
32         self.logger = logger
33
34     def __call__(self, msg):
35         self.logger.debug(msg)
36
37
38 def _add_basic_arguments(parser):
39     parser.add_argument('--cmserver-ip',
40                         dest='cmserverip',
41                         metavar='IP',
42                         required=False,
43                         default='config-manager',
44                         type=str,
45                         action='store')
46     parser.add_argument('--cmserver-port',
47                         dest='cmserverport',
48                         metavar='PORT',
49                         required=False,
50                         default=61100,
51                         type=str,
52                         action='store')
53     parser.add_argument('--client-lib',
54                         dest='clientlib',
55                         metavar='LIB',
56                         required=False,
57                         default='cmframework.lib.CMClientImpl',
58                         type=str,
59                         action='store')
60
61
62 class ShowProperty(ShowOne):
63     """A command for showing the value associated with a property"""
64
65     log = logging.getLogger(__name__)
66
67     def get_parser(self, prog_name):
68         parser = super(ShowProperty, self).get_parser(prog_name)
69         _add_basic_arguments(parser)
70         parser.add_argument(
71             'property',
72             metavar='<property>',
73             help=('Property name'),
74         )
75
76         return parser
77
78     @staticmethod
79     def dumps(data):
80         return json.dumps(data, sort_keys=True, indent=4, separators=(',', ':'))
81
82     def take_action(self, parsed_args):
83         try:
84             logger = VerboseLogger(self.log)
85
86             api = cmmanage.CMManage(parsed_args.cmserverip,
87                                     parsed_args.cmserverport,
88                                     parsed_args.clientlib,
89                                     logger)
90
91             cm_property = parsed_args.property
92
93             data = api.get_properties('')
94
95             d = {}
96             for name, value in data.iteritems():
97                 try:
98                     d[name] = json.loads(value)
99                 except Exception as ex:
100                     d[name] = value
101
102             cm = ConfigManager(d)
103             cm.mask_sensitive_data()
104
105             prop = d.get(cm_property)
106             split = None
107             if not prop:
108                 split = cm_property.split('.')
109                 cm_property = '.'.join(split[:2])
110                 prop = d.get(cm_property)
111
112             if prop == '' or prop == None:
113                 raise Exception('{} not configured'.format(cm_property))
114
115             d = d[cm_property]
116             if split:
117                 if split[2:]:
118                     for field in split[2:]:
119                         try:
120                             d = d[field]
121                         except KeyError as ex:
122                             raise Exception('{} not found in {}'.format(field, cm_property))
123
124             if isinstance(d, dict):
125                 columns = tuple(d.keys())
126                 if isinstance(self.formatter, TableFormatter):
127                     data = tuple(map(ShowProperty.dumps, d.values()))
128                 else:
129                     data = tuple(d.values())
130             else:
131                 columns = (parsed_args.property, )
132                 data = (d, )
133             return (columns, data)
134
135         except Exception:
136             raise
137
138
139 class ListProperties(Lister):
140     """A command for showing properties matching some filter"""
141
142     log = logging.getLogger(__name__)
143
144     def get_parser(self, prog_name):
145         parser = super(ListProperties, self).get_parser(prog_name)
146         _add_basic_arguments(parser)
147         parser.add_argument('--matching-filter',
148                             dest='filter',
149                             metavar='FILTER',
150                             required=False,
151                             default='.*',
152                             type=str,
153                             action='store')
154
155         return parser
156
157     def take_action(self, parsed_args):
158         try:
159             logger = VerboseLogger(self.log)
160
161             api = cmmanage.CMManage(parsed_args.cmserverip,
162                                     parsed_args.cmserverport,
163                                     parsed_args.clientlib,
164                                     logger)
165
166             prop_filter = parsed_args.filter
167             data = api.get_properties('')
168
169             header = ('property', 'value')
170
171             columns = ()
172
173             d = {}
174             for name, value in data.iteritems():
175                 try:
176                     d[name] = json.loads(value)
177                 except Exception as ex:
178                     d[name] = value
179
180             cm = ConfigManager(d)
181             cm.mask_sensitive_data()
182
183             pattern = re.compile(prop_filter)
184             for name, value in d.iteritems():
185                 if not pattern.match(name):
186                     continue
187                 if isinstance(self.formatter, TableFormatter):
188                     try:
189                         v = json.dumps(value, sort_keys=True, indent=1, separators=(',', ':'))
190                     except Exception:
191                         pass
192                 else:
193                     v = value
194                 entry = (name, v)
195                 columns = (entry,) + columns
196             if not columns:
197                 raise Exception('Not found')
198
199             return (header, columns)
200
201         except Exception:
202             raise
203
204
205 class DeleteProperty(command.Command):
206     """A command for deleting a property"""
207
208     log = logging.getLogger(__name__)
209
210     def get_parser(self, prog_name):
211         parser = super(DeleteProperty, self).get_parser(prog_name)
212         _add_basic_arguments(parser)
213
214         parser.add_argument(
215             'property',
216             metavar='<property>',
217             help=('Property name'),
218         )
219
220         return parser
221
222     def take_action(self, parsed_args):
223         try:
224             logger = VerboseLogger(self.log)
225
226             api = cmmanage.CMManage(parsed_args.cmserverip,
227                                     parsed_args.cmserverport,
228                                     parsed_args.clientlib,
229                                     logger)
230
231
232             api.delete_property(parsed_args.property)
233
234
235             self.app.stdout.write('%s deleted successfully\n' % parsed_args.property)
236
237         except Exception as exp:
238             self.app.stderr.write('Failed with error %s\n' % str(exp))
239
240
241 class SetProperty(command.Command):
242     """A command for setting a property"""
243
244     log = logging.getLogger(__name__)
245
246     def get_parser(self, prog_name):
247         parser = super(SetProperty, self).get_parser(prog_name)
248         _add_basic_arguments(parser)
249
250         parser.add_argument(
251             'property',
252             metavar='<property>',
253             help=('Property name'),
254         )
255
256         parser.add_argument('--value',
257                             dest='value',
258                             metavar='VALUE',
259                             required=False,
260                             type=str,
261                             action='store')
262
263         parser.add_argument('--file',
264                             dest='file',
265                             metavar='FILE',
266                             required=False,
267                             type=str,
268                             action='store')
269         return parser
270
271     def take_action(self, parsed_args):
272         try:
273             logger = VerboseLogger(self.log)
274
275             api = cmmanage.CMManage(parsed_args.cmserverip,
276                                     parsed_args.cmserverport,
277                                     parsed_args.clientlib,
278                                     logger)
279
280             if parsed_args.value and parsed_args.file:
281                 raise Exception('Either --value or --file needs to be specified')
282
283             if parsed_args.value:
284                 api.set_property(parsed_args.property, parsed_args.value)
285             elif parsed_args.file:
286                 if not os.path.exists(parsed_args.file):
287                     raise Exception('File %s is not valid' % parsed_args.file)
288
289                 with open(parsed_args.file, 'r') as f:
290                     data = ""
291                     try:
292                         data = f.read()
293                         d = json.loads(data)
294                         data = json.dumps(d)
295                     except Exception as exp:
296                         pass
297
298                     api.set_property(parsed_args.property, data)
299             else:
300                 raise Exception('--value or --file needs to be specified')
301
302
303             self.app.stdout.write('%s set successfully\n' % parsed_args.property)
304
305         except Exception as exp:
306             self.app.stderr.write('Failed with error %s\n' % str(exp))
307
308
309 class DumpPropertyToFile(command.Command):
310     """A command for dumping property value to a file"""
311
312     log = logging.getLogger(__name__)
313
314     def get_parser(self, prog_name):
315         parser = super(DumpPropertyToFile, self).get_parser(prog_name)
316         _add_basic_arguments(parser)
317
318         parser.add_argument(
319             'property',
320             metavar='<property>',
321             help=('Property name'),
322         )
323
324         parser.add_argument(
325             'file',
326             metavar='<FILE>',
327             help=('Filename'),
328         )
329
330         return parser
331
332     def take_action(self, parsed_args):
333         try:
334             logger = VerboseLogger(self.log)
335
336             api = cmmanage.CMManage(parsed_args.cmserverip,
337                                     parsed_args.cmserverport,
338                                     parsed_args.clientlib,
339                                     logger)
340
341             cm_property = parsed_args.property
342             config = api.get_properties('')
343
344             if os.path.exists(parsed_args.file):
345                 raise Exception('File %s already exists' % parsed_args.file)
346
347             with open(parsed_args.file, 'w') as f:
348                 d = {}
349                 if cm_property not in config.keys():
350                     raise Exception('Property not found')
351                 for name, value in config.iteritems():
352                     try:
353                         d[name] = json.loads(value)
354                     except Exception as ex:
355                         d[name] = value
356                 cm = ConfigManager(d)
357                 cm.mask_sensitive_data()
358                 d = d.get(cm_property)
359                 try:
360                     data = json.dumps(d, sort_keys=True, indent=1, separators=(',', ':'))
361                 except Exception as exp:
362                     data = d
363                 if data == 'null' or data == '""':
364                     data = ''
365                 f.write(data)
366
367             self.app.stdout.write('Completed successfully\n')
368
369         except Exception as exp:
370             self.app.stderr.write('Failed with error %s\n' % str(exp))