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.
@@ -0,0 +1,53 @@
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.config import cfg
21
+ from opensipscli.logger import logger
22
+ from opensipscli.module import Module
23
+
24
+ class instance(Module):
25
+
26
+ def get_instances(self):
27
+ l = cfg.config.sections()
28
+ default_section = cfg.get_default_instance()
29
+ if default_section not in l:
30
+ l.insert(0, default_section)
31
+ return l
32
+
33
+ def do_show(self, params, modifiers):
34
+ print(cfg.current_instance)
35
+
36
+ def do_list(self, params, modifiers):
37
+ for i in self.get_instances():
38
+ print(i)
39
+
40
+ def complete_switch(self, text, line, *ignore):
41
+ if len(line.split(' ')) > 3:
42
+ return []
43
+ return [ a for a in self.get_instances() if a.startswith(text)]
44
+
45
+ def do_switch(self, params, modifiers):
46
+ if len(params) == 0:
47
+ return
48
+ new_instance = params[0]
49
+ if cfg.has_instance(new_instance):
50
+ cfg.set_instance(new_instance)
51
+ else:
52
+ logger.error("cannot switch to instance '{}': instance not found!".format(new_instance))
53
+ return -1
@@ -0,0 +1,200 @@
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 re
21
+ import json
22
+ import shlex
23
+ from collections import OrderedDict
24
+ from opensipscli.config import cfg
25
+ from opensipscli.logger import logger
26
+ from opensipscli.module import Module
27
+ from opensipscli import comm
28
+
29
+ try:
30
+ import yaml
31
+ yaml_available = True
32
+ except ImportError:
33
+ yaml_available = False
34
+
35
+ # temporary special handling for commands that require array params
36
+ # format is: command: (idx, name)
37
+ MI_ARRAY_PARAMS_COMMANDS = {
38
+ "fs_subscribe": (1, "events"),
39
+ "fs_unsubscribe": (1, "events"),
40
+ "b2b_trigger_scenario": (3, "scenario_params"),
41
+ "dlg_push_var": (2, "DID"),
42
+ "get_statistics": (0, "statistics"),
43
+ "list_statistics": (0, "statistics"),
44
+ "reset_statistics": (0, "statistics"),
45
+ "trace_start": (0, "filter"),
46
+ "raise_event": (1, "params"),
47
+ "dfks_set_feature": (4, "values"),
48
+ "cluster_broadcast_mi": (2, "cmd_params"),
49
+ }
50
+
51
+
52
+ MI_MODIFIERS = [ "-j" ]
53
+
54
+ class mi(Module):
55
+
56
+ def print_pretty_print(self, result):
57
+ print(json.dumps(result, indent=4))
58
+
59
+ def print_dictionary(self, result):
60
+ print(str(result))
61
+
62
+ def print_lines(self, result, indent=0):
63
+ if type(result) in [OrderedDict, dict]:
64
+ for k, v in result.items():
65
+ if type(v) in [OrderedDict, list, dict]:
66
+ print(" " * indent + k + ":")
67
+ self.print_lines(v, indent + 4)
68
+ else:
69
+ print(" " * indent + "{}: {}". format(k, v))
70
+ elif type(result) == list:
71
+ for v in result:
72
+ self.print_lines(v, indent)
73
+ else:
74
+ print(" " * indent + str(result))
75
+ pass
76
+
77
+ def print_yaml(self, result):
78
+ if not yaml_available:
79
+ logger.warning("yaml not available on your platform! "
80
+ "Please install `python-yaml` package or similar!")
81
+ else:
82
+ print(yaml.dump(result, default_flow_style=False).strip())
83
+
84
+ def get_params_set(self, cmds):
85
+ l = set()
86
+ for p in cmds:
87
+ m = re.match('([a-zA-Z\.\-_]+)=', p)
88
+ # if it's not a parameter name, skip
89
+ if m:
90
+ l.add(m.group(1))
91
+ else:
92
+ return None
93
+ return l
94
+
95
+ def get_params_names(self, line):
96
+ cmds = shlex.split(line)
97
+ # cmd[0] = module, cmd[1] = command
98
+ if len(cmds) < 2:
99
+ return None
100
+ return self.get_params_set(cmds[2:])
101
+
102
+ def parse_params(self, cmd, params, modifiers):
103
+
104
+ # first, we check to see if we have only named parameters
105
+ nparams = self.get_params_set(params)
106
+ if nparams is not None:
107
+ logger.debug("named parameters are used")
108
+ new_params = {}
109
+ for p in params:
110
+ s = p.split("=", 1)
111
+ value = "" if len(s) == 1 else s[1]
112
+ # check to see if we have to split them in array or not
113
+ if cmd in MI_ARRAY_PARAMS_COMMANDS and \
114
+ "-j" not in modifiers and \
115
+ MI_ARRAY_PARAMS_COMMANDS[cmd][1] == s[0]:
116
+ value = shlex.split(value)
117
+ new_params[s[0]] = value
118
+ else:
119
+ # old style positional parameters
120
+ logger.debug("positional parameters are used")
121
+ # if the command is not in MI_ARRAY_PARAMS_COMMANDS, return the
122
+ # parameters as they are
123
+ logger.debug("0. {}".format(params))
124
+ if "-j" in modifiers:
125
+ json_params = []
126
+ for x in params:
127
+ try:
128
+ x = json.loads(x)
129
+ except:
130
+ pass
131
+ json_params.append(x)
132
+ params = json_params
133
+
134
+ if not cmd in MI_ARRAY_PARAMS_COMMANDS or "-j" in modifiers:
135
+ return params
136
+ # build params based on their index
137
+ new_params = params[0:MI_ARRAY_PARAMS_COMMANDS[cmd][0]]
138
+ if params[MI_ARRAY_PARAMS_COMMANDS[cmd][0]:]:
139
+ new_params.append(params[MI_ARRAY_PARAMS_COMMANDS[cmd][0]:])
140
+ return new_params
141
+
142
+ def __invoke__(self, cmd, params=None, modifiers=None):
143
+ params = self.parse_params(cmd, params, modifiers)
144
+ # Mi Module works with JSON Communication
145
+ logger.debug("running command '{}' '{}'".format(cmd, params))
146
+ res = comm.execute(cmd, params)
147
+ if res is None:
148
+ return -1
149
+ output_type = cfg.get('output_type')
150
+ if output_type == "pretty-print":
151
+ self.print_pretty_print(res)
152
+ elif output_type == "dictionary":
153
+ self.print_dictionary(res)
154
+ elif output_type == "lines":
155
+ self.print_lines(res)
156
+ elif output_type == "yaml":
157
+ self.print_yaml(res)
158
+ elif output_type == "none":
159
+ pass # no one interested in the reply
160
+ else:
161
+ logger.error("unknown output_type='{}'! Dropping output!"
162
+ .format(output_type))
163
+ return 0
164
+
165
+ def __complete__(self, command, text, line, begidx, endidx):
166
+ # TODO: shall we cache this?
167
+ params_arr = comm.execute('which', {'command': command})
168
+ if len(text) == 0:
169
+ # if last character is an equal, it's probably a value, or it will
170
+ if line[-1] == "=":
171
+ return ['']
172
+ params = self.get_params_names(line)
173
+ if params is None:
174
+ flat_list = list([item for sublist in params_arr for item in sublist])
175
+ else:
176
+ # check in the line to see the parameters we've used
177
+ flat_list = set()
178
+ for p in params_arr:
179
+ sp = set(p)
180
+ if params.issubset(sp):
181
+ flat_list = flat_list.union(sp)
182
+ flat_list = flat_list - params
183
+ else:
184
+ flat_list = []
185
+ for l in params_arr:
186
+ p = [ x for x in l if x.startswith(text) ]
187
+ if len(p) != 0:
188
+ flat_list += p
189
+ l = [ x + "=" for x in list(dict.fromkeys(flat_list)) ]
190
+ return l if len(l) > 0 else ['']
191
+
192
+ def __exclude__(self):
193
+ vld = comm.valid()
194
+ return (not vld[0], vld[1])
195
+
196
+ def __get_methods__(self):
197
+ return comm.execute('which')
198
+
199
+ def __get_modifiers__(self):
200
+ return MI_MODIFIERS
@@ -0,0 +1,354 @@
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.module import Module
21
+ from opensipscli.logger import logger
22
+ from socket import gethostname
23
+ from pprint import pprint
24
+ from time import gmtime, mktime
25
+ from os.path import exists, join, dirname
26
+ from os import makedirs
27
+ from opensipscli.config import cfg, OpenSIPSCLIConfig
28
+ from random import randrange
29
+
30
+ openssl_version = None
31
+
32
+ try:
33
+ from cryptography import x509
34
+ from cryptography.hazmat.backends import default_backend
35
+ from cryptography.hazmat.primitives import hashes, serialization
36
+ from cryptography.hazmat.primitives.asymmetric import rsa
37
+ from cryptography.x509.oid import NameOID
38
+ import datetime
39
+ openssl_version = 'cryptography'
40
+ except ImportError:
41
+ logger.info("cryptography library not available!")
42
+ try:
43
+ from OpenSSL import crypto, SSL
44
+ openssl_version = 'openssl'
45
+ except (TypeError, ImportError):
46
+ logger.info("OpenSSL library not available!")
47
+
48
+ class tlsCert:
49
+
50
+ def __init__(self, prefix, cfg=None):
51
+
52
+ if not cfg:
53
+ self.load(prefix)
54
+ return
55
+ self.CN = cfg.read_param("tls_"+prefix+"_common_name", "Website address (CN)", "opensips.org")
56
+ self.C = cfg.read_param("tls_"+prefix+"_country", "Country (C)", "RO")
57
+ self.ST = cfg.read_param("tls_"+prefix+"_state", "State (ST)", "Bucharest")
58
+ self.L = cfg.read_param("tls_"+prefix+"_locality", "Locality (L)", "Bucharest")
59
+ self.O = cfg.read_param("tls_"+prefix+"_organisation", "Organization (O)", "OpenSIPS")
60
+ self.OU = cfg.read_param("tls_"+prefix+"_organisational_unit", "Organisational Unit (OU)", "Project")
61
+ self.notafter = int(cfg.read_param("tls_"+prefix+"_notafter", "Certificate validity (seconds)", 315360000))
62
+ self.md = cfg.read_param("tls_"+prefix+"_md", "Digest Algorithm", "SHA256")
63
+
64
+ class tlsKey:
65
+
66
+ def __init__(self, prefix, cfg=None):
67
+
68
+ if not cfg:
69
+ self.load(prefix)
70
+ return
71
+ self.key_size = int(cfg.read_param("tls_"+prefix+"_key_size", "RSA key size (bits)", 4096))
72
+
73
+
74
+ class tlsOpenSSLCert(tlsCert):
75
+
76
+ def __init__(self, prefix, cfg=None):
77
+ super().__init__(prefix, cfg)
78
+ if not cfg:
79
+ return
80
+ cert = crypto.X509()
81
+ cert.set_version(2)
82
+ cert.get_subject().CN = self.CN
83
+ cert.get_subject().C = self.C
84
+ cert.get_subject().ST = self.ST
85
+ cert.get_subject().L = self.L
86
+ cert.get_subject().O = self.O
87
+ cert.get_subject().OU = self.OU
88
+ cert.set_serial_number(randrange(100000))
89
+ cert.gmtime_adj_notBefore(0)
90
+ cert.gmtime_adj_notAfter(self.notafter)
91
+
92
+ extensions = [
93
+ crypto.X509Extension(b'basicConstraints', False, b'CA:TRUE'),
94
+ crypto.X509Extension(b'extendedKeyUsage', False, b'clientAuth,serverAuth')
95
+ ]
96
+
97
+ cert.add_extensions(extensions)
98
+
99
+ self.cert = cert
100
+
101
+ def load(self, cacert):
102
+ self.cert = crypto.load_certificate(crypto.FILETYPE_PEM, open(cacert, 'rt').read())
103
+
104
+ def sign(self, key):
105
+ self.cert.set_pubkey(key)
106
+ self.cert.sign(key.key, self.md)
107
+
108
+ def set_issuer(self, issuer):
109
+ self.cert.set_issuer(issuer)
110
+
111
+ def get_subject(self):
112
+ return self.cert.get_subject()
113
+
114
+ def dump(self):
115
+ return crypto.dump_certificate(crypto.FILETYPE_PEM, self.cert).decode('utf-8')
116
+
117
+ class tlsCryptographyCert(tlsCert):
118
+
119
+ def __init__(self, prefix, cfg=None):
120
+ super().__init__(prefix, cfg)
121
+ if not cfg:
122
+ return
123
+ builder = x509.CertificateBuilder()
124
+ builder = builder.subject_name(x509.Name([
125
+ x509.NameAttribute(NameOID.COMMON_NAME, self.CN),
126
+ x509.NameAttribute(NameOID.COUNTRY_NAME, self.C),
127
+ x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, self.ST),
128
+ x509.NameAttribute(NameOID.LOCALITY_NAME, self.L),
129
+ x509.NameAttribute(NameOID.ORGANIZATION_NAME, self.O),
130
+ x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, self.OU),
131
+ ]))
132
+ builder = builder.serial_number(x509.random_serial_number())
133
+ builder = builder.not_valid_before(datetime.datetime.today() -
134
+ datetime.timedelta(1))
135
+ builder = builder.not_valid_after(datetime.datetime.today() +
136
+ datetime.timedelta(0, self.notafter))
137
+ builder = builder.add_extension(
138
+ x509.BasicConstraints(ca=False, path_length=None),
139
+ critical=False
140
+ )
141
+ builder = builder.add_extension(
142
+ x509.ExtendedKeyUsage([
143
+ x509.ExtendedKeyUsageOID.CLIENT_AUTH,
144
+ x509.ExtendedKeyUsageOID.SERVER_AUTH]),
145
+ critical=False
146
+ )
147
+ self.builder = builder
148
+ self.cert = None
149
+
150
+ def load(self, cacert):
151
+ self.cert = x509.load_pem_x509_certificate(open(cacert, 'rb').read())
152
+
153
+ def sign(self, key):
154
+ self.builder = self.builder.public_key(key.key.public_key())
155
+ self.cert = self.builder.sign(private_key = key.key,
156
+ algorithm=getattr(hashes, self.md)(),
157
+ backend=default_backend())
158
+
159
+ def set_issuer(self, issuer):
160
+ self.builder = self.builder.issuer_name(issuer)
161
+
162
+ def get_subject(self):
163
+ if self.cert:
164
+ return self.cert.subject
165
+ return self.builder._subject_name
166
+
167
+ def dump(self):
168
+ return self.cert.public_bytes(encoding=serialization.Encoding.PEM).decode('utf-8')
169
+
170
+
171
+ class tlsOpenSSLKey(tlsKey):
172
+
173
+ def __init__(self, prefix, cfg=None):
174
+ super().__init__(prefix, cfg)
175
+ if not cfg:
176
+ return
177
+ key = crypto.PKey()
178
+ key.generate_key(crypto.TYPE_RSA, self.key_size)
179
+ self.key = key
180
+
181
+ def dump(self):
182
+ return crypto.dump_privatekey(crypto.FILETYPE_PEM, self.key).decode('utf-8')
183
+
184
+ def load(self, key):
185
+ self.key = crypto.load_privatekey(crypto.FILETYPE_PEM, open(key, 'rt').read())
186
+
187
+ class tlsCryptographyKey(tlsKey):
188
+
189
+ def __init__(self, prefix, cfg=None):
190
+ super().__init__(prefix, cfg)
191
+ if not cfg:
192
+ return
193
+ self.key = rsa.generate_private_key(
194
+ key_size=self.key_size,
195
+ public_exponent=65537,
196
+ backend=default_backend()
197
+ )
198
+
199
+ def dump(self):
200
+ return self.key.private_bytes(encoding=serialization.Encoding.PEM,
201
+ format=serialization.PrivateFormat.TraditionalOpenSSL,
202
+ encryption_algorithm=serialization.NoEncryption()
203
+ ).decode('utf-8')
204
+
205
+ def load(self, key):
206
+ self.key = serialization.load_pem_private_key(open(key, 'rb').read(),
207
+ password=None)
208
+
209
+ class tls(Module):
210
+ def do_rootCA(self, params, modifiers=None):
211
+ global cfg
212
+ logger.info("Preparing to generate CA cert + key...")
213
+
214
+ # TODO
215
+ # separate cli.cfg files for TLS are fully deprecated, this if block is
216
+ # only kept for backwards-compatibility. Remove starting from v3.2! <3
217
+ if cfg.exists('tls_ca_config'):
218
+ tls_cfg = cfg.get('tls_ca_config')
219
+ cfg = OpenSIPSCLIConfig()
220
+ cfg.parse(tls_cfg)
221
+
222
+ ca_dir = cfg.read_param("tls_ca_dir", "Output directory", "/etc/opensips/tls/rootCA/")
223
+ cert_file = cfg.read_param("tls_ca_cert_file", "Output cert file", "cacert.pem")
224
+ key_file = cfg.read_param("tls_ca_key_file", "Output key file", "private/cakey.pem")
225
+ c_f = join(ca_dir, cert_file)
226
+ k_f = join(ca_dir, key_file)
227
+
228
+ if (exists(c_f) or exists(k_f)) and not cfg.read_param("tls_ca_overwrite",
229
+ "CA certificate or key already exists, overwrite?", "yes", True):
230
+ return
231
+
232
+ if openssl_version == 'openssl':
233
+ cert = tlsOpenSSLCert("ca", cfg)
234
+ key = tlsOpenSSLKey("ca", cfg)
235
+ else:
236
+ cert = tlsCryptographyCert("ca", cfg)
237
+ key = tlsCryptographyKey("ca", cfg)
238
+
239
+ cert.set_issuer(cert.get_subject())
240
+ cert.sign(key)
241
+
242
+ try:
243
+ if not exists(dirname(c_f)):
244
+ makedirs(dirname(c_f))
245
+ open(c_f, "wt").write(cert.dump())
246
+ except Exception as e:
247
+ logger.exception(e)
248
+ logger.error("Failed to write to %s", c_f)
249
+ return
250
+
251
+ try:
252
+ if not exists(dirname(k_f)):
253
+ makedirs(dirname(k_f))
254
+ open(k_f, "wt").write(key.dump())
255
+ except Exception as e:
256
+ logger.exception(e)
257
+ logger.error("Failed to write to %s", k_f)
258
+ return
259
+
260
+ logger.info("CA certificate created in " + c_f)
261
+ logger.info("CA private key created in " + k_f)
262
+
263
+ def do_userCERT(self, params, modifiers=None):
264
+ global cfg
265
+ logger.info("Preparing to generate user cert + key + CA list...")
266
+
267
+ # TODO
268
+ # separate cli.cfg files for TLS are fully deprecated, this if block is
269
+ # only kept for backwards-compatibility. Remove starting from v3.2! <3
270
+ if cfg.exists('tls_user_config'):
271
+ tls_cfg = cfg.get('tls_user_config')
272
+ cfg = OpenSIPSCLIConfig()
273
+ cfg.parse(tls_cfg)
274
+
275
+ user_dir = cfg.read_param("tls_user_dir", "Output directory", "/etc/opensips/tls/user/")
276
+ cert_file = cfg.read_param("tls_user_cert_file", "Output cert file", "user-cert.pem")
277
+ key_file = cfg.read_param("tls_user_key_file", "Output key file", "user-privkey.pem")
278
+ calist_file = cfg.read_param("tls_user_calist_file", "Output CA list file", "user-calist.pem")
279
+
280
+ c_f = join(user_dir, cert_file)
281
+ k_f = join(user_dir, key_file)
282
+ ca_f = join(user_dir, calist_file)
283
+
284
+ if (exists(c_f) or exists(k_f) or exists(ca_f)) and not cfg.read_param("tls_user_overwrite",
285
+ "User certificate, key or CA list file already exists, overwrite?", "yes", True):
286
+ return
287
+
288
+ cacert = cfg.read_param("tls_user_cacert", "CA cert file", "/etc/opensips/tls/rootCA/cacert.pem")
289
+ cakey = cfg.read_param("tls_user_cakey", "CA key file", "/etc/opensips/tls/rootCA/private/cakey.pem")
290
+
291
+ try:
292
+ if openssl_version == 'openssl':
293
+ ca_cert = tlsOpenSSLCert(cacert)
294
+ else:
295
+ ca_cert = tlsCryptographyCert(cacert)
296
+ except Exception as e:
297
+ logger.exception(e)
298
+ logger.error("Failed to load %s", cacert)
299
+ return
300
+
301
+ try:
302
+ if openssl_version == 'openssl':
303
+ ca_key = tlsOpenSSLLey(cakey)
304
+ else:
305
+ ca_key = tlsCryptographyKey(cakey)
306
+ except Exception as e:
307
+ logger.exception(e)
308
+ logger.error("Failed to load %s", cakey)
309
+ return
310
+
311
+ # create a self-signed cert
312
+ if openssl_version == 'openssl':
313
+ cert = tlsOpenSSLCert("user", cfg)
314
+ key = tlsOpenSSLKey("user", cfg)
315
+ else:
316
+ cert = tlsCryptographyCert("user", cfg)
317
+ key = tlsCryptographyKey("user", cfg)
318
+
319
+ cert.set_issuer(ca_cert.get_subject())
320
+ cert.sign(ca_key)
321
+ try:
322
+ if not exists(dirname(c_f)):
323
+ makedirs(dirname(c_f))
324
+ open(c_f, "wt").write(cert.dump())
325
+ except Exception as e:
326
+ logger.exception(e)
327
+ logger.error("Failed to write to %s", c_f)
328
+ return
329
+
330
+ try:
331
+ if not exists(dirname(k_f)):
332
+ makedirs(dirname(k_f))
333
+ open(k_f, "wt").write(key.dump())
334
+ except Exception as e:
335
+ logger.exception(e)
336
+ logger.error("Failed to write to %s", k_f)
337
+ return
338
+
339
+ try:
340
+ if not exists(dirname(ca_f)):
341
+ makedirs(dirname(ca_f))
342
+ open(ca_f, "wt").write(ca_cert.dump())
343
+ except Exception as e:
344
+ logger.exception(e)
345
+ logger.error("Failed to write to %s", ca_f)
346
+ return
347
+
348
+ logger.info("user certificate created in " + c_f)
349
+ logger.info("user private key created in " + k_f)
350
+ logger.info("user CA list (chain of trust) created in " + ca_f)
351
+
352
+
353
+ def __exclude__(self):
354
+ return (not openssl_version, None)