jaseci 1.4.0.9__py3-none-any.whl → 1.4.0.11__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.

Potentially problematic release.


This version of jaseci might be problematic. Click here for more details.

Files changed (87) hide show
  1. jaseci/VERSION +1 -1
  2. jaseci/__init__.py +3 -0
  3. jaseci/actions/standard/elastic.py +3 -2
  4. jaseci/actions/standard/mail.py +3 -2
  5. jaseci/actions/standard/std.py +3 -2
  6. jaseci/actions/standard/stripe.py +3 -2
  7. jaseci/actions/standard/task.py +3 -5
  8. jaseci/actions/standard/tests/test_mail_lib.py +8 -7
  9. jaseci/actions/tests/test_std.py +4 -5
  10. jaseci/actor/walker.py +6 -3
  11. jaseci/api/config_api.py +3 -2
  12. jaseci/api/jac_api.py +2 -2
  13. jaseci/api/jsorc_api.py +60 -121
  14. jaseci/api/prometheus_api.py +14 -20
  15. jaseci/api/queue_api.py +9 -5
  16. jaseci/api/tests/test_global_api.py +3 -3
  17. jaseci/api/tests/test_logger_api.py +3 -3
  18. jaseci/api/user_api.py +3 -3
  19. jaseci/api/webhook_api.py +6 -4
  20. jaseci/attr/action.py +10 -4
  21. jaseci/element/master.py +2 -0
  22. jaseci/element/super_master.py +2 -0
  23. jaseci/hook/memory.py +3 -1
  24. jaseci/hook/redis.py +5 -4
  25. jaseci/jac/interpreter/interp.py +16 -4
  26. jaseci/jac/tests/test_book.py +2 -2
  27. jaseci/jsctl/jsctl.py +48 -15
  28. jaseci/jsctl/tests/test_jsctl.py +5 -0
  29. jaseci/jsorc.py +733 -0
  30. jaseci/jsorc_settings.py +184 -0
  31. jaseci/manifests/database.yaml +107 -0
  32. jaseci/manifests/elastic.yaml +5923 -0
  33. jaseci/manifests/prometheus.yaml +1273 -0
  34. jaseci/{svc/jsorc-backup/jaseci-redis.yaml → manifests/redis.yaml} +20 -0
  35. jaseci/svc/__init__.py +0 -25
  36. jaseci/svc/{elastic/elastic.py → elastic_svc.py} +5 -16
  37. jaseci/svc/kube_svc.py +240 -0
  38. jaseci/svc/{mail/mail.py → mail_svc.py} +14 -17
  39. jaseci/svc/{prometheus/prometheus.py → prome_svc.py} +5 -16
  40. jaseci/svc/{redis/redis.py → redis_svc.py} +14 -26
  41. jaseci/svc/{stripe/stripe.py → stripe_svc.py} +4 -7
  42. jaseci/svc/{task/task.py → task_svc.py} +27 -24
  43. jaseci/svc/{task/common.py → tasks.py} +287 -293
  44. jaseci/tests/jac_test_progs.py +21 -0
  45. jaseci/tests/test_core.py +14 -15
  46. jaseci/tests/test_jac.py +59 -60
  47. jaseci/tests/test_node.py +6 -13
  48. jaseci/tests/test_progs.py +74 -52
  49. jaseci/tests/test_stripe.py +6 -10
  50. jaseci/utils/actions/actions_manager.py +254 -0
  51. jaseci/{svc/actions_optimizer → utils/actions}/actions_optimizer.py +9 -19
  52. jaseci/utils/json_handler.py +2 -3
  53. jaseci/utils/test_core.py +4 -5
  54. jaseci/utils/utils.py +12 -0
  55. {jaseci-1.4.0.9.dist-info → jaseci-1.4.0.11.dist-info}/METADATA +2 -1
  56. {jaseci-1.4.0.9.dist-info → jaseci-1.4.0.11.dist-info}/RECORD +63 -80
  57. jaseci/svc/common.py +0 -763
  58. jaseci/svc/config.py +0 -9
  59. jaseci/svc/elastic/__init__.py +0 -3
  60. jaseci/svc/elastic/config.py +0 -8
  61. jaseci/svc/elastic/manifest.py +0 -1
  62. jaseci/svc/jsorc-backup/jsorc.py +0 -182
  63. jaseci/svc/jsorc-backup/promon/__init__.py +0 -0
  64. jaseci/svc/jsorc-backup/promon/promon.py +0 -202
  65. jaseci/svc/mail/__init__.py +0 -4
  66. jaseci/svc/mail/config.py +0 -25
  67. jaseci/svc/meta.py +0 -164
  68. jaseci/svc/postgres/__init__.py +0 -0
  69. jaseci/svc/postgres/manifest.py +0 -106
  70. jaseci/svc/prometheus/__init__.py +0 -5
  71. jaseci/svc/prometheus/config.py +0 -11
  72. jaseci/svc/prometheus/manifest.py +0 -1102
  73. jaseci/svc/redis/__init__.py +0 -5
  74. jaseci/svc/redis/config.py +0 -10
  75. jaseci/svc/redis/manifest.py +0 -65
  76. jaseci/svc/state.py +0 -17
  77. jaseci/svc/stripe/__init__.py +0 -3
  78. jaseci/svc/stripe/config.py +0 -7
  79. jaseci/svc/task/__init__.py +0 -5
  80. jaseci/svc/task/config.py +0 -17
  81. /jaseci/{svc/actions_optimizer → manifests}/__init__.py +0 -0
  82. /jaseci/{svc/jsorc-backup → utils/actions}/__init__.py +0 -0
  83. /jaseci/{svc/actions_optimizer → utils/actions}/actions_state.py +0 -0
  84. {jaseci-1.4.0.9.dist-info → jaseci-1.4.0.11.dist-info}/LICENSE +0 -0
  85. {jaseci-1.4.0.9.dist-info → jaseci-1.4.0.11.dist-info}/WHEEL +0 -0
  86. {jaseci-1.4.0.9.dist-info → jaseci-1.4.0.11.dist-info}/entry_points.txt +0 -0
  87. {jaseci-1.4.0.9.dist-info → jaseci-1.4.0.11.dist-info}/top_level.txt +0 -0
jaseci/svc/common.py DELETED
@@ -1,763 +0,0 @@
1
- from copy import deepcopy
2
- from multiprocessing import Process
3
-
4
- from kubernetes import config as kubernetes_config
5
- from kubernetes.client import ApiClient, CoreV1Api, AppsV1Api, RbacAuthorizationV1Api
6
- from kubernetes.client.rest import ApiException
7
-
8
- from jaseci.utils.utils import logger
9
- from jaseci.actions.live_actions import load_action_config
10
- from jaseci.svc.actions_optimizer.actions_optimizer import ActionsOptimizer
11
- from .state import ServiceState as Ss
12
- from .config import META_CONFIG, KUBERNETES_CONFIG
13
-
14
- import time
15
- import numpy as np
16
-
17
- ###################################################
18
- # UNSAFE PARAMS #
19
- ###################################################
20
-
21
- UNSAFE_PARAPHRASE = "I know what I'm doing!"
22
- UNSAFE_KINDS = ["PersistentVolumeClaim"]
23
-
24
- COMMON_ERROR = "Not properly configured!"
25
- DEFAULT_CONFIG = {"enabled": False}
26
-
27
-
28
- class CommonService:
29
- _daemon = {}
30
-
31
- def __init__(self, hook=None):
32
- self.app = None
33
- self.enabled = False
34
- self.state = Ss.NOT_STARTED
35
- self.quiet = True
36
- self.build_settings(hook)
37
-
38
- ###################################################
39
- # PROPERTIES #
40
- ###################################################
41
-
42
- # ------------------- DAEMON -------------------- #
43
-
44
- @property
45
- def daemon(self):
46
- return __class__._daemon
47
-
48
- ###################################################
49
- # BUILDER #
50
- ###################################################
51
-
52
- def start(self, hook=None):
53
- try:
54
- if self.enabled and self.is_ready():
55
- self.state = Ss.STARTED
56
- self.run(hook)
57
- self.state = Ss.RUNNING
58
- self.post_run(hook)
59
- except Exception as e:
60
- if not (self.quiet):
61
- logger.error(
62
- f"Skipping {self.__class__.__name__} due to initialization "
63
- f"failure!\n{e.__class__.__name__}: {e}"
64
- )
65
- self.failed()
66
-
67
- return self
68
-
69
- def run(self, hook=None):
70
- raise Exception(f"{COMMON_ERROR} Please override run method!")
71
-
72
- def post_run(self, hook=None):
73
- pass
74
-
75
- ###################################################
76
- # COMMONS #
77
- ###################################################
78
-
79
- def poke(self, msg: str = None):
80
- if self.is_running():
81
- return self.app
82
- raise Exception(
83
- msg or f"{self.__class__.__name__} is disabled or not yet configured!"
84
- )
85
-
86
- def is_ready(self):
87
- return self.state.is_ready() and self.app is None
88
-
89
- def is_running(self):
90
- return self.state.is_running() and not (self.app is None)
91
-
92
- def has_failed(self):
93
- return self.state.has_failed()
94
-
95
- def build_settings(self, hook) -> dict:
96
- try:
97
- self.manifest = self.build_manifest(hook)
98
- self.manifest_meta = {}
99
- if self.manifest:
100
- self.manifest_meta["__OLD_CONFIG__"] = self.manifest.pop(
101
- "__OLD_CONFIG__", {}
102
- )
103
- self.manifest_meta["__UNSAFE_PARAPHRASE__"] = self.manifest.pop(
104
- "__UNSAFE_PARAPHRASE__", ""
105
- )
106
-
107
- config = self.build_config(hook)
108
- self.enabled = config.pop("enabled", False)
109
- self.quiet = config.pop("quiet", False)
110
- self.config = config
111
- except Exception:
112
- logger.exception(f"Error loading settings for {self.__class__}")
113
- self.config = DEFAULT_CONFIG
114
- self.manifest = None
115
- self.manifest_meta = {}
116
-
117
- def build_config(self, hook) -> dict:
118
- return DEFAULT_CONFIG
119
-
120
- def build_manifest(self, hook) -> dict:
121
- pass
122
-
123
- # ------------------- DAEMON -------------------- #
124
-
125
- def spawn_daemon(self, **targets):
126
- for name, target in targets.items():
127
- dae: Process = self.daemon.get(name)
128
- if not dae or not dae.is_alive():
129
- process = Process(target=target, daemon=True)
130
- process.start()
131
- self.daemon[name] = process
132
-
133
- def terminate_daemon(self, *names):
134
- for name in names:
135
- dae: Process = self.daemon.pop(name, None)
136
- if not (dae is None) and dae.is_alive():
137
- logger.info(f"Terminating {name} ...")
138
- dae.terminate()
139
-
140
- ###################################################
141
- # CLEANER #
142
- ###################################################
143
-
144
- def reset(self, hook, start=True):
145
- self.app = None
146
- self.state = Ss.NOT_STARTED
147
- self.__init__(hook)
148
-
149
- if start:
150
- self.start(hook)
151
-
152
- def failed(self):
153
- self.app = None
154
- self.state = Ss.FAILED
155
-
156
-
157
- class ProxyService(CommonService):
158
- def __init__(self):
159
- super().__init__(__class__)
160
-
161
-
162
- class Kube:
163
- def __init__(self, in_cluster: bool, config: dict):
164
- if in_cluster:
165
- kubernetes_config.load_incluster_config()
166
- else:
167
- kubernetes_config.load_kube_config()
168
-
169
- self.client = ApiClient(config)
170
- self.core = CoreV1Api(config)
171
- self.api = AppsV1Api(self.client)
172
- self.auth = RbacAuthorizationV1Api(self.client)
173
- self.ping()
174
- self.defaults()
175
-
176
- def ping(self):
177
- res = self.client.call_api("/readyz", "GET")
178
- return res[1] == 200
179
-
180
- def create(self, api, namespace, conf):
181
- if api.startswith("ClusterRole"):
182
- self.create_apis[api](body=conf)
183
- else:
184
- self.create_apis[api](namespace=namespace, body=conf)
185
-
186
- def patch(self, api, name, namespace, conf):
187
- if api.startswith("ClusterRole"):
188
- self.patch_apis[api](name=name, body=conf)
189
- else:
190
- self.patch_apis[api](name=name, namespace=namespace, body=conf)
191
-
192
- def read(self, api: str, name: str, namespace: str = None):
193
- if api.startswith("ClusterRole"):
194
- return self.read_apis[api](name=name)
195
- else:
196
- return self.read_apis[api](name=name, namespace=namespace)
197
-
198
- def delete(self, api: str, name: str, namespace: str = None):
199
- if api.startswith("ClusterRole"):
200
- return self.delete_apis[api](name=name)
201
- else:
202
- return self.delete_apis[api](name=name, namespace=namespace)
203
-
204
- def is_running(self, name: str, namespace: str):
205
- try:
206
- return (
207
- self.core.list_namespaced_pod(
208
- namespace=namespace, label_selector=f"pod={name}"
209
- )
210
- .items[0]
211
- .status.phase
212
- == "Running"
213
- )
214
- except Exception:
215
- return False
216
-
217
- def defaults(self):
218
- self.create_apis = {
219
- "Service": self.core.create_namespaced_service,
220
- "Deployment": self.api.create_namespaced_deployment,
221
- "ConfigMap": self.core.create_namespaced_config_map,
222
- "ServiceAccount": self.core.create_namespaced_service_account,
223
- "ClusterRole": self.auth.create_cluster_role,
224
- "ClusterRoleBinding": self.auth.create_cluster_role_binding,
225
- "Secret": self.core.create_namespaced_secret,
226
- "PersistentVolumeClaim": (
227
- self.core.create_namespaced_persistent_volume_claim
228
- ),
229
- "DaemonSet": self.api.create_namespaced_daemon_set,
230
- }
231
- self.patch_apis = {
232
- "Service": self.core.patch_namespaced_service,
233
- "Deployment": self.api.patch_namespaced_deployment,
234
- "ConfigMap": self.core.patch_namespaced_config_map,
235
- "ServiceAccount": self.core.patch_namespaced_service_account,
236
- "ClusterRole": self.auth.patch_cluster_role,
237
- "ClusterRoleBinding": self.auth.patch_cluster_role_binding,
238
- "Secret": self.core.patch_namespaced_secret,
239
- "PersistentVolumeClaim": (
240
- self.core.patch_namespaced_persistent_volume_claim
241
- ),
242
- "DaemonSet": self.api.patch_namespaced_daemon_set,
243
- }
244
- self.delete_apis = {
245
- "Service": self.core.delete_namespaced_service,
246
- "Deployment": self.api.delete_namespaced_deployment,
247
- "ConfigMap": self.core.delete_namespaced_config_map,
248
- "ServiceAccount": self.core.delete_namespaced_service_account,
249
- "ClusterRole": self.auth.delete_cluster_role,
250
- "ClusterRoleBinding": self.auth.delete_cluster_role_binding,
251
- "Secret": self.core.delete_namespaced_secret,
252
- "PersistentVolumeClaim": (
253
- self.core.delete_namespaced_persistent_volume_claim
254
- ),
255
- "DaemonSet": self.api.delete_namespaced_daemon_set,
256
- }
257
- self.read_apis = {
258
- "Service": self.core.read_namespaced_service,
259
- "Endpoints": self.core.read_namespaced_endpoints,
260
- "Deployment": self.api.read_namespaced_deployment,
261
- "ConfigMap": self.core.read_namespaced_config_map,
262
- "ServiceAccount": self.core.read_namespaced_service_account,
263
- "ClusterRole": self.auth.read_cluster_role,
264
- "ClusterRoleBinding": self.auth.read_cluster_role_binding,
265
- "Secret": self.core.read_namespaced_secret,
266
- "PersistentVolumeClaim": self.core.read_namespaced_persistent_volume_claim,
267
- "DaemonSet": self.api.read_namespaced_daemon_set,
268
- }
269
-
270
-
271
- class JsOrc:
272
- def __init__(self, meta):
273
- self.automated = False
274
- self.meta = meta
275
- self.services = {}
276
- self.background = {}
277
- self.context = {}
278
- self.benchmark = {
279
- "jsorc": {"active": False, "requests": {}},
280
- "actions_optimizer": {"active": False, "requests": {}},
281
- }
282
- self.actions_history = {"active": False, "history": []}
283
- self.actions_calls = {}
284
- self.system_states = {"active": False, "states": []}
285
- self.actions_optimizer = ActionsOptimizer(
286
- benchmark=self.benchmark["actions_optimizer"],
287
- actions_history=self.actions_history,
288
- actions_calls=self.actions_calls,
289
- )
290
-
291
- ###################################################
292
- # BUILDER #
293
- ###################################################
294
-
295
- def build(self):
296
- try:
297
- hook = self.build_context("hook")
298
- config = hook.service_glob("META_CONFIG", META_CONFIG)
299
- if config.pop("automation", False):
300
- self.kubernetes = Kube(**config.pop("kubernetes", KUBERNETES_CONFIG))
301
- self.actions_optimizer.kube = self.kubernetes
302
- self.prometheus = self.meta.get_service("promon", hook)
303
- self.backoff_interval = config.pop("backoff_interval", 10)
304
- self.namespace = config.pop("namespace", "default")
305
- self.actions_optimizer.namespace = self.namespace
306
- self.keep_alive = config.pop(
307
- "keep_alive", ["promon", "redis", "task", "mail"]
308
- )
309
- self.automated = True
310
- else:
311
- self.automated = False
312
- except Exception:
313
- self.automated = False
314
-
315
- def interval_check(self):
316
- hook = self.meta.build_hook()
317
- for svc in self.keep_alive:
318
- try:
319
- self.check(self.namespace, svc, hook)
320
- except Exception as e:
321
- logger.exception(
322
- f"Error checking {svc} !\n" f"{e.__class__.__name__}: {e}"
323
- )
324
- self.optimize(jsorc_interval=self.backoff_interval)
325
- self.record_system_state()
326
-
327
- ###################################################
328
- # KUBERNETES #
329
- ###################################################
330
-
331
- def in_cluster(self):
332
- """
333
- Check if JSORC/Jaseci is running in a kubernetes cluster
334
- """
335
- try:
336
- if not hasattr(self, "kubernetes"):
337
- return False
338
- return self.kubernetes.ping()
339
- except ApiException as e:
340
- logger.info(f"Kubernetes cluster environment check failed: {e}")
341
- return False
342
-
343
- def create(self, kind: str, name: str, namespace: str, conf: dict):
344
- try:
345
- logger.info(f"Creating {kind} for `{name}` with namespace `{namespace}`")
346
- self.kubernetes.create(kind, namespace, conf)
347
- except ApiException as e:
348
- logger.error(
349
- f"Error creating {kind} for `{name}` with namespace `{namespace}` -- {e}"
350
- )
351
-
352
- def patch(self, kind: str, name: str, namespace: str, conf: dict):
353
- try:
354
- logger.info(f"Patching {kind} for `{name}` with namespace `{namespace}`")
355
- self.kubernetes.patch(kind, name, namespace, conf)
356
- except ApiException as e:
357
- logger.error(
358
- f"Error patching {kind} for `{name}` with namespace `{namespace}` -- {e}"
359
- )
360
-
361
- def read(self, kind: str, name: str, namespace: str):
362
- try:
363
- logger.info(f"Retrieving {kind} for `{name}` with namespace `{namespace}`")
364
- return self.kubernetes.read(kind, name, namespace=namespace)
365
- except ApiException as e:
366
- logger.error(
367
- f"Error retrieving {kind} for `{name}` with namespace `{namespace}` -- {e}"
368
- )
369
- return e
370
-
371
- def delete(self, kind: str, name: str, namespace: str):
372
- try:
373
- logger.info(f"Deleting {kind} for `{name}` with namespace `{namespace}`")
374
- return self.kubernetes.delete(kind, name, namespace=namespace)
375
- except ApiException as e:
376
- logger.error(
377
- f"Error deleting {kind} for `{name}` with namespace `{namespace}` -- {e}"
378
- )
379
- return e
380
-
381
- def check(self, namespace, svc_name, hook):
382
- svc = self.meta.get_service(svc_name, hook)
383
-
384
- if not svc.is_running():
385
- if svc.manifest:
386
- config_map = svc.manifest
387
- pod_name = ""
388
- old_config_map = deepcopy(svc.manifest_meta.get("__OLD_CONFIG__", {}))
389
- unsafe_paraphrase = svc.manifest_meta.get("__UNSAFE_PARAPHRASE__", "")
390
- for kind, confs in config_map.items():
391
- for conf in confs:
392
- name = conf["metadata"]["name"]
393
- names = old_config_map.get(kind, [])
394
- if name in names:
395
- names.remove(name)
396
-
397
- if kind == "Service":
398
- pod_name = name
399
- res = self.read(kind, name, namespace)
400
- if hasattr(res, "status") and res.status == 404 and conf:
401
- self.create(kind, name, namespace, conf)
402
- elif not isinstance(res, ApiException) and res.metadata:
403
- if res.metadata.labels:
404
- config_version = res.metadata.labels.get(
405
- "config_version", 1
406
- )
407
- else:
408
- config_version = 1
409
-
410
- if config_version != conf.get("metadata").get(
411
- "labels", {}
412
- ).get("config_version", 1):
413
- self.patch(kind, name, namespace, conf)
414
-
415
- if (
416
- old_config_map
417
- and type(old_config_map) is dict
418
- and kind in old_config_map
419
- and name in old_config_map[kind]
420
- ):
421
- old_config_map.get(kind, []).remove(name)
422
-
423
- for to_be_removed in old_config_map.get(kind, []):
424
- res = self.read(kind, to_be_removed, namespace)
425
- if not isinstance(res, ApiException) and res.metadata:
426
- if (
427
- kind not in UNSAFE_KINDS
428
- or unsafe_paraphrase == UNSAFE_PARAPHRASE
429
- ):
430
- self.delete(kind, to_be_removed, namespace)
431
- else:
432
- logger.info(
433
- f"You don't have permission to delete `{kind}` for `{to_be_removed}` with namespace `{namespace}`!"
434
- )
435
-
436
- if self.kubernetes.is_running(pod_name, namespace):
437
- logger.info(
438
- f"Pod state is running. Trying to Restart {svc_name}..."
439
- )
440
- svc.reset(hook)
441
- else:
442
- logger.info(f"Restarting {svc_name}...")
443
- svc.reset(hook)
444
-
445
- ###################################################
446
- # SERVICE HANDLER #
447
- ###################################################
448
-
449
- def add_service_builder(self, name, svc):
450
- if self.services.get(name):
451
- raise Exception(f"{name} already exists!")
452
-
453
- self.services[name] = svc
454
-
455
- def build_service(self, name, background, *args, **kwargs):
456
- svc = self.services.get(name)
457
-
458
- if not svc:
459
- logger.error(f"Service {name} is not yet set!")
460
- return None
461
-
462
- svc = svc(*args, **kwargs)
463
-
464
- if background:
465
- self.background[name] = svc
466
-
467
- return svc
468
-
469
- def get_service(self, name, *args, **kwargs):
470
- svc = self.background.get(name)
471
-
472
- if not svc:
473
- return self.build_service(name, True, *args, **kwargs)
474
-
475
- return svc
476
-
477
- ###################################################
478
- # CONTEXT HANDLER #
479
- ###################################################
480
-
481
- def add_context(self, name, cls, *args, **kwargs):
482
- self.context[name] = {"class": cls, "args": args, "kwargs": kwargs}
483
-
484
- def build_context(self, ctx, *args, **kwargs):
485
- ctx = self.context[ctx]
486
- return ctx["class"](*args, *ctx["args"], **kwargs, **ctx["kwargs"])
487
-
488
- ###################################################
489
- # ACTION MANAGER #
490
- ###################################################
491
- def load_action_config(self, config, name):
492
- """
493
- Load the config for an action
494
- """
495
- return load_action_config(config, name)
496
-
497
- def load_actions(self, name, mode):
498
- """
499
- Load an action as local, module or remote.
500
- """
501
- # Using module for local
502
- mode = "module" if mode == "local" else mode
503
-
504
- if mode == "module":
505
- self.actions_optimizer.load_action_module(name)
506
- elif mode == "remote":
507
- self.actions_optimizer.load_action_remote(name)
508
-
509
- def unload_actions(self, name, mode, retire_svc):
510
- """
511
- Unload an action
512
- """
513
- # We are using module for local
514
- mode = "module" if mode == "local" else mode
515
- if mode == "auto":
516
- res = self.actions_optimizer.unload_action_auto(name)
517
- if not res[0]:
518
- return res
519
- if retire_svc:
520
- self.retire_uservice(name)
521
- return res
522
- elif mode == "module":
523
- return self.actions_optimizer.unload_action_module(name)
524
- elif mode == "remote":
525
- res = self.actions_optimizer.unload_action_remote(name)
526
- if not res[0]:
527
- return res
528
- if retire_svc:
529
- self.retire_uservice(name)
530
- return res
531
- else:
532
- return (False, f"Unrecognized action mode {mode}.")
533
-
534
- def retire_uservice(self, name):
535
- """
536
- Retire a remote microservice for the action.
537
- """
538
- self.actions_optimizer.retire_remote(name)
539
-
540
- def get_actions_status(self, name=""):
541
- """
542
- Return the status of the action
543
- """
544
- return self.actions_optimizer.get_actions_status(name)
545
-
546
- def actions_tracking_start(self):
547
- """ """
548
- self.actions_history["active"] = True
549
- self.actions_history["history"] = [{"ts": time.time()}]
550
- self.actions_calls.clear()
551
-
552
- def actions_tracking_stop(self):
553
- """ """
554
- if not self.actions_history["active"]:
555
- return []
556
-
557
- self.actions_optimizer.summarize_action_calls()
558
-
559
- return self.actions_history["history"]
560
-
561
- def benchmark_start(self):
562
- """
563
- Put JSORC under benchmark mode.
564
- """
565
- self.benchmark["jsorc"]["active"] = True
566
- self.benchmark["jsorc"]["requests"] = {}
567
- self.benchmark["jsorc"]["start_ts"] = time.time()
568
-
569
- def state_tracking_start(self):
570
- """
571
- Ask JSORC to start tracking the state of the system as observed by JSORC on every interval.
572
- """
573
- self.system_states = {"active": True, "states": []}
574
-
575
- def state_tracking_stop(self):
576
- """
577
- Stop state tracking for JSORC
578
- """
579
- ret = self.system_states
580
- self.system_states = {"active": True, "states": []}
581
- return ret
582
-
583
- def state_tracking_report(self):
584
- """
585
- Return state tracking history so far
586
- """
587
- return self.system_states
588
-
589
- def record_system_state(self):
590
- """
591
- Record system state
592
- """
593
- if self.system_states["active"]:
594
- ts = int(time.time())
595
- prom_profile = self.prometheus.info(
596
- namespace=self.namespace,
597
- exclude_prom=True,
598
- timestamp=ts,
599
- duration=self.backoff_interval,
600
- )
601
- self.system_states["states"].append(
602
- {
603
- "ts": ts,
604
- "actions": self.get_actions_status(name=""),
605
- "prometheus": prom_profile,
606
- }
607
- )
608
-
609
- def benchmark_stop(self, report):
610
- """
611
- Stop benchmark mode and report result during the benchmark period
612
- """
613
- if not self.benchmark["jsorc"]["active"]:
614
- return {}
615
-
616
- res = self.benchmark_report()
617
- self.benchmark["jsorc"]["requests"] = {}
618
- self.benchmark["jsorc"]["active"] = False
619
-
620
- if report:
621
- return res
622
- else:
623
- return {}
624
-
625
- def benchmark_report(self):
626
- """
627
- Summarize benchmark results and report.
628
- """
629
- summary = {}
630
- duration = time.time() - self.benchmark["jsorc"]["start_ts"]
631
- for request, data in self.benchmark["jsorc"]["requests"].items():
632
- summary[request] = {}
633
- all_reqs = []
634
- for req_name, times in data.items():
635
- if len(times) == 0:
636
- continue
637
- all_reqs.extend(times)
638
- summary[request][req_name] = {
639
- "throughput": len(times) / duration,
640
- "average_latency": sum(times) / len(times) * 1000,
641
- "50th_latency": np.percentile(times, 50) * 1000,
642
- "90th_latency": np.percentile(times, 90) * 1000,
643
- "95th_latency": np.percentile(times, 95) * 1000,
644
- "99th_latency": np.percentile(times, 99) * 1000,
645
- }
646
- summary[request]["all"] = {
647
- "throughput": len(all_reqs) / duration,
648
- "average_latency": sum(all_reqs) / len(all_reqs) * 1000,
649
- "50th_latency": np.percentile(all_reqs, 50) * 1000,
650
- "90th_latency": np.percentile(all_reqs, 90) * 1000,
651
- "95th_latency": np.percentile(all_reqs, 95) * 1000,
652
- "99th_latency": np.percentile(all_reqs, 99) * 1000,
653
- }
654
-
655
- return summary
656
-
657
- def record_state(self):
658
- """
659
- Record the current state of the system observed by JSORC
660
- """
661
-
662
- def add_to_benchmark(self, request_type, request, request_time):
663
- """
664
- Add requests to benchmark performance tracking
665
- """
666
- for bm in self.benchmark.values():
667
- if request_type not in bm["requests"]:
668
- bm["requests"][request_type] = {"_default_": []}
669
- if request_type == "walker_run":
670
- walker_name = dict(request.data)["name"]
671
- if walker_name not in bm["requests"][request_type]:
672
- bm["requests"][request_type][walker_name] = []
673
- bm["requests"][request_type][walker_name].append(request_time)
674
- else:
675
- bm["requests"][request_type]["_default_"].append(request_time)
676
-
677
- def set_action_policy(self, policy_name, policy_params):
678
- """
679
- Set an action optimizer policy
680
- """
681
- return self.actions_optimizer.set_action_policy(policy_name, policy_params)
682
-
683
- def get_action_policy(self):
684
- """
685
- Get the current action optimization policy
686
- """
687
- return self.actions_optimizer.get_action_policy()
688
-
689
- def pre_action_call_hook(self, *args):
690
- pass
691
-
692
- def post_action_call_hook(self, *args):
693
- action_name = args[0]
694
- action_time = args[1]
695
- if action_name not in self.actions_calls:
696
- self.actions_calls[action_name] = []
697
-
698
- self.actions_calls[action_name].append(action_time)
699
-
700
- def pre_request_hook(self, *args):
701
- pass
702
-
703
- def post_request_hook(self, *args):
704
- request_type = args[0]
705
- request = args[1]
706
- request_time = args[2]
707
- if self.benchmark["jsorc"]["active"]:
708
- self.add_to_benchmark(request_type, request, request_time)
709
-
710
- def optimize(self, jsorc_interval):
711
- self.actions_optimizer.run(jsorc_interval)
712
-
713
-
714
- class MetaProperties:
715
- def __init__(self, cls):
716
- self.cls = cls
717
-
718
- if not hasattr(cls, "_app"):
719
- setattr(cls, "_app", None)
720
- setattr(cls, "_enabled", True)
721
- setattr(cls, "_state", Ss.NOT_STARTED)
722
- setattr(cls, "_quiet", False)
723
- setattr(cls, "_running_interval", 0)
724
-
725
- @property
726
- def app(self) -> JsOrc:
727
- return self.cls._app
728
-
729
- @app.setter
730
- def app(self, val: JsOrc):
731
- self.cls._app = val
732
-
733
- @property
734
- def state(self) -> Ss:
735
- return self.cls._state
736
-
737
- @state.setter
738
- def state(self, val: Ss):
739
- self.cls._state = val
740
-
741
- @property
742
- def enabled(self) -> bool:
743
- return self.cls._enabled
744
-
745
- @enabled.setter
746
- def enabled(self, val: bool):
747
- pass
748
-
749
- @property
750
- def quiet(self) -> bool:
751
- return self.cls._quiet
752
-
753
- @quiet.setter
754
- def quiet(self, val: bool):
755
- pass
756
-
757
- @property
758
- def running_interval(self) -> int:
759
- return self.cls._running_interval
760
-
761
- @running_interval.setter
762
- def running_interval(self, val: int):
763
- self.cls._running_interval = val