csspin-ce 3.0.0__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,487 @@
1
+ # -*- mode: python; coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2024 CONTACT Software GmbH
4
+ # https://www.contact-software.com/
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ # pylint: disable=too-many-lines
19
+
20
+ """
21
+ Provides a wrapper around the CLI tool of ce_services and
22
+ provisions all tool necessary for these ce_services.
23
+ """
24
+
25
+ import os
26
+ import shutil
27
+ import sys
28
+ from urllib.error import HTTPError
29
+
30
+ from csspin import (
31
+ Verbosity,
32
+ config,
33
+ debug,
34
+ die,
35
+ exists,
36
+ mkdir,
37
+ option,
38
+ rmtree,
39
+ setenv,
40
+ sh,
41
+ task,
42
+ )
43
+ from path import Path
44
+
45
+ defaults = config(
46
+ hivemq=config(
47
+ enabled=False,
48
+ install_dir="{spin.data}/hivemq",
49
+ version="2024.4",
50
+ elements_integration=config(
51
+ user="csiot_integrator",
52
+ password="", # nosec: hardcoded_password_funcarg
53
+ install_dir="",
54
+ ),
55
+ ),
56
+ influxdb=config(
57
+ enabled=False,
58
+ version="1.8.10",
59
+ install_dir="{spin.data}/influxdb",
60
+ ),
61
+ traefik=config(
62
+ version="2.11.2",
63
+ dashboard_port="",
64
+ install_dir="{spin.data}/traefik",
65
+ ),
66
+ solr=config(
67
+ version="9.8.1",
68
+ install_dir="{spin.data}/solr",
69
+ version_postfix="-slim",
70
+ ),
71
+ redis=config(
72
+ version="7.2.4",
73
+ install_dir="{spin.data}/redis",
74
+ ),
75
+ loglevel="",
76
+ requires=config(
77
+ spin=["csspin_ce.mkinstance", "csspin_java.java"],
78
+ python=[
79
+ "ce_services",
80
+ "requests",
81
+ "psutil",
82
+ ],
83
+ system=config(debian=config(apt=["redis-server"])),
84
+ ),
85
+ )
86
+
87
+
88
+ def extract_service_config(cfg):
89
+ """
90
+ Helper to match the config of the plugin into the config of the ce_services
91
+ command-line tool.
92
+
93
+ Returns a dict to feed directly into
94
+ ce_services.RequireAllServices/ce_services.Require.
95
+
96
+ :param cfg: The spin config tree
97
+ :type cfg: ConfigTree
98
+ :return: A dict with the ce_services config from the config_tree
99
+ :rtype: dict
100
+ """
101
+ additional_cfg = {}
102
+ if cfg.mkinstance.base.instance_admpwd:
103
+ additional_cfg["instance_admpwd"] = cfg.mkinstance.base.instance_admpwd
104
+ if cfg.ce_services.loglevel:
105
+ additional_cfg["loglevel"] = cfg.ce_services.loglevel
106
+ if cfg.ce_services.influxdb.enabled:
107
+ additional_cfg["influxd"] = True
108
+ if cfg.ce_services.hivemq.enabled:
109
+ additional_cfg["hivemq"] = True
110
+ hivemq_options = {
111
+ "hivemq_elements_integration_password": cfg.ce_services.hivemq.elements_integration.password,
112
+ "hivemq_elements_integration_user": cfg.ce_services.hivemq.elements_integration.user,
113
+ }
114
+ for key, value in hivemq_options.items():
115
+ if value:
116
+ additional_cfg[key] = value
117
+
118
+ return additional_cfg
119
+
120
+
121
+ @task(aliases=["ce_services"])
122
+ def ce_services(
123
+ cfg,
124
+ instance: option(
125
+ "-i", # noqa: F821
126
+ "--instance", # noqa: F821
127
+ help="Directory of the CONTACT Elements instance.", # noqa: F722
128
+ ),
129
+ args,
130
+ ):
131
+ """Start the CE services synchronously."""
132
+
133
+ if (
134
+ not Path(os.getenv("CADDOK_BASE", "")).is_dir()
135
+ and not (instance := Path(instance).absolute()).is_dir()
136
+ ):
137
+ die("Can't find the CE instance.")
138
+ if instance:
139
+ setenv(CADDOK_BASE=instance)
140
+
141
+ # Now set the relevant CLI options from cfg, making sure to only add those
142
+ # from cfg that haven't already been set by the CLI.
143
+ all_cli_args = list(args)
144
+ for key, value in extract_service_config(cfg).items():
145
+ cli_option_name = f"--{key}"
146
+ if cli_option_name not in args:
147
+ if cli_option_name in ("--influxd", "--hivemq"):
148
+ all_cli_args.append(cli_option_name)
149
+ else:
150
+ all_cli_args.append(f"{cli_option_name}={value}")
151
+
152
+ if cfg.verbosity == Verbosity.QUIET:
153
+ verbosity_arg = "-q"
154
+ elif cfg.verbosity == Verbosity.INFO:
155
+ verbosity_arg = "-v"
156
+ elif cfg.verbosity == Verbosity.DEBUG:
157
+ verbosity_arg = "-vv"
158
+ else:
159
+ verbosity_arg = None
160
+
161
+ sh("ce_services", verbosity_arg, *all_cli_args)
162
+
163
+
164
+ def provision(cfg): # pylint: disable=too-many-statements
165
+ """
166
+ Provision tools necessary to startup all ce_services.
167
+ """
168
+ import tarfile
169
+ import zipfile
170
+ from tempfile import TemporaryDirectory
171
+
172
+ from csspin import download
173
+
174
+ def extract(archive, extract_to, member=""):
175
+ """Unpacks archives"""
176
+ member = member.replace("\\", "/")
177
+
178
+ if tarfile.is_tarfile(archive):
179
+ extractor = tarfile.open
180
+ mode = "r:gz"
181
+ elif zipfile.is_zipfile(archive):
182
+ extractor = zipfile.ZipFile
183
+ mode = "r"
184
+ else:
185
+ raise KeyError("Unsupported archive type...")
186
+
187
+ with extractor(archive, mode=mode) as arc:
188
+ if isinstance(arc, tarfile.TarFile):
189
+ members = (
190
+ entity
191
+ for entity in arc.getmembers() # pylint: disable=maybe-no-member
192
+ if entity.name.startswith(member)
193
+ )
194
+ elif isinstance(arc, zipfile.ZipFile):
195
+ members = (
196
+ entity
197
+ for entity in arc.namelist() # pylint: disable=maybe-no-member
198
+ if entity.startswith(member)
199
+ )
200
+ else:
201
+ members = ()
202
+
203
+ arc.extractall(
204
+ members=members,
205
+ path=extract_to,
206
+ ) # nosec: tarfile_unsafe_members
207
+
208
+ def install_traefik(cfg):
209
+ version = cfg.ce_services.traefik.version
210
+ traefik_install_dir = cfg.ce_services.traefik.install_dir / version
211
+ mkdir(traefik_install_dir)
212
+
213
+ traefik = traefik_install_dir / f"traefik{cfg.platform.exe}"
214
+
215
+ if not traefik.exists():
216
+ debug("Installing Traefik")
217
+
218
+ archive = (
219
+ f"traefik_v{version}_windows_amd64.zip"
220
+ if sys.platform == "win32"
221
+ else f"traefik_v{version}_linux_amd64.tar.gz"
222
+ )
223
+
224
+ with TemporaryDirectory() as tmp_dir:
225
+ archive_path = Path(tmp_dir) / archive
226
+ download(
227
+ f"https://github.com/traefik/traefik/releases/download/v{version}/{archive}",
228
+ archive_path,
229
+ )
230
+ extract(archive_path, traefik_install_dir, f"traefik{cfg.platform.exe}")
231
+ else:
232
+ debug(f"Using cached traefik ({traefik})")
233
+
234
+ def install_solr(cfg):
235
+ version = cfg.ce_services.solr.version
236
+ install_dir = cfg.ce_services.solr.install_dir
237
+ postfix = cfg.ce_services.solr.version_postfix
238
+ mkdir(install_dir)
239
+
240
+ solr_name = Path(f"solr-{version}{postfix}")
241
+ solr_path = install_dir / solr_name
242
+
243
+ if not solr_path.exists():
244
+ debug("Installing Apache Solr")
245
+ archive = f"{solr_name}.tgz"
246
+
247
+ from csspin import warn # noqa: F401
248
+
249
+ with TemporaryDirectory() as tmp_dir:
250
+ archive_path = Path(tmp_dir) / archive
251
+ try:
252
+ download(
253
+ f"https://dlcdn.apache.org/solr/solr/{version}/{archive}",
254
+ archive_path,
255
+ )
256
+
257
+ except HTTPError as exc:
258
+ if exc.status != 404:
259
+ die(
260
+ f"Could not download Apache Solr {version} from "
261
+ f"dlcdn.apache.org: {exc}"
262
+ )
263
+
264
+ warn(
265
+ "Could not download Apache Solr from dlcdn.apache.org, " # noqa: E501
266
+ "trying archive.apache.org instead."
267
+ )
268
+ try:
269
+ download(
270
+ f"https://archive.apache.org/dist/solr/solr/{version}/{archive}",
271
+ archive_path,
272
+ )
273
+ except HTTPError as exc2:
274
+ die(
275
+ f"Could not download Apache Solr {version} from "
276
+ f"archive.apache.org: {exc2}"
277
+ )
278
+
279
+ extract(archive_path, install_dir, solr_name)
280
+ else:
281
+ debug(f"Using cached Apache Solr ({solr_path})")
282
+
283
+ def install_redis(cfg):
284
+ if sys.platform == "win32":
285
+ redis_install_dir = (
286
+ cfg.ce_services.redis.install_dir / cfg.ce_services.redis.version
287
+ )
288
+ redis = redis_install_dir / "redis-server.exe"
289
+ if not redis.exists():
290
+ mkdir(cfg.ce_services.redis.install_dir)
291
+ debug("Installing redis-server")
292
+ with TemporaryDirectory() as tmp_dir:
293
+ redis_installer_archive = (
294
+ Path(tmp_dir)
295
+ / f"redis-windows-{cfg.ce_services.redis.version}.zip"
296
+ )
297
+ download(
298
+ f"https://github.com/zkteco-home/redis-windows/archive/refs/tags/{cfg.ce_services.redis.version}.zip", # noqa: E501
299
+ redis_installer_archive,
300
+ )
301
+ extract(redis_installer_archive, cfg.ce_services.redis.install_dir)
302
+ (
303
+ cfg.ce_services.redis.install_dir
304
+ / f"redis-windows-{cfg.ce_services.redis.version}"
305
+ ).rename(redis_install_dir)
306
+ else:
307
+ debug(f"Using cached redis-server ({redis})")
308
+
309
+ elif not shutil.which("redis-server"):
310
+ die(
311
+ "Cannot provision redis-server on linux. Please run 'spin system-provision'." # noqa: E501
312
+ )
313
+
314
+ def install_hivemq(cfg):
315
+ def _download(
316
+ url,
317
+ zipfile_name,
318
+ target_directory,
319
+ ignore,
320
+ unpacked_source_directory,
321
+ ):
322
+ """
323
+ Downloads the zip from provided URL and moves the desired content
324
+ into the target directory.
325
+ """
326
+ if exists(target_directory):
327
+ rmtree(target_directory)
328
+ mkdir(target_directory)
329
+
330
+ with TemporaryDirectory() as tmp_dir:
331
+ download(
332
+ url=url,
333
+ location=(download_file := Path(tmp_dir) / zipfile_name),
334
+ )
335
+ extract(download_file, tmp_dir)
336
+
337
+ for f in os.listdir(
338
+ (
339
+ unpacked_source_directory := Path(tmp_dir)
340
+ / unpacked_source_directory
341
+ )
342
+ ):
343
+ if f not in ignore:
344
+ debug(
345
+ "Moving"
346
+ f" {(source := str(unpacked_source_directory / f))}"
347
+ f" -> {(target := str(target_directory))}"
348
+ )
349
+ shutil.move(source, target)
350
+
351
+ hivemq_version = cfg.ce_services.hivemq.version
352
+ hivemq_base_dir = cfg.ce_services.hivemq.install_dir / hivemq_version
353
+ if exists(hivemq_base_dir):
354
+ debug(f"Using cached HiveMQ ({hivemq_base_dir})")
355
+ else:
356
+ debug(f"Installing HiveMQ {hivemq_version}")
357
+ hivemq_zipfile = f"hivemq-ce-{hivemq_version}.zip"
358
+ _download(
359
+ url="https://github.com/hivemq/hivemq-community-edition/releases"
360
+ f"/download/{hivemq_version}/{hivemq_zipfile}",
361
+ zipfile_name=hivemq_zipfile,
362
+ unpacked_source_directory=f"hivemq-ce-{hivemq_version}",
363
+ target_directory=hivemq_base_dir,
364
+ ignore={"data", "log", hivemq_zipfile},
365
+ )
366
+ if sys.platform != "win32":
367
+ from stat import S_IEXEC
368
+
369
+ for f in ("run.sh", "diagnostics.sh"):
370
+ os.chmod(
371
+ (f := hivemq_base_dir / "bin" / f),
372
+ os.stat(f).st_mode | S_IEXEC,
373
+ )
374
+ for f in os.listdir((path := hivemq_base_dir / "bin" / "init-script")):
375
+ os.chmod((f := path / f), os.stat(f).st_mode | S_IEXEC)
376
+
377
+ rmtree(hivemq_base_dir / "extensions" / "hivemq-allow-all-extension")
378
+
379
+ def install_influxdb(cfg):
380
+ version = cfg.ce_services.influxdb.version
381
+ if not (
382
+ influxdb_dir := cfg.ce_services.influxdb.install_dir / version
383
+ ).exists():
384
+ mkdir(influxdb_dir)
385
+ debug(f"Installing InfluxDB {version}")
386
+ archive = (
387
+ f"influxdb-{version}_windows_amd64.zip"
388
+ if sys.platform == "win32"
389
+ else f"influxdb-{version}_linux_amd64.tar.gz"
390
+ )
391
+
392
+ with TemporaryDirectory() as tmp_dir:
393
+ download(
394
+ f"https://dl.influxdata.com/influxdb/releases/{archive}",
395
+ (archive_path := Path(tmp_dir) / archive),
396
+ )
397
+ extract(archive_path, tmp_dir)
398
+
399
+ if (
400
+ sources := Path(tmp_dir) / f"influxdb-{version}-1"
401
+ ) and sys.platform == "win32":
402
+ for f in os.listdir(sources):
403
+ debug(
404
+ "Moving" f" {(source := sources / f)}" f" -> {influxdb_dir}"
405
+ )
406
+ shutil.move(source, influxdb_dir)
407
+ else:
408
+ from stat import S_IEXEC
409
+
410
+ for f in os.listdir((sources := sources / "usr" / "bin")):
411
+ debug("Moving" f" {(source := sources / f)} -> {influxdb_dir}")
412
+ shutil.move(source, influxdb_dir)
413
+ os.chmod((f := influxdb_dir / f), os.stat(f).st_mode | S_IEXEC)
414
+ else:
415
+ debug(f"Using cached InfluxDB ({influxdb_dir})")
416
+
417
+ install_traefik(cfg)
418
+ install_redis(cfg)
419
+
420
+ if not cfg.ce_services.solr.use:
421
+ install_solr(cfg)
422
+
423
+ if cfg.ce_services.hivemq.enabled:
424
+ install_hivemq(cfg)
425
+
426
+ if cfg.ce_services.influxdb.enabled:
427
+ install_influxdb(cfg)
428
+
429
+
430
+ def init(cfg):
431
+ """
432
+ Set all provisioned tools into the PATH variable.
433
+ """
434
+ path_extensions = {
435
+ cfg.ce_services.traefik.install_dir / cfg.ce_services.traefik.version,
436
+ }
437
+
438
+ if cfg.ce_services.solr.use:
439
+ from shutil import which # noqa: F401
440
+
441
+ solr_path = which(cfg.ce_services.solr.use)
442
+ if solr_path is None:
443
+ if Path(solr_path).exists():
444
+ path_extensions.add(solr_path)
445
+ else:
446
+ die(
447
+ f"Cannot find Solr executable: {cfg.ce_services.solr.use}. "
448
+ "Please check your configuration."
449
+ )
450
+
451
+ else:
452
+ path_extensions.add(
453
+ cfg.ce_services.solr.install_dir
454
+ / f"solr-{cfg.ce_services.solr.version}{cfg.ce_services.solr.version_postfix}"
455
+ / "bin"
456
+ )
457
+
458
+ if sys.platform == "win32":
459
+ path_extensions.add(
460
+ cfg.ce_services.redis.install_dir / cfg.ce_services.redis.version
461
+ )
462
+
463
+ if cfg.ce_services.influxdb.enabled:
464
+ path_extensions.add(
465
+ cfg.ce_services.influxdb.install_dir / cfg.ce_services.influxdb.version
466
+ )
467
+
468
+ if cfg.ce_services.hivemq.enabled:
469
+ hivemq_intgr_dir = cfg.ce_services.hivemq.elements_integration.install_dir
470
+
471
+ if not hivemq_intgr_dir or not exists(hivemq_intgr_dir):
472
+ die(
473
+ "CONTACT Elements HiveMQ Integration installation directory"
474
+ f" does not exist. ({hivemq_intgr_dir})"
475
+ )
476
+
477
+ setenv(
478
+ HIVEMQ_HOME=cfg.ce_services.hivemq.install_dir
479
+ / cfg.ce_services.hivemq.version
480
+ )
481
+ setenv(
482
+ HIVEMQ_EXTENSION_FOLDER=cfg.ce_services.hivemq.elements_integration.install_dir
483
+ )
484
+
485
+ setenv(
486
+ PATH=f"{os.pathsep.join([str(e) for e in path_extensions])}{os.pathsep}{os.getenv('PATH', '')}"
487
+ )
@@ -0,0 +1,105 @@
1
+ # -*- mode: yaml; coding: utf-8 -*-
2
+ #
3
+ # Schema for the ce_services plugin for csspin
4
+
5
+ ce_services:
6
+ type: object
7
+ help: |
8
+ The ce_services plugin equips csspin with the ability to provision,
9
+ start, stop or even control services. These services are e.g. traefik,
10
+ solr, redis and many more.
11
+ properties:
12
+ hivemq:
13
+ type: object
14
+ help: |
15
+ Configuration regarding the HiveMQ service
16
+ properties:
17
+ enabled:
18
+ type: bool
19
+ help: If enabled, the hivemq service will be started.
20
+ version:
21
+ type: str
22
+ help: The version of HiveMQ to use.
23
+ install_dir:
24
+ type: path
25
+ help: The installation directory of hivemq.
26
+ elements_integration:
27
+ type: object
28
+ help: |
29
+ Configuration regarding the CONTACT Elements
30
+ Integration for HiveMQ
31
+ properties:
32
+ user:
33
+ type: str
34
+ help: |
35
+ The default user to use when accessing the
36
+ CONTACT Elements Integration
37
+ password:
38
+ type: secret
39
+ help: The password corresponding to the user
40
+ install_dir:
41
+ type: path
42
+ help: |
43
+ The installation directory of the HiveMQ CONTACT
44
+ Elements integration.
45
+ influxdb:
46
+ type: object
47
+ help: Configuration regarding the influxdb service.
48
+ properties:
49
+ enabled:
50
+ type: bool
51
+ help: If enabled, the influxdb service will be started.
52
+ version:
53
+ type: str
54
+ help: The version of InfluxDB to use.
55
+ install_dir:
56
+ type: path
57
+ help: Installation directory of influxdb.
58
+ traefik:
59
+ type: object
60
+ help: Configuration regarding the traefik service.
61
+ properties:
62
+ version:
63
+ type: str
64
+ help: The version of Traefik to use.
65
+ dashboard_port:
66
+ type: str
67
+ help: |
68
+ The port at which the Traefik dashboard should be
69
+ exposed.
70
+ install_dir:
71
+ type: path
72
+ help: Traefik's installation directory.
73
+ solr:
74
+ type: object
75
+ help: Configuration regarding the Apache Solr service.
76
+ properties:
77
+ version:
78
+ type: str
79
+ help: The version of Apache Solr to use.
80
+ install_dir:
81
+ type: path
82
+ help: The installation directory of Apache Solr.
83
+ version_postfix:
84
+ type: str
85
+ help: The Solr version postfix.
86
+ use:
87
+ type: path
88
+ help: |
89
+ The use property can be used to specify an alternative
90
+ Apache Solr source to use. This is useful to rather use
91
+ the system Apache Solr than the one defined in
92
+ spinfile.yaml or ``ce_services.solr.install_dir``.
93
+ redis:
94
+ type: object
95
+ help: Configuration regarding redis
96
+ properties:
97
+ version:
98
+ type: str
99
+ help: The redis version to use.
100
+ install_dir:
101
+ type: path
102
+ help: The installation directory of redis.
103
+ loglevel:
104
+ type: str
105
+ help: The loglevel for the started services.
@@ -0,0 +1,58 @@
1
+ # -*- mode: python; coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2025 CONTACT Software GmbH
4
+ # https://www.contact-software.com/
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ """
19
+ Spin plugin for the ce_support_tools.
20
+ """
21
+
22
+ import os
23
+
24
+ from csspin import config, die, option, setenv, sh, task
25
+ from path import Path
26
+
27
+ defaults = config(
28
+ requires=config(
29
+ python=["ce-support-tools"],
30
+ spin=[
31
+ "csspin_ce.mkinstance",
32
+ ],
33
+ ),
34
+ )
35
+
36
+
37
+ @task()
38
+ def pyperf(
39
+ cfg, # pylint: disable=unused-argument
40
+ instancedir: option("-D", "--instancedir", required=False, type=str), # noqa: F821
41
+ help: option("--help", is_flag=True), # pylint: disable=redefined-builtin
42
+ args,
43
+ ):
44
+ """
45
+ Run the pyperf tool with the given arguments.
46
+ """
47
+ if help:
48
+ args = (*args, "--help")
49
+ if (
50
+ not Path(os.getenv("CADDOK_BASE", "")).is_dir()
51
+ and not (instancedir := Path(instancedir).absolute()).is_dir()
52
+ ):
53
+ die("Can't find the CE instance.")
54
+ if instancedir:
55
+ setenv(CADDOK_BASE=instancedir)
56
+ if args is None or len(args) == 0:
57
+ args = ("--help",)
58
+ sh("powerscript", "-m", "ce.support.pyperf", *args)
@@ -0,0 +1,8 @@
1
+ # -*- mode: yaml; coding: utf-8 -*-
2
+ #
3
+ # Schema for the ce_support_tools plugin for csspin
4
+
5
+ ce_support_tools:
6
+ type: object
7
+ help: |
8
+ The ce_support_tools plugin for csspin