opensipscli 0.3.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- opensipscli/__init__.py +20 -0
- opensipscli/args.py +56 -0
- opensipscli/cli.py +472 -0
- opensipscli/comm.py +57 -0
- opensipscli/config.py +162 -0
- opensipscli/db.py +989 -0
- opensipscli/defaults.py +91 -0
- opensipscli/libs/__init__.py +20 -0
- opensipscli/libs/sqlalchemy_utils.py +244 -0
- opensipscli/logger.py +85 -0
- opensipscli/main.py +86 -0
- opensipscli/module.py +69 -0
- opensipscli/modules/__init__.py +24 -0
- opensipscli/modules/database.py +1062 -0
- opensipscli/modules/diagnose.py +1089 -0
- opensipscli/modules/instance.py +53 -0
- opensipscli/modules/mi.py +200 -0
- opensipscli/modules/tls.py +354 -0
- opensipscli/modules/trace.py +292 -0
- opensipscli/modules/trap.py +138 -0
- opensipscli/modules/user.py +281 -0
- opensipscli/version.py +22 -0
- opensipscli-0.3.1.data/scripts/opensips-cli +9 -0
- opensipscli-0.3.1.dist-info/LICENSE +674 -0
- opensipscli-0.3.1.dist-info/METADATA +225 -0
- opensipscli-0.3.1.dist-info/RECORD +28 -0
- opensipscli-0.3.1.dist-info/WHEEL +5 -0
- opensipscli-0.3.1.dist-info/top_level.txt +1 -0
opensipscli/__init__.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
##
|
|
3
|
+
## This file is part of OpenSIPS CLI
|
|
4
|
+
## (see https://github.com/OpenSIPS/opensips-cli).
|
|
5
|
+
##
|
|
6
|
+
## This program is free software: you can redistribute it and/or modify
|
|
7
|
+
## it under the terms of the GNU General Public License as published by
|
|
8
|
+
## the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
## (at your option) any later version.
|
|
10
|
+
##
|
|
11
|
+
## This program is distributed in the hope that it will be useful,
|
|
12
|
+
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
## GNU General Public License for more details.
|
|
15
|
+
##
|
|
16
|
+
## You should have received a copy of the GNU General Public License
|
|
17
|
+
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
18
|
+
##
|
|
19
|
+
|
|
20
|
+
from .version import __version__
|
opensipscli/args.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
##
|
|
3
|
+
## This file is part of OpenSIPS CLI
|
|
4
|
+
## (see https://github.com/OpenSIPS/opensips-cli).
|
|
5
|
+
##
|
|
6
|
+
## This program is free software: you can redistribute it and/or modify
|
|
7
|
+
## it under the terms of the GNU General Public License as published by
|
|
8
|
+
## the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
## (at your option) any later version.
|
|
10
|
+
##
|
|
11
|
+
## This program is distributed in the hope that it will be useful,
|
|
12
|
+
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
## GNU General Public License for more details.
|
|
15
|
+
##
|
|
16
|
+
## You should have received a copy of the GNU General Public License
|
|
17
|
+
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
18
|
+
##
|
|
19
|
+
|
|
20
|
+
"""
|
|
21
|
+
Class that instruct the default values for arguments
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from opensipscli import defaults
|
|
25
|
+
|
|
26
|
+
class OpenSIPSCLIArgs:
|
|
27
|
+
|
|
28
|
+
"""
|
|
29
|
+
Class that contains the default values of CLI Arguments
|
|
30
|
+
"""
|
|
31
|
+
debug = False
|
|
32
|
+
print = False
|
|
33
|
+
execute = True
|
|
34
|
+
command = []
|
|
35
|
+
config = None
|
|
36
|
+
instance = defaults.DEFAULT_SECTION
|
|
37
|
+
extra_options = {}
|
|
38
|
+
|
|
39
|
+
__fields__ = ['debug',
|
|
40
|
+
'print',
|
|
41
|
+
'execute',
|
|
42
|
+
'command',
|
|
43
|
+
'config',
|
|
44
|
+
'instance',
|
|
45
|
+
'extra_options']
|
|
46
|
+
|
|
47
|
+
def __init__(self, **kwargs):
|
|
48
|
+
for k in kwargs:
|
|
49
|
+
if k in self.__fields__:
|
|
50
|
+
self.__setattr__(k, kwargs[k])
|
|
51
|
+
else:
|
|
52
|
+
self.extra_options[k] = kwargs[k]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
|
|
56
|
+
|
opensipscli/cli.py
ADDED
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
##
|
|
3
|
+
## This file is part of OpenSIPS CLI
|
|
4
|
+
## (see https://github.com/OpenSIPS/opensips-cli).
|
|
5
|
+
##
|
|
6
|
+
## This program is free software: you can redistribute it and/or modify
|
|
7
|
+
## it under the terms of the GNU General Public License as published by
|
|
8
|
+
## the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
## (at your option) any later version.
|
|
10
|
+
##
|
|
11
|
+
## This program is distributed in the hope that it will be useful,
|
|
12
|
+
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
## GNU General Public License for more details.
|
|
15
|
+
##
|
|
16
|
+
## You should have received a copy of the GNU General Public License
|
|
17
|
+
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
18
|
+
##
|
|
19
|
+
|
|
20
|
+
import cmd
|
|
21
|
+
import sys
|
|
22
|
+
import os
|
|
23
|
+
import shlex
|
|
24
|
+
import readline
|
|
25
|
+
import atexit
|
|
26
|
+
import importlib
|
|
27
|
+
from opensipscli import args
|
|
28
|
+
from opensipscli import comm
|
|
29
|
+
from opensipscli import defaults
|
|
30
|
+
from opensipscli.config import cfg
|
|
31
|
+
from opensipscli.logger import logger
|
|
32
|
+
from opensipscli.modules import *
|
|
33
|
+
|
|
34
|
+
class OpenSIPSCLI(cmd.Cmd, object):
|
|
35
|
+
"""
|
|
36
|
+
OpenSIPS-Cli shell
|
|
37
|
+
"""
|
|
38
|
+
modules = {}
|
|
39
|
+
excluded_errs = {}
|
|
40
|
+
registered_atexit = False
|
|
41
|
+
|
|
42
|
+
def __init__(self, options = None):
|
|
43
|
+
"""
|
|
44
|
+
contructor for OpenSIPS-Cli
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
if not options:
|
|
48
|
+
options = args.OpenSIPSCLIArgs()
|
|
49
|
+
|
|
50
|
+
self.debug = options.debug
|
|
51
|
+
self.print = options.print
|
|
52
|
+
self.execute = options.execute
|
|
53
|
+
self.command = options.command
|
|
54
|
+
self.modules_dir_inserted = None
|
|
55
|
+
|
|
56
|
+
if self.debug:
|
|
57
|
+
logger.setLevel("DEBUG")
|
|
58
|
+
|
|
59
|
+
cfg_file = None
|
|
60
|
+
if not options.config:
|
|
61
|
+
for f in defaults.CFG_PATHS:
|
|
62
|
+
if os.path.isfile(f) and os.access(f, os.R_OK):
|
|
63
|
+
# found a valid config file
|
|
64
|
+
cfg_file = f
|
|
65
|
+
break
|
|
66
|
+
else:
|
|
67
|
+
cfg_file = options.config
|
|
68
|
+
if not cfg_file:
|
|
69
|
+
logger.debug("no config file found in any of {}".
|
|
70
|
+
format(", ".join(defaults.CFG_PATHS)))
|
|
71
|
+
else:
|
|
72
|
+
logger.debug("using config file {}".format(cfg_file))
|
|
73
|
+
|
|
74
|
+
# __init__ of the configuration file
|
|
75
|
+
cfg.parse(cfg_file)
|
|
76
|
+
if not cfg.has_instance(options.instance):
|
|
77
|
+
logger.warning("Unknown instance '{}'! Using default instance '{}'!".
|
|
78
|
+
format(options.instance, defaults.DEFAULT_SECTION))
|
|
79
|
+
instance = defaults.DEFAULT_SECTION
|
|
80
|
+
else:
|
|
81
|
+
instance = options.instance
|
|
82
|
+
cfg.set_instance(instance)
|
|
83
|
+
if options:
|
|
84
|
+
cfg.set_custom_options(options.extra_options)
|
|
85
|
+
|
|
86
|
+
if not self.execute:
|
|
87
|
+
# __init__ of cmd.Cmd module
|
|
88
|
+
cmd.Cmd.__init__(self)
|
|
89
|
+
|
|
90
|
+
# Opening the current working instance
|
|
91
|
+
self.update_instance(cfg.current_instance)
|
|
92
|
+
|
|
93
|
+
if self.print:
|
|
94
|
+
logger.info(f"Config:\n" + "\n".join([f"{k}: {v}" for k, v in cfg.to_dict().items()]))
|
|
95
|
+
|
|
96
|
+
def update_logger(self):
|
|
97
|
+
"""
|
|
98
|
+
alter logging level
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
# first of all, let's handle logging
|
|
102
|
+
if self.debug:
|
|
103
|
+
level = "DEBUG"
|
|
104
|
+
else:
|
|
105
|
+
level = cfg.get("log_level")
|
|
106
|
+
logger.setLevel(level)
|
|
107
|
+
|
|
108
|
+
def clear_instance(self):
|
|
109
|
+
"""
|
|
110
|
+
update history
|
|
111
|
+
"""
|
|
112
|
+
# make sure we dump everything before swapping files
|
|
113
|
+
self.history_write()
|
|
114
|
+
|
|
115
|
+
def update_instance(self, instance):
|
|
116
|
+
"""
|
|
117
|
+
constructor of an OpenSIPS-Cli instance
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
# first of all, let's handle logging
|
|
121
|
+
self.current_instance = instance
|
|
122
|
+
self.update_logger()
|
|
123
|
+
|
|
124
|
+
# Update the intro and prompt
|
|
125
|
+
self.intro = cfg.get('prompt_intro')
|
|
126
|
+
self.prompt = '(%s): ' % cfg.get('prompt_name')
|
|
127
|
+
|
|
128
|
+
# initialize communcation handler
|
|
129
|
+
self.handler = comm.initialize()
|
|
130
|
+
|
|
131
|
+
# remove all loaded modules
|
|
132
|
+
self.modules = {}
|
|
133
|
+
|
|
134
|
+
skip_modules = []
|
|
135
|
+
if cfg.exists('skip_modules'):
|
|
136
|
+
skip_modules = cfg.get('skip_modules')
|
|
137
|
+
sys_modules = {}
|
|
138
|
+
if not self.execute:
|
|
139
|
+
print(self.intro)
|
|
140
|
+
# add the built-in modules and commands list
|
|
141
|
+
for mod in ['set', 'clear', 'help', 'history', 'exit', 'quit']:
|
|
142
|
+
self.modules[mod] = (self, None)
|
|
143
|
+
sys_modules = sys.modules
|
|
144
|
+
else:
|
|
145
|
+
try:
|
|
146
|
+
mod = "opensipscli.modules.{}".format(self.command[0])
|
|
147
|
+
sys_modules = { mod: sys.modules[mod] }
|
|
148
|
+
except:
|
|
149
|
+
pass
|
|
150
|
+
|
|
151
|
+
available_modules = { key[20:]: sys_modules[key] for key in
|
|
152
|
+
sys_modules.keys() if
|
|
153
|
+
key.startswith("opensipscli.modules.") and
|
|
154
|
+
key[20:] not in skip_modules }
|
|
155
|
+
for name, module in available_modules.items():
|
|
156
|
+
m = importlib.import_module("opensipscli.modules.{}".format(name))
|
|
157
|
+
if not hasattr(m, "Module"):
|
|
158
|
+
logger.debug("Skipping module '{}' - does not extend Module".
|
|
159
|
+
format(name))
|
|
160
|
+
continue
|
|
161
|
+
if not hasattr(m, name):
|
|
162
|
+
logger.debug("Skipping module '{}' - module implementation not found".
|
|
163
|
+
format(name))
|
|
164
|
+
continue
|
|
165
|
+
mod = getattr(module, name)
|
|
166
|
+
if not hasattr(mod, '__exclude__') or not hasattr(mod, '__get_methods__'):
|
|
167
|
+
logger.debug("Skipping module '{}' - module does not implement Module".
|
|
168
|
+
format(name))
|
|
169
|
+
continue
|
|
170
|
+
excl_mod = mod.__exclude__(mod)
|
|
171
|
+
if excl_mod[0] is True:
|
|
172
|
+
if excl_mod[1]:
|
|
173
|
+
self.excluded_errs[name] = excl_mod[1]
|
|
174
|
+
logger.debug("Skipping module '{}' - excluded on purpose".format(name))
|
|
175
|
+
continue
|
|
176
|
+
logger.debug("Loaded module '{}'".format(name))
|
|
177
|
+
imod = mod()
|
|
178
|
+
self.modules[name] = (imod, mod.__get_methods__(imod))
|
|
179
|
+
|
|
180
|
+
def history_write(self):
|
|
181
|
+
"""
|
|
182
|
+
save history file
|
|
183
|
+
"""
|
|
184
|
+
history_file = cfg.get('history_file')
|
|
185
|
+
logger.debug("saving history in {}".format(history_file))
|
|
186
|
+
os.makedirs(os.path.expanduser(os.path.dirname(history_file)), exist_ok=True)
|
|
187
|
+
try:
|
|
188
|
+
readline.write_history_file(os.path.expanduser(history_file))
|
|
189
|
+
except PermissionError:
|
|
190
|
+
logger.warning("failed to write CLI history to {} " +
|
|
191
|
+
"(no permission)".format(
|
|
192
|
+
history_file))
|
|
193
|
+
|
|
194
|
+
def preloop(self):
|
|
195
|
+
"""
|
|
196
|
+
preload a history file
|
|
197
|
+
"""
|
|
198
|
+
history_file = cfg.get('history_file')
|
|
199
|
+
logger.debug("using history file {}".format(history_file))
|
|
200
|
+
try:
|
|
201
|
+
readline.read_history_file(os.path.expanduser(history_file))
|
|
202
|
+
except PermissionError:
|
|
203
|
+
logger.warning("failed to read CLI history from {} " +
|
|
204
|
+
"(no permission)".format(
|
|
205
|
+
history_file))
|
|
206
|
+
except FileNotFoundError:
|
|
207
|
+
pass
|
|
208
|
+
|
|
209
|
+
readline.set_history_length(int(cfg.get('history_file_size')))
|
|
210
|
+
if not self.registered_atexit:
|
|
211
|
+
atexit.register(self.history_write)
|
|
212
|
+
|
|
213
|
+
def postcmd(self, stop, line):
|
|
214
|
+
"""
|
|
215
|
+
post command after switching instance
|
|
216
|
+
"""
|
|
217
|
+
if self.current_instance != cfg.current_instance:
|
|
218
|
+
self.clear_instance()
|
|
219
|
+
self.update_instance(cfg.current_instance)
|
|
220
|
+
# make sure we update all the history information
|
|
221
|
+
self.preloop()
|
|
222
|
+
|
|
223
|
+
return stop
|
|
224
|
+
|
|
225
|
+
def print_topics(self, header, cmds, cmdlen, maxcol):
|
|
226
|
+
"""
|
|
227
|
+
print topics, omit misc commands
|
|
228
|
+
"""
|
|
229
|
+
if header is not None:
|
|
230
|
+
if cmds:
|
|
231
|
+
self.stdout.write('%s\n' % str(header))
|
|
232
|
+
if self.ruler:
|
|
233
|
+
self.stdout.write('%s\n' % str(self.ruler*len(header)))
|
|
234
|
+
self.columnize(cmds, maxcol-1)
|
|
235
|
+
self.stdout.write('\n')
|
|
236
|
+
|
|
237
|
+
def cmdloop(self, intro=None):
|
|
238
|
+
"""
|
|
239
|
+
command loop, catching SIGINT
|
|
240
|
+
"""
|
|
241
|
+
if self.execute:
|
|
242
|
+
if len(self.command) < 1:
|
|
243
|
+
logger.error("no modules to run specified!")
|
|
244
|
+
return -1
|
|
245
|
+
|
|
246
|
+
module, command, modifiers, params = self.parse_command(self.command)
|
|
247
|
+
|
|
248
|
+
logger.debug("running in non-interactive mode {} {} {}".
|
|
249
|
+
format(module, command, params))
|
|
250
|
+
try:
|
|
251
|
+
ret = self.run_command(module, command, modifiers, params)
|
|
252
|
+
except KeyboardInterrupt:
|
|
253
|
+
print('^C')
|
|
254
|
+
return -1
|
|
255
|
+
|
|
256
|
+
# assume that by default it exists with success
|
|
257
|
+
if ret is None:
|
|
258
|
+
ret = 0
|
|
259
|
+
return ret
|
|
260
|
+
while True:
|
|
261
|
+
try:
|
|
262
|
+
super(OpenSIPSCLI, self).cmdloop(intro='')
|
|
263
|
+
break
|
|
264
|
+
except KeyboardInterrupt:
|
|
265
|
+
print('^C')
|
|
266
|
+
# any other commands exits with negative value
|
|
267
|
+
return -1
|
|
268
|
+
|
|
269
|
+
def emptyline(self):
|
|
270
|
+
if cfg.getBool('prompt_emptyline_repeat_cmd'):
|
|
271
|
+
super().emptyline()
|
|
272
|
+
|
|
273
|
+
def complete_modules(self, text):
|
|
274
|
+
"""
|
|
275
|
+
complete modules selection based on given text
|
|
276
|
+
"""
|
|
277
|
+
l = [a for a in self.modules.keys() if a.startswith(text)]
|
|
278
|
+
if len(l) == 1:
|
|
279
|
+
l[0] = l[0] + " "
|
|
280
|
+
return l
|
|
281
|
+
|
|
282
|
+
def complete_functions(self, module, text, line, begidx, endidx):
|
|
283
|
+
"""
|
|
284
|
+
complete function selection based on given text
|
|
285
|
+
"""
|
|
286
|
+
|
|
287
|
+
# builtin commands
|
|
288
|
+
_, command, modifiers, params = self.parse_command(line.split())
|
|
289
|
+
# get all the available modifiers of the module
|
|
290
|
+
all_params = []
|
|
291
|
+
if not command:
|
|
292
|
+
# haven't got to a command yet, so we might have some modifiers
|
|
293
|
+
try:
|
|
294
|
+
modiffunc = getattr(module[0], '__get_modifiers__')
|
|
295
|
+
modifiers_params = modiffunc()
|
|
296
|
+
except:
|
|
297
|
+
pass
|
|
298
|
+
all_params = [ x for x in modifiers_params if x not in modifiers ]
|
|
299
|
+
# if we are introducing a modifier, auto-complete only them
|
|
300
|
+
if begidx > 1 and line[begidx-1] == '-':
|
|
301
|
+
stripped_params = [ p.lstrip("-") for p in modifiers_params ]
|
|
302
|
+
l = [a for a in stripped_params if a.startswith(text)]
|
|
303
|
+
if len(l) == 1:
|
|
304
|
+
l[0] = l[0] + " "
|
|
305
|
+
else:
|
|
306
|
+
l = [a for a in l if a not in [ m.strip("-") for m in modifiers]]
|
|
307
|
+
return l
|
|
308
|
+
|
|
309
|
+
if module[1]:
|
|
310
|
+
all_params = all_params + module[1]
|
|
311
|
+
if len(all_params) > 0 and (not command or
|
|
312
|
+
(len(params) == 0 and line[-1] != ' ')):
|
|
313
|
+
l = [a for a in all_params if a.startswith(text)]
|
|
314
|
+
if len(l) == 1:
|
|
315
|
+
l[0] += " "
|
|
316
|
+
else:
|
|
317
|
+
try:
|
|
318
|
+
compfunc = getattr(module[0], '__complete__')
|
|
319
|
+
l = compfunc(command, text, line, begidx, endidx)
|
|
320
|
+
if not l:
|
|
321
|
+
return None
|
|
322
|
+
except AttributeError:
|
|
323
|
+
return ['']
|
|
324
|
+
# looking for a different command
|
|
325
|
+
return l
|
|
326
|
+
|
|
327
|
+
# Overwritten function for our customized auto-complete
|
|
328
|
+
def complete(self, text, state):
|
|
329
|
+
"""
|
|
330
|
+
auto-complete selection based on given text and state parameters
|
|
331
|
+
"""
|
|
332
|
+
if state == 0:
|
|
333
|
+
origline = readline.get_line_buffer()
|
|
334
|
+
line = origline.lstrip()
|
|
335
|
+
stripped = len(origline) - len(line)
|
|
336
|
+
begidx = readline.get_begidx() - stripped
|
|
337
|
+
endidx = readline.get_endidx() - stripped
|
|
338
|
+
if begidx > 0:
|
|
339
|
+
mod, args, foo = self.parseline(line)
|
|
340
|
+
if mod == '':
|
|
341
|
+
return self.complete_modules(text)[state]
|
|
342
|
+
elif not mod in self.modules:
|
|
343
|
+
logger.error("BUG: mod '{}' not found!".format(mod))
|
|
344
|
+
else:
|
|
345
|
+
module = self.modules[mod]
|
|
346
|
+
self.completion_matches = \
|
|
347
|
+
self.complete_functions(module, text, line, begidx, endidx)
|
|
348
|
+
else:
|
|
349
|
+
self.completion_matches = self.complete_modules(text)
|
|
350
|
+
try:
|
|
351
|
+
return self.completion_matches[state]
|
|
352
|
+
except IndexError:
|
|
353
|
+
return ['']
|
|
354
|
+
|
|
355
|
+
# Parse parameters
|
|
356
|
+
def parse_command(self, line):
|
|
357
|
+
|
|
358
|
+
module = line[0]
|
|
359
|
+
if len(line) < 2:
|
|
360
|
+
return module, None, [], []
|
|
361
|
+
paramIndex = 1
|
|
362
|
+
while paramIndex < len(line):
|
|
363
|
+
if line[paramIndex][0] != "-":
|
|
364
|
+
break
|
|
365
|
+
paramIndex = paramIndex + 1
|
|
366
|
+
if paramIndex == 1:
|
|
367
|
+
modifiers = []
|
|
368
|
+
command = line[1]
|
|
369
|
+
params = line[2:]
|
|
370
|
+
elif paramIndex == len(line):
|
|
371
|
+
modifiers = line[1:paramIndex]
|
|
372
|
+
command = None
|
|
373
|
+
params = []
|
|
374
|
+
else:
|
|
375
|
+
modifiers = line[1:paramIndex]
|
|
376
|
+
command = line[paramIndex]
|
|
377
|
+
params = line[paramIndex + 1:]
|
|
378
|
+
|
|
379
|
+
return module, command, modifiers, params
|
|
380
|
+
|
|
381
|
+
# Execute commands from Modules
|
|
382
|
+
def run_command(self, module, cmd, modifiers, params):
|
|
383
|
+
"""
|
|
384
|
+
run a module command with given parameters
|
|
385
|
+
"""
|
|
386
|
+
try:
|
|
387
|
+
mod = self.modules[module]
|
|
388
|
+
except (AttributeError, KeyError):
|
|
389
|
+
if module in self.excluded_errs:
|
|
390
|
+
for err_msg in self.excluded_errs[module]:
|
|
391
|
+
logger.error(err_msg)
|
|
392
|
+
return -1
|
|
393
|
+
else:
|
|
394
|
+
logger.error("no module '{}' loaded".format(module))
|
|
395
|
+
return -1
|
|
396
|
+
# if the module does not return any methods (returned None)
|
|
397
|
+
# we simply call the module's name method
|
|
398
|
+
if not mod[1]:
|
|
399
|
+
if cmd and params is not None:
|
|
400
|
+
params.insert(0, cmd)
|
|
401
|
+
cmd = mod[0].__module__
|
|
402
|
+
if cmd.startswith("opensipscli.modules."):
|
|
403
|
+
cmd = cmd[20:]
|
|
404
|
+
elif not cmd and '' not in mod[1]:
|
|
405
|
+
logger.error("module '{}' expects the following commands: {}".
|
|
406
|
+
format(module, ", ".join(mod[1])))
|
|
407
|
+
return -1
|
|
408
|
+
elif cmd and not cmd in mod[1]:
|
|
409
|
+
logger.error("no command '{}' in module '{}'".
|
|
410
|
+
format(cmd, module))
|
|
411
|
+
return -1
|
|
412
|
+
logger.debug("running command '{}' '{}'".format(cmd, params))
|
|
413
|
+
return mod[0].__invoke__(cmd, params, modifiers)
|
|
414
|
+
|
|
415
|
+
def default(self, line):
|
|
416
|
+
try:
|
|
417
|
+
aux = shlex.split(line)
|
|
418
|
+
except ValueError:
|
|
419
|
+
""" if the line ends in a backspace, just clean it"""
|
|
420
|
+
line = line[:-1]
|
|
421
|
+
aux = shlex.split(line)
|
|
422
|
+
|
|
423
|
+
module, cmd, modifiers, params = self.parse_command(aux)
|
|
424
|
+
self.run_command(module, cmd, modifiers, params)
|
|
425
|
+
|
|
426
|
+
def do_history(self, line):
|
|
427
|
+
"""
|
|
428
|
+
print entries in history file
|
|
429
|
+
"""
|
|
430
|
+
if not line:
|
|
431
|
+
try:
|
|
432
|
+
with open(os.path.expanduser(cfg.get('history_file'))) as hf:
|
|
433
|
+
for num, line in enumerate(hf, 1):
|
|
434
|
+
print(num, line, end='')
|
|
435
|
+
except FileNotFoundError:
|
|
436
|
+
pass
|
|
437
|
+
|
|
438
|
+
def do_set(self, line):
|
|
439
|
+
"""
|
|
440
|
+
handle dynamic settings (key-value pairs)
|
|
441
|
+
"""
|
|
442
|
+
parsed = line.split('=', 1)
|
|
443
|
+
if len(parsed) < 2:
|
|
444
|
+
logger.error("setting value format is 'key=value'!")
|
|
445
|
+
return
|
|
446
|
+
key = parsed[0]
|
|
447
|
+
value = parsed[1]
|
|
448
|
+
cfg.set(key, value)
|
|
449
|
+
|
|
450
|
+
# Used to get info for a certain command
|
|
451
|
+
def do_help(self, line):
|
|
452
|
+
# TODO: Add help for commands
|
|
453
|
+
print("Usage:: help cmd - returns information about \"cmd\"")
|
|
454
|
+
|
|
455
|
+
# Clear the terminal screen
|
|
456
|
+
def do_clear(self, line):
|
|
457
|
+
os.system('clear')
|
|
458
|
+
|
|
459
|
+
# Commands used to exit the shell
|
|
460
|
+
def do_EOF(self, line): # It catches Ctrl+D
|
|
461
|
+
print('^D')
|
|
462
|
+
return True
|
|
463
|
+
|
|
464
|
+
def do_quit(self, line):
|
|
465
|
+
return True
|
|
466
|
+
|
|
467
|
+
def do_exit(self, line):
|
|
468
|
+
return True
|
|
469
|
+
|
|
470
|
+
def mi(self, cmd, params = [], silent = False):
|
|
471
|
+
"""helper for running MI commands"""
|
|
472
|
+
return comm.execute(cmd, params, silent)
|
opensipscli/comm.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
##
|
|
3
|
+
## This file is part of OpenSIPS CLI
|
|
4
|
+
## (see https://github.com/OpenSIPS/opensips-cli).
|
|
5
|
+
##
|
|
6
|
+
## This program is free software: you can redistribute it and/or modify
|
|
7
|
+
## it under the terms of the GNU General Public License as published by
|
|
8
|
+
## the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
## (at your option) any later version.
|
|
10
|
+
##
|
|
11
|
+
## This program is distributed in the hope that it will be useful,
|
|
12
|
+
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
## GNU General Public License for more details.
|
|
15
|
+
##
|
|
16
|
+
## You should have received a copy of the GNU General Public License
|
|
17
|
+
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
18
|
+
##
|
|
19
|
+
|
|
20
|
+
from opensipscli.logger import logger
|
|
21
|
+
from opensipscli.config import cfg
|
|
22
|
+
from opensips.mi import OpenSIPSMI, OpenSIPSMIException
|
|
23
|
+
|
|
24
|
+
comm_handler = None
|
|
25
|
+
comm_handler_valid = None
|
|
26
|
+
|
|
27
|
+
def initialize():
|
|
28
|
+
global comm_handler
|
|
29
|
+
comm_type = cfg.get('communication_type')
|
|
30
|
+
comm_handler = OpenSIPSMI(comm_type, **cfg.to_dict())
|
|
31
|
+
valid()
|
|
32
|
+
|
|
33
|
+
def execute(cmd, params=[], silent=False):
|
|
34
|
+
global comm_handler
|
|
35
|
+
try:
|
|
36
|
+
ret = comm_handler.execute(cmd, params)
|
|
37
|
+
except OpenSIPSMIException as ex:
|
|
38
|
+
if not silent:
|
|
39
|
+
logger.error("command '{}' returned: {}".format(cmd, ex))
|
|
40
|
+
return None
|
|
41
|
+
return ret
|
|
42
|
+
|
|
43
|
+
def valid():
|
|
44
|
+
global comm_handler
|
|
45
|
+
global comm_handler_valid
|
|
46
|
+
if comm_handler_valid:
|
|
47
|
+
return comm_handler_valid
|
|
48
|
+
if not comm_handler:
|
|
49
|
+
comm_handler_valid = (False, None)
|
|
50
|
+
try:
|
|
51
|
+
if hasattr(comm_handler, "valid"):
|
|
52
|
+
comm_handler_valid = comm_handler.valid()
|
|
53
|
+
else:
|
|
54
|
+
comm_handler_valid = (True, None)
|
|
55
|
+
except:
|
|
56
|
+
comm_handler_valid = (False, None)
|
|
57
|
+
return comm_handler_valid
|