orchestrator-core 4.4.0rc1__py3-none-any.whl → 4.4.0rc3__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.
- orchestrator/__init__.py +1 -1
- orchestrator/api/api_v1/endpoints/subscriptions.py +1 -0
- orchestrator/cli/scheduler.py +12 -16
- orchestrator/schedules/scheduler.py +23 -6
- orchestrator/services/subscriptions.py +4 -0
- orchestrator/targets.py +1 -0
- orchestrator/workflows/utils.py +48 -1
- {orchestrator_core-4.4.0rc1.dist-info → orchestrator_core-4.4.0rc3.dist-info}/METADATA +1 -1
- {orchestrator_core-4.4.0rc1.dist-info → orchestrator_core-4.4.0rc3.dist-info}/RECORD +11 -11
- {orchestrator_core-4.4.0rc1.dist-info → orchestrator_core-4.4.0rc3.dist-info}/WHEEL +0 -0
- {orchestrator_core-4.4.0rc1.dist-info → orchestrator_core-4.4.0rc3.dist-info}/licenses/LICENSE +0 -0
orchestrator/__init__.py
CHANGED
orchestrator/cli/scheduler.py
CHANGED
|
@@ -13,11 +13,13 @@
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
import logging
|
|
16
|
+
import time
|
|
16
17
|
|
|
17
18
|
import typer
|
|
18
|
-
from apscheduler.schedulers.blocking import BlockingScheduler
|
|
19
19
|
|
|
20
|
-
from orchestrator.schedules.scheduler import
|
|
20
|
+
from orchestrator.schedules.scheduler import (
|
|
21
|
+
get_paused_scheduler,
|
|
22
|
+
)
|
|
21
23
|
|
|
22
24
|
log = logging.getLogger(__name__)
|
|
23
25
|
|
|
@@ -27,13 +29,11 @@ app: typer.Typer = typer.Typer()
|
|
|
27
29
|
@app.command()
|
|
28
30
|
def run() -> None:
|
|
29
31
|
"""Start scheduler and loop eternally to keep thread alive."""
|
|
30
|
-
|
|
32
|
+
with get_paused_scheduler() as scheduler:
|
|
33
|
+
scheduler.resume()
|
|
31
34
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
except (KeyboardInterrupt, SystemExit):
|
|
35
|
-
scheduler.shutdown()
|
|
36
|
-
scheduler_dispose_db_connections()
|
|
35
|
+
while True:
|
|
36
|
+
time.sleep(1)
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
@app.command()
|
|
@@ -42,10 +42,8 @@ def show_schedule() -> None:
|
|
|
42
42
|
|
|
43
43
|
in cli underscore is replaced by a dash `show-schedule`
|
|
44
44
|
"""
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
scheduler.shutdown(wait=False)
|
|
48
|
-
scheduler_dispose_db_connections()
|
|
45
|
+
with get_paused_scheduler() as scheduler:
|
|
46
|
+
jobs = scheduler.get_jobs()
|
|
49
47
|
|
|
50
48
|
for job in jobs:
|
|
51
49
|
typer.echo(f"[{job.id}] Next run: {job.next_run_time} | Trigger: {job.trigger}")
|
|
@@ -54,10 +52,8 @@ def show_schedule() -> None:
|
|
|
54
52
|
@app.command()
|
|
55
53
|
def force(job_id: str) -> None:
|
|
56
54
|
"""Force the execution of (a) scheduler(s) based on a job_id."""
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
scheduler.shutdown(wait=False)
|
|
60
|
-
scheduler_dispose_db_connections()
|
|
55
|
+
with get_paused_scheduler() as scheduler:
|
|
56
|
+
job = scheduler.get_job(job_id)
|
|
61
57
|
|
|
62
58
|
if not job:
|
|
63
59
|
typer.echo(f"Job '{job_id}' not found.")
|
|
@@ -12,9 +12,11 @@
|
|
|
12
12
|
# limitations under the License.
|
|
13
13
|
|
|
14
14
|
|
|
15
|
+
from contextlib import contextmanager
|
|
15
16
|
from datetime import datetime
|
|
16
|
-
from typing import Any
|
|
17
|
+
from typing import Any, Generator
|
|
17
18
|
|
|
19
|
+
from apscheduler.executors.pool import ThreadPoolExecutor
|
|
18
20
|
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
|
|
19
21
|
from apscheduler.schedulers.background import BackgroundScheduler
|
|
20
22
|
from more_itertools import partition
|
|
@@ -28,14 +30,31 @@ from orchestrator.settings import app_settings
|
|
|
28
30
|
from orchestrator.utils.helpers import camel_to_snake, to_camel
|
|
29
31
|
|
|
30
32
|
jobstores = {"default": SQLAlchemyJobStore(url=str(app_settings.DATABASE_URI))}
|
|
33
|
+
executors = {
|
|
34
|
+
"default": ThreadPoolExecutor(1),
|
|
35
|
+
}
|
|
36
|
+
job_defaults = {
|
|
37
|
+
"coalesce": True,
|
|
38
|
+
}
|
|
31
39
|
|
|
32
|
-
scheduler = BackgroundScheduler(jobstores=jobstores)
|
|
40
|
+
scheduler = BackgroundScheduler(jobstores=jobstores, executors=executors, job_defaults=job_defaults)
|
|
33
41
|
|
|
34
42
|
|
|
35
43
|
def scheduler_dispose_db_connections() -> None:
|
|
36
44
|
jobstores["default"].engine.dispose()
|
|
37
45
|
|
|
38
46
|
|
|
47
|
+
@contextmanager
|
|
48
|
+
def get_paused_scheduler() -> Generator[BackgroundScheduler, Any, None]:
|
|
49
|
+
scheduler.start(paused=True)
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
yield scheduler
|
|
53
|
+
finally:
|
|
54
|
+
scheduler.shutdown()
|
|
55
|
+
scheduler_dispose_db_connections()
|
|
56
|
+
|
|
57
|
+
|
|
39
58
|
class ScheduledTask(BaseModel):
|
|
40
59
|
id: str
|
|
41
60
|
name: str | None = None
|
|
@@ -131,10 +150,8 @@ def get_scheduler_tasks(
|
|
|
131
150
|
sort_by: list[Sort] | None = None,
|
|
132
151
|
error_handler: CallableErrorHandler = default_error_handler,
|
|
133
152
|
) -> tuple[list[ScheduledTask], int]:
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
scheduler.shutdown(wait=False)
|
|
137
|
-
scheduler_dispose_db_connections()
|
|
153
|
+
with get_paused_scheduler() as pauzed_scheduler:
|
|
154
|
+
scheduled_tasks = pauzed_scheduler.get_jobs()
|
|
138
155
|
|
|
139
156
|
scheduled_tasks = filter_scheduled_tasks(scheduled_tasks, error_handler, filter_by)
|
|
140
157
|
scheduled_tasks = sort_scheduled_tasks(scheduled_tasks, error_handler, sort_by)
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
# limitations under the License.
|
|
13
13
|
|
|
14
14
|
"""Module that provides service functions on subscriptions."""
|
|
15
|
+
|
|
15
16
|
import pickle # noqa: S403
|
|
16
17
|
from collections import defaultdict
|
|
17
18
|
from collections.abc import Sequence
|
|
@@ -506,6 +507,7 @@ TARGET_DEFAULT_USABLE_MAP: dict[Target, list[str]] = {
|
|
|
506
507
|
Target.TERMINATE: ["active", "provisioning"],
|
|
507
508
|
Target.SYSTEM: ["active"],
|
|
508
509
|
Target.VALIDATE: ["active"],
|
|
510
|
+
Target.RECONCILE: ["active"],
|
|
509
511
|
}
|
|
510
512
|
|
|
511
513
|
WF_USABLE_MAP: dict[str, list[str]] = {}
|
|
@@ -532,6 +534,7 @@ def subscription_workflows(subscription: SubscriptionTable) -> dict[str, Any]:
|
|
|
532
534
|
... "terminate": [],
|
|
533
535
|
... "system": [],
|
|
534
536
|
... "validate": [],
|
|
537
|
+
... "reconcile: [],
|
|
535
538
|
... }
|
|
536
539
|
|
|
537
540
|
"""
|
|
@@ -552,6 +555,7 @@ def subscription_workflows(subscription: SubscriptionTable) -> dict[str, Any]:
|
|
|
552
555
|
"terminate": [],
|
|
553
556
|
"system": [],
|
|
554
557
|
"validate": [],
|
|
558
|
+
"reconcile": [],
|
|
555
559
|
}
|
|
556
560
|
for workflow in subscription.product.workflows:
|
|
557
561
|
if workflow.name in WF_USABLE_WHILE_OUT_OF_SYNC or workflow.is_task:
|
orchestrator/targets.py
CHANGED
orchestrator/workflows/utils.py
CHANGED
|
@@ -154,7 +154,7 @@ def _generate_modify_form(workflow_target: str, workflow_name: str) -> InputForm
|
|
|
154
154
|
|
|
155
155
|
|
|
156
156
|
def wrap_modify_initial_input_form(initial_input_form: InputStepFunc | None) -> StateInputStepFunc | None:
|
|
157
|
-
"""Wrap initial input for modify and terminate workflows.
|
|
157
|
+
"""Wrap initial input for modify, reconcile and terminate workflows.
|
|
158
158
|
|
|
159
159
|
This is needed because the frontend expects all modify workflows to start with a page that only contains the
|
|
160
160
|
subscription id. It also expects the second page to have some user visible inputs and the subscription id *again*.
|
|
@@ -355,6 +355,53 @@ def validate_workflow(description: str) -> Callable[[Callable[[], StepList]], Wo
|
|
|
355
355
|
return _validate_workflow
|
|
356
356
|
|
|
357
357
|
|
|
358
|
+
def reconcile_workflow(
|
|
359
|
+
description: str,
|
|
360
|
+
additional_steps: StepList | None = None,
|
|
361
|
+
authorize_callback: Authorizer | None = None,
|
|
362
|
+
retry_auth_callback: Authorizer | None = None,
|
|
363
|
+
) -> Callable[[Callable[[], StepList]], Workflow]:
|
|
364
|
+
"""Similar to a modify_workflow but without required input user input to perform a sync with external systems based on the subscriptions existing configuration.
|
|
365
|
+
|
|
366
|
+
Use this for subscription reconcile workflows.
|
|
367
|
+
|
|
368
|
+
Example::
|
|
369
|
+
|
|
370
|
+
@reconcile_workflow("Reconcile l2vpn")
|
|
371
|
+
def reconcile_l2vpn() -> StepList:
|
|
372
|
+
return (
|
|
373
|
+
begin
|
|
374
|
+
>> update_l2vpn_in_external_systems
|
|
375
|
+
)
|
|
376
|
+
"""
|
|
377
|
+
|
|
378
|
+
wrapped_reconcile_initial_input_form_generator = wrap_modify_initial_input_form(None)
|
|
379
|
+
|
|
380
|
+
def _reconcile_workflow(f: Callable[[], StepList]) -> Workflow:
|
|
381
|
+
steplist = (
|
|
382
|
+
init
|
|
383
|
+
>> store_process_subscription()
|
|
384
|
+
>> unsync
|
|
385
|
+
>> f()
|
|
386
|
+
>> (additional_steps or StepList())
|
|
387
|
+
>> resync
|
|
388
|
+
>> refresh_subscription_search_index
|
|
389
|
+
>> done
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
return make_workflow(
|
|
393
|
+
f,
|
|
394
|
+
description,
|
|
395
|
+
wrapped_reconcile_initial_input_form_generator,
|
|
396
|
+
Target.RECONCILE,
|
|
397
|
+
steplist,
|
|
398
|
+
authorize_callback=authorize_callback,
|
|
399
|
+
retry_auth_callback=retry_auth_callback,
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
return _reconcile_workflow
|
|
403
|
+
|
|
404
|
+
|
|
358
405
|
def ensure_provisioning_status(modify_steps: Step | StepList) -> StepList:
|
|
359
406
|
"""Decorator to ensure subscription modifications are executed only during Provisioning status."""
|
|
360
407
|
return (
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
orchestrator/__init__.py,sha256=
|
|
1
|
+
orchestrator/__init__.py,sha256=4-pAbOsr1gG9e7wEsiJqmS0b4qyvGlnxbcG8JjrN-c8,1066
|
|
2
2
|
orchestrator/app.py,sha256=7UrXKjBKNSEaSSXAd5ww_RdMFhFqE4yvfj8faS2MzAA,12089
|
|
3
3
|
orchestrator/exception_handlers.py,sha256=UsW3dw8q0QQlNLcV359bIotah8DYjMsj2Ts1LfX4ClY,1268
|
|
4
4
|
orchestrator/log_config.py,sha256=1tPRX5q65e57a6a_zEii_PFK8SzWT0mnA5w2sKg4hh8,1853
|
|
5
5
|
orchestrator/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
6
|
orchestrator/security.py,sha256=iXFxGxab54aav7oHEKLAVkTgrQMJGHy6IYLojEnD7gI,2422
|
|
7
7
|
orchestrator/settings.py,sha256=2Kgc6m3qUCcSM3Z_IVUeehfgO0QphMFkLrS0RC3sU-U,4365
|
|
8
|
-
orchestrator/targets.py,sha256=
|
|
8
|
+
orchestrator/targets.py,sha256=d7Fyh_mWIWPivA_E7DTNFpZID3xFW_K0JlZ5nksVX7k,830
|
|
9
9
|
orchestrator/types.py,sha256=qzs7xx5AYRmKbpYRyJJP3wuDb0W0bcAzefCN0RWLAco,15459
|
|
10
10
|
orchestrator/version.py,sha256=b58e08lxs47wUNXv0jXFO_ykpksmytuzEXD4La4W-NQ,1366
|
|
11
11
|
orchestrator/workflow.py,sha256=meDCPnyyX_n5PsMUaFy2wWb5EKNm1_ff7zRDBYrbcDg,45901
|
|
@@ -23,7 +23,7 @@ orchestrator/api/api_v1/endpoints/products.py,sha256=BfFtwu9dZXEQbtKxYj9icc73GKG
|
|
|
23
23
|
orchestrator/api/api_v1/endpoints/resource_types.py,sha256=gGyuaDyOD0TAVoeFGaGmjDGnQ8eQQArOxKrrk4MaDzA,2145
|
|
24
24
|
orchestrator/api/api_v1/endpoints/settings.py,sha256=5s-k169podZjgGHUbVDmSQwpY_3Cs_Bbf2PPtZIkBcw,6184
|
|
25
25
|
orchestrator/api/api_v1/endpoints/subscription_customer_descriptions.py,sha256=1_6LtgQleoq3M6z_W-Qz__Bj3OFUweoPrUqHMwSH6AM,3288
|
|
26
|
-
orchestrator/api/api_v1/endpoints/subscriptions.py,sha256=
|
|
26
|
+
orchestrator/api/api_v1/endpoints/subscriptions.py,sha256=7KaodccUiMkcVnrFnK2azp_V_-hGudcIyhov5WwVGQY,9810
|
|
27
27
|
orchestrator/api/api_v1/endpoints/translations.py,sha256=dIWh_fCnZZUxJoGiNeJ49DK_xpf75IpR_0EIMSvzIvY,963
|
|
28
28
|
orchestrator/api/api_v1/endpoints/user.py,sha256=RyI32EXVu6I-IxWjz0XB5zQWzzLL60zKXLgLqLH02xU,1827
|
|
29
29
|
orchestrator/api/api_v1/endpoints/workflows.py,sha256=_0vhGiQeu3-z16Zi0WmuDWBs8gmed6BzRNwYH_sF6AY,1977
|
|
@@ -36,7 +36,7 @@ orchestrator/cli/migrate_domain_models.py,sha256=WRXy_1OnziQwpsCFZXvjB30nDJtjj0i
|
|
|
36
36
|
orchestrator/cli/migrate_tasks.py,sha256=bju8XColjSZD0v3rS4kl-24dLr8En_H4-6enBmqd494,7255
|
|
37
37
|
orchestrator/cli/migrate_workflows.py,sha256=nxUpx0vgEIc_8aJrjAyrw3E9Dt8JmaamTts8oiQ4vHY,8923
|
|
38
38
|
orchestrator/cli/migration_helpers.py,sha256=C5tpkP5WEBr7G9S-1k1hgSI8ili6xd9Z5ygc9notaK0,4110
|
|
39
|
-
orchestrator/cli/scheduler.py,sha256=
|
|
39
|
+
orchestrator/cli/scheduler.py,sha256=2q6xT_XVOodY3e_qzIV98MWNvKvrbFpOJajWesj1fcs,1911
|
|
40
40
|
orchestrator/cli/domain_gen_helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
41
41
|
orchestrator/cli/domain_gen_helpers/fixed_input_helpers.py,sha256=uzpwsaau81hHSxNMOS9-o7kF-9_78R0f_UE0AvWooZQ,6775
|
|
42
42
|
orchestrator/cli/domain_gen_helpers/helpers.py,sha256=tIPxn8ezED_xYZxH7ZAtQLwkDc6RNmLZVxWAoJ3a9lw,4203
|
|
@@ -249,7 +249,7 @@ orchestrator/migrations/versions/schema/2025-07-04_4b58e336d1bf_deprecating_work
|
|
|
249
249
|
orchestrator/migrations/versions/schema/2025-07-28_850dccac3b02_update_description_of_resume_workflows_.py,sha256=R6Qoga83DJ1IL0WYPu0u5u2ZvAmqGlDmUMv_KtJyOhQ,812
|
|
250
250
|
orchestrator/schedules/__init__.py,sha256=Zy0fTOBMGIRFoh5iVFDLF9_PRAFaONYDThGK9EsysWo,981
|
|
251
251
|
orchestrator/schedules/resume_workflows.py,sha256=jRnVRWDy687pQu-gtk80ecwiLSdrvtL15tG3U2zWA6I,891
|
|
252
|
-
orchestrator/schedules/scheduler.py,sha256=
|
|
252
|
+
orchestrator/schedules/scheduler.py,sha256=_Y6TB-GKNJM0Nk7CRLuMnw0djFEBrDm999GOOcBuBeQ,5880
|
|
253
253
|
orchestrator/schedules/scheduling.py,sha256=_mbpHMhijey8Y56ebtJ4wVkrp_kPVRm8hoByzlQF4SE,2821
|
|
254
254
|
orchestrator/schedules/task_vacuum.py,sha256=mxb7fsy1GphRwvUWi_lvwNaj51YAXUdIDlkOJd90AFI,874
|
|
255
255
|
orchestrator/schedules/validate_products.py,sha256=zWFQeVn3F8LP3joExLiKdmHs008pZsO-RolcIXHjFyE,1322
|
|
@@ -276,7 +276,7 @@ orchestrator/services/resource_types.py,sha256=_QBy_JOW_X3aSTqH0CuLrq4zBJL0p7Q-U
|
|
|
276
276
|
orchestrator/services/settings.py,sha256=HEWfFulgoEDwgfxGEO__QTr5fDiwNBEj1UhAeTAdbLQ,3159
|
|
277
277
|
orchestrator/services/settings_env_variables.py,sha256=iPErQjqPQCxKs0sPhefB16d8SBBVUi6eiRnFBK5bgqA,2196
|
|
278
278
|
orchestrator/services/subscription_relations.py,sha256=aIdyzwyyy58OFhwjRPCPgnQTUTmChu6SeSQRIleQoDE,13138
|
|
279
|
-
orchestrator/services/subscriptions.py,sha256=
|
|
279
|
+
orchestrator/services/subscriptions.py,sha256=XhJ5ygAAyWUIZHULhKyi1uU5DwkKZhzdxxn9vdQZYiA,27281
|
|
280
280
|
orchestrator/services/tasks.py,sha256=mR3Fj1VsudltpanJKI2PvrxersyhVQ1skp8H7r3XnYI,5288
|
|
281
281
|
orchestrator/services/translations.py,sha256=GyP8soUFGej8AS8uulBsk10CCK6Kwfjv9AHMFm3ElQY,1713
|
|
282
282
|
orchestrator/services/workflows.py,sha256=iEkt2OBuTwkDru4V6ZSKatnw0b96ZdPV-VQqeZ9EOgU,4015
|
|
@@ -312,14 +312,14 @@ orchestrator/workflows/__init__.py,sha256=NzIGGI-8SNAwCk2YqH6sHhEWbgAY457ntDwjO1
|
|
|
312
312
|
orchestrator/workflows/modify_note.py,sha256=eXt5KQvrkOXf-3YEXCn2XbBLP9N-n1pUYRW2t8Odupo,2150
|
|
313
313
|
orchestrator/workflows/removed_workflow.py,sha256=V0Da5TEdfLdZZKD38ig-MTp3_IuE7VGqzHHzvPYQmLI,909
|
|
314
314
|
orchestrator/workflows/steps.py,sha256=CZxfzkG5ANJYwuYTkQ4da2RpQqIjXCtey_Uy1ezRAZ4,6479
|
|
315
|
-
orchestrator/workflows/utils.py,sha256=
|
|
315
|
+
orchestrator/workflows/utils.py,sha256=VUCDoIl5XAKtIeAJpVpyW2pCIg3PoVWfwGn28BYlYhA,15424
|
|
316
316
|
orchestrator/workflows/tasks/__init__.py,sha256=GyHNfEFCGKQwRiN6rQmvSRH2iYX7npjMZn97n8XzmLU,571
|
|
317
317
|
orchestrator/workflows/tasks/cleanup_tasks_log.py,sha256=BfWYbPXhnLAHUJ0mlODDnjZnQQAvKCZJDVTwbwOWI04,1624
|
|
318
318
|
orchestrator/workflows/tasks/resume_workflows.py,sha256=T3iobSJjVgiupe0rClD34kUZ7KF4pL5yK2AVeRLZog8,4313
|
|
319
319
|
orchestrator/workflows/tasks/validate_product_type.py,sha256=paG-NAY1bdde3Adt8zItkcBKf5Pxw6f5ngGW6an6dYU,3192
|
|
320
320
|
orchestrator/workflows/tasks/validate_products.py,sha256=GZJBoFF-WMphS7ghMs2-gqvV2iL1F0POhk0uSNt93n0,8510
|
|
321
321
|
orchestrator/workflows/translations/en-GB.json,sha256=ST53HxkphFLTMjFHonykDBOZ7-P_KxksktZU3GbxLt0,846
|
|
322
|
-
orchestrator_core-4.4.
|
|
323
|
-
orchestrator_core-4.4.
|
|
324
|
-
orchestrator_core-4.4.
|
|
325
|
-
orchestrator_core-4.4.
|
|
322
|
+
orchestrator_core-4.4.0rc3.dist-info/licenses/LICENSE,sha256=b-aA5OZQuuBATmLKo_mln8CQrDPPhg3ghLzjPjLn4Tg,11409
|
|
323
|
+
orchestrator_core-4.4.0rc3.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
324
|
+
orchestrator_core-4.4.0rc3.dist-info/METADATA,sha256=Xu_xQc9lhukT3Gj52AVnPxfBc8Q8Pk77IBbPbMkgBe8,5967
|
|
325
|
+
orchestrator_core-4.4.0rc3.dist-info/RECORD,,
|
|
File without changes
|
{orchestrator_core-4.4.0rc1.dist-info → orchestrator_core-4.4.0rc3.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|