opengris-scaler 1.12.37__cp38-cp38-musllinux_1_2_x86_64.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.
- opengris_scaler-1.12.37.dist-info/METADATA +730 -0
- opengris_scaler-1.12.37.dist-info/RECORD +196 -0
- opengris_scaler-1.12.37.dist-info/WHEEL +5 -0
- opengris_scaler-1.12.37.dist-info/entry_points.txt +10 -0
- opengris_scaler-1.12.37.dist-info/licenses/LICENSE +201 -0
- opengris_scaler-1.12.37.dist-info/licenses/LICENSE.spdx +7 -0
- opengris_scaler-1.12.37.dist-info/licenses/NOTICE +8 -0
- opengris_scaler.libs/libcapnp-1-e88d5415.0.1.so +0 -0
- opengris_scaler.libs/libgcc_s-2298274a.so.1 +0 -0
- opengris_scaler.libs/libkj-1-9bebd8ac.0.1.so +0 -0
- opengris_scaler.libs/libstdc++-08d5c7eb.so.6.0.33 +0 -0
- scaler/__init__.py +14 -0
- scaler/about.py +5 -0
- scaler/client/__init__.py +0 -0
- scaler/client/agent/__init__.py +0 -0
- scaler/client/agent/client_agent.py +218 -0
- scaler/client/agent/disconnect_manager.py +27 -0
- scaler/client/agent/future_manager.py +112 -0
- scaler/client/agent/heartbeat_manager.py +74 -0
- scaler/client/agent/mixins.py +89 -0
- scaler/client/agent/object_manager.py +98 -0
- scaler/client/agent/task_manager.py +64 -0
- scaler/client/client.py +672 -0
- scaler/client/future.py +252 -0
- scaler/client/object_buffer.py +129 -0
- scaler/client/object_reference.py +25 -0
- scaler/client/serializer/__init__.py +0 -0
- scaler/client/serializer/default.py +16 -0
- scaler/client/serializer/mixins.py +38 -0
- scaler/cluster/__init__.py +0 -0
- scaler/cluster/cluster.py +95 -0
- scaler/cluster/combo.py +157 -0
- scaler/cluster/object_storage_server.py +45 -0
- scaler/cluster/scheduler.py +86 -0
- scaler/config/__init__.py +0 -0
- scaler/config/common/__init__.py +0 -0
- scaler/config/common/logging.py +41 -0
- scaler/config/common/web.py +18 -0
- scaler/config/common/worker.py +65 -0
- scaler/config/common/worker_adapter.py +28 -0
- scaler/config/config_class.py +317 -0
- scaler/config/defaults.py +94 -0
- scaler/config/mixins.py +20 -0
- scaler/config/section/__init__.py +0 -0
- scaler/config/section/cluster.py +66 -0
- scaler/config/section/ecs_worker_adapter.py +78 -0
- scaler/config/section/native_worker_adapter.py +30 -0
- scaler/config/section/object_storage_server.py +13 -0
- scaler/config/section/scheduler.py +126 -0
- scaler/config/section/symphony_worker_adapter.py +35 -0
- scaler/config/section/top.py +16 -0
- scaler/config/section/webui.py +16 -0
- scaler/config/types/__init__.py +0 -0
- scaler/config/types/network_backend.py +12 -0
- scaler/config/types/object_storage_server.py +45 -0
- scaler/config/types/worker.py +67 -0
- scaler/config/types/zmq.py +83 -0
- scaler/entry_points/__init__.py +0 -0
- scaler/entry_points/cluster.py +10 -0
- scaler/entry_points/object_storage_server.py +26 -0
- scaler/entry_points/scheduler.py +51 -0
- scaler/entry_points/top.py +272 -0
- scaler/entry_points/webui.py +6 -0
- scaler/entry_points/worker_adapter_ecs.py +22 -0
- scaler/entry_points/worker_adapter_native.py +31 -0
- scaler/entry_points/worker_adapter_symphony.py +26 -0
- scaler/io/__init__.py +0 -0
- scaler/io/async_binder.py +89 -0
- scaler/io/async_connector.py +95 -0
- scaler/io/async_object_storage_connector.py +225 -0
- scaler/io/mixins.py +154 -0
- scaler/io/sync_connector.py +68 -0
- scaler/io/sync_object_storage_connector.py +249 -0
- scaler/io/sync_subscriber.py +83 -0
- scaler/io/utility.py +80 -0
- scaler/io/ymq/__init__.py +0 -0
- scaler/io/ymq/_ymq.pyi +95 -0
- scaler/io/ymq/_ymq.so +0 -0
- scaler/io/ymq/ymq.py +138 -0
- scaler/io/ymq_async_object_storage_connector.py +184 -0
- scaler/io/ymq_sync_object_storage_connector.py +184 -0
- scaler/object_storage/__init__.py +0 -0
- scaler/object_storage/object_storage_server.so +0 -0
- scaler/protocol/__init__.py +0 -0
- scaler/protocol/capnp/__init__.py +0 -0
- scaler/protocol/capnp/_python.py +6 -0
- scaler/protocol/capnp/common.capnp +68 -0
- scaler/protocol/capnp/message.capnp +218 -0
- scaler/protocol/capnp/object_storage.capnp +57 -0
- scaler/protocol/capnp/status.capnp +73 -0
- scaler/protocol/introduction.md +105 -0
- scaler/protocol/python/__init__.py +0 -0
- scaler/protocol/python/common.py +140 -0
- scaler/protocol/python/message.py +751 -0
- scaler/protocol/python/mixins.py +13 -0
- scaler/protocol/python/object_storage.py +118 -0
- scaler/protocol/python/status.py +279 -0
- scaler/protocol/worker.md +228 -0
- scaler/scheduler/__init__.py +0 -0
- scaler/scheduler/allocate_policy/__init__.py +0 -0
- scaler/scheduler/allocate_policy/allocate_policy.py +9 -0
- scaler/scheduler/allocate_policy/capability_allocate_policy.py +280 -0
- scaler/scheduler/allocate_policy/even_load_allocate_policy.py +159 -0
- scaler/scheduler/allocate_policy/mixins.py +55 -0
- scaler/scheduler/controllers/__init__.py +0 -0
- scaler/scheduler/controllers/balance_controller.py +65 -0
- scaler/scheduler/controllers/client_controller.py +131 -0
- scaler/scheduler/controllers/config_controller.py +31 -0
- scaler/scheduler/controllers/graph_controller.py +424 -0
- scaler/scheduler/controllers/information_controller.py +81 -0
- scaler/scheduler/controllers/mixins.py +194 -0
- scaler/scheduler/controllers/object_controller.py +147 -0
- scaler/scheduler/controllers/scaling_policies/__init__.py +0 -0
- scaler/scheduler/controllers/scaling_policies/fixed_elastic.py +145 -0
- scaler/scheduler/controllers/scaling_policies/mixins.py +10 -0
- scaler/scheduler/controllers/scaling_policies/null.py +14 -0
- scaler/scheduler/controllers/scaling_policies/types.py +9 -0
- scaler/scheduler/controllers/scaling_policies/utility.py +20 -0
- scaler/scheduler/controllers/scaling_policies/vanilla.py +95 -0
- scaler/scheduler/controllers/task_controller.py +376 -0
- scaler/scheduler/controllers/worker_controller.py +169 -0
- scaler/scheduler/object_usage/__init__.py +0 -0
- scaler/scheduler/object_usage/object_tracker.py +131 -0
- scaler/scheduler/scheduler.py +251 -0
- scaler/scheduler/task/__init__.py +0 -0
- scaler/scheduler/task/task_state_machine.py +92 -0
- scaler/scheduler/task/task_state_manager.py +61 -0
- scaler/ui/__init__.py +0 -0
- scaler/ui/common/__init__.py +0 -0
- scaler/ui/common/constants.py +9 -0
- scaler/ui/common/live_display.py +147 -0
- scaler/ui/common/memory_window.py +146 -0
- scaler/ui/common/setting_page.py +40 -0
- scaler/ui/common/task_graph.py +840 -0
- scaler/ui/common/task_log.py +111 -0
- scaler/ui/common/utility.py +66 -0
- scaler/ui/common/webui.py +80 -0
- scaler/ui/common/worker_processors.py +104 -0
- scaler/ui/v1.py +76 -0
- scaler/ui/v2.py +102 -0
- scaler/ui/webui.py +21 -0
- scaler/utility/__init__.py +0 -0
- scaler/utility/debug.py +19 -0
- scaler/utility/event_list.py +63 -0
- scaler/utility/event_loop.py +58 -0
- scaler/utility/exceptions.py +42 -0
- scaler/utility/formatter.py +44 -0
- scaler/utility/graph/__init__.py +0 -0
- scaler/utility/graph/optimization.py +27 -0
- scaler/utility/graph/topological_sorter.py +11 -0
- scaler/utility/graph/topological_sorter_graphblas.py +174 -0
- scaler/utility/identifiers.py +107 -0
- scaler/utility/logging/__init__.py +0 -0
- scaler/utility/logging/decorators.py +25 -0
- scaler/utility/logging/scoped_logger.py +33 -0
- scaler/utility/logging/utility.py +183 -0
- scaler/utility/many_to_many_dict.py +123 -0
- scaler/utility/metadata/__init__.py +0 -0
- scaler/utility/metadata/profile_result.py +31 -0
- scaler/utility/metadata/task_flags.py +30 -0
- scaler/utility/mixins.py +13 -0
- scaler/utility/network_util.py +7 -0
- scaler/utility/one_to_many_dict.py +72 -0
- scaler/utility/queues/__init__.py +0 -0
- scaler/utility/queues/async_indexed_queue.py +37 -0
- scaler/utility/queues/async_priority_queue.py +70 -0
- scaler/utility/queues/async_sorted_priority_queue.py +45 -0
- scaler/utility/queues/indexed_queue.py +114 -0
- scaler/utility/serialization.py +9 -0
- scaler/version.txt +1 -0
- scaler/worker/__init__.py +0 -0
- scaler/worker/agent/__init__.py +0 -0
- scaler/worker/agent/heartbeat_manager.py +110 -0
- scaler/worker/agent/mixins.py +137 -0
- scaler/worker/agent/processor/__init__.py +0 -0
- scaler/worker/agent/processor/object_cache.py +107 -0
- scaler/worker/agent/processor/processor.py +285 -0
- scaler/worker/agent/processor/streaming_buffer.py +28 -0
- scaler/worker/agent/processor_holder.py +147 -0
- scaler/worker/agent/processor_manager.py +369 -0
- scaler/worker/agent/profiling_manager.py +109 -0
- scaler/worker/agent/task_manager.py +150 -0
- scaler/worker/agent/timeout_manager.py +19 -0
- scaler/worker/preload.py +84 -0
- scaler/worker/worker.py +265 -0
- scaler/worker_adapter/__init__.py +0 -0
- scaler/worker_adapter/common.py +26 -0
- scaler/worker_adapter/ecs.py +241 -0
- scaler/worker_adapter/native.py +138 -0
- scaler/worker_adapter/symphony/__init__.py +0 -0
- scaler/worker_adapter/symphony/callback.py +45 -0
- scaler/worker_adapter/symphony/heartbeat_manager.py +82 -0
- scaler/worker_adapter/symphony/message.py +24 -0
- scaler/worker_adapter/symphony/task_manager.py +289 -0
- scaler/worker_adapter/symphony/worker.py +204 -0
- scaler/worker_adapter/symphony/worker_adapter.py +123 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
from typing import Optional, Tuple
|
|
3
|
+
from urllib.parse import urlparse
|
|
4
|
+
|
|
5
|
+
from scaler.config import defaults
|
|
6
|
+
from scaler.config.common.logging import LoggingConfig
|
|
7
|
+
from scaler.config.config_class import ConfigClass
|
|
8
|
+
from scaler.config.types.object_storage_server import ObjectStorageAddressConfig
|
|
9
|
+
from scaler.config.types.zmq import ZMQConfig
|
|
10
|
+
from scaler.scheduler.allocate_policy.allocate_policy import AllocatePolicy
|
|
11
|
+
from scaler.scheduler.controllers.scaling_policies.types import ScalingControllerStrategy
|
|
12
|
+
from scaler.utility.event_loop import EventLoopType
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclasses.dataclass
|
|
16
|
+
class SchedulerConfig(ConfigClass):
|
|
17
|
+
scheduler_address: ZMQConfig = dataclasses.field(
|
|
18
|
+
metadata=dict(positional=True, help="scheduler address to connect to, e.g.: `tcp://localhost:6378`")
|
|
19
|
+
)
|
|
20
|
+
object_storage_address: Optional[ObjectStorageAddressConfig] = dataclasses.field(
|
|
21
|
+
default=None,
|
|
22
|
+
metadata=dict(
|
|
23
|
+
short="-osa",
|
|
24
|
+
help="specify the object storage server address, if not specified, "
|
|
25
|
+
"the address is scheduler address with port number plus 1, "
|
|
26
|
+
"e.g.: if scheduler address is tcp://localhost:2345, "
|
|
27
|
+
"then object storage address is tcp://localhost:2346",
|
|
28
|
+
),
|
|
29
|
+
)
|
|
30
|
+
monitor_address: Optional[ZMQConfig] = dataclasses.field(
|
|
31
|
+
default=None,
|
|
32
|
+
metadata=dict(
|
|
33
|
+
short="-ma",
|
|
34
|
+
help="specify monitoring address, if not specified, the monitoring address is scheduler address with port "
|
|
35
|
+
"number plus 2, e.g.: if scheduler address is tcp://localhost:2345, then monitoring address is "
|
|
36
|
+
"tcp://localhost:2347",
|
|
37
|
+
),
|
|
38
|
+
)
|
|
39
|
+
scaling_controller_strategy: ScalingControllerStrategy = dataclasses.field(
|
|
40
|
+
default=ScalingControllerStrategy.NULL,
|
|
41
|
+
metadata=dict(
|
|
42
|
+
short="-scs",
|
|
43
|
+
choices=[s.name for s in ScalingControllerStrategy],
|
|
44
|
+
help="specify the scaling controller strategy, if not specified, no scaling controller will be used",
|
|
45
|
+
),
|
|
46
|
+
)
|
|
47
|
+
adapter_webhook_urls: Tuple[str, ...] = dataclasses.field(
|
|
48
|
+
default=(),
|
|
49
|
+
metadata=dict(
|
|
50
|
+
short="-awu",
|
|
51
|
+
type=str,
|
|
52
|
+
nargs="*",
|
|
53
|
+
help="specify the adapter webhook urls for the scaling controller to send scaling events to",
|
|
54
|
+
),
|
|
55
|
+
)
|
|
56
|
+
protected: bool = dataclasses.field(
|
|
57
|
+
default=False,
|
|
58
|
+
metadata=dict(
|
|
59
|
+
short="-p", action="store_true", help="protect scheduler and worker from being shutdown by client"
|
|
60
|
+
),
|
|
61
|
+
)
|
|
62
|
+
allocate_policy: AllocatePolicy = dataclasses.field(
|
|
63
|
+
default=AllocatePolicy.even,
|
|
64
|
+
metadata=dict(
|
|
65
|
+
short="-ap",
|
|
66
|
+
choices=[p.name for p in AllocatePolicy],
|
|
67
|
+
help="specify allocate policy, this controls how scheduler will prioritize tasks, "
|
|
68
|
+
"including balancing tasks",
|
|
69
|
+
),
|
|
70
|
+
)
|
|
71
|
+
max_number_of_tasks_waiting: int = dataclasses.field(
|
|
72
|
+
default=defaults.DEFAULT_MAX_NUMBER_OF_TASKS_WAITING,
|
|
73
|
+
metadata=dict(short="-mt", help="max number of tasks can wait in scheduler while all workers are full"),
|
|
74
|
+
)
|
|
75
|
+
client_timeout_seconds: int = dataclasses.field(
|
|
76
|
+
default=defaults.DEFAULT_CLIENT_TIMEOUT_SECONDS,
|
|
77
|
+
metadata=dict(short="-ct", help="discard client when timeout seconds reached"),
|
|
78
|
+
)
|
|
79
|
+
worker_timeout_seconds: int = dataclasses.field(
|
|
80
|
+
default=defaults.DEFAULT_WORKER_TIMEOUT_SECONDS,
|
|
81
|
+
metadata=dict(short="-wt", help="discard worker when timeout seconds reached"),
|
|
82
|
+
)
|
|
83
|
+
object_retention_seconds: int = dataclasses.field(
|
|
84
|
+
default=defaults.DEFAULT_OBJECT_RETENTION_SECONDS,
|
|
85
|
+
metadata=dict(short="-ot", help="discard function in scheduler when timeout seconds reached"),
|
|
86
|
+
)
|
|
87
|
+
load_balance_seconds: int = dataclasses.field(
|
|
88
|
+
default=defaults.DEFAULT_LOAD_BALANCE_SECONDS,
|
|
89
|
+
metadata=dict(short="-ls", help="number of seconds for load balance operation in scheduler"),
|
|
90
|
+
)
|
|
91
|
+
load_balance_trigger_times: int = dataclasses.field(
|
|
92
|
+
default=defaults.DEFAULT_LOAD_BALANCE_TRIGGER_TIMES,
|
|
93
|
+
metadata=dict(
|
|
94
|
+
short="-lbt",
|
|
95
|
+
help="exact number of repeated load balance advices when trigger load balance operation in scheduler",
|
|
96
|
+
),
|
|
97
|
+
)
|
|
98
|
+
event_loop: str = dataclasses.field(
|
|
99
|
+
default="builtin",
|
|
100
|
+
metadata=dict(short="-el", choices=EventLoopType.allowed_types(), help="select the event loop type"),
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
worker_io_threads: int = dataclasses.field(
|
|
104
|
+
default=defaults.DEFAULT_IO_THREADS,
|
|
105
|
+
metadata=dict(short="-wit", help="set the number of io threads for io backend per worker"),
|
|
106
|
+
)
|
|
107
|
+
logging_config: LoggingConfig = dataclasses.field(default_factory=LoggingConfig)
|
|
108
|
+
|
|
109
|
+
def __post_init__(self):
|
|
110
|
+
if self.max_number_of_tasks_waiting < -1:
|
|
111
|
+
raise ValueError("max_number_of_tasks_waiting must be -1 (for unlimited) or non-negative.")
|
|
112
|
+
if (
|
|
113
|
+
self.client_timeout_seconds <= 0
|
|
114
|
+
or self.worker_timeout_seconds <= 0
|
|
115
|
+
or self.object_retention_seconds <= 0
|
|
116
|
+
or self.load_balance_seconds <= 0
|
|
117
|
+
):
|
|
118
|
+
raise ValueError("All timeout/retention/balance second values must be positive.")
|
|
119
|
+
if self.load_balance_trigger_times <= 0:
|
|
120
|
+
raise ValueError("load_balance_trigger_times must be a positive integer.")
|
|
121
|
+
for adapter_webhook_url in self.adapter_webhook_urls:
|
|
122
|
+
parsed_url = urlparse(adapter_webhook_url)
|
|
123
|
+
if not all([parsed_url.scheme, parsed_url.netloc]):
|
|
124
|
+
raise ValueError(f"adapter_webhook_urls contains url '{adapter_webhook_url}' which is not a valid URL.")
|
|
125
|
+
if self.worker_io_threads <= 0:
|
|
126
|
+
raise ValueError("worker_io_threads must be a positive integer.")
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
|
|
3
|
+
from scaler.config import defaults
|
|
4
|
+
from scaler.config.common.logging import LoggingConfig
|
|
5
|
+
from scaler.config.common.web import WebConfig
|
|
6
|
+
from scaler.config.common.worker import WorkerConfig
|
|
7
|
+
from scaler.config.common.worker_adapter import WorkerAdapterConfig
|
|
8
|
+
from scaler.config.config_class import ConfigClass
|
|
9
|
+
from scaler.utility.event_loop import EventLoopType
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclasses.dataclass
|
|
13
|
+
class SymphonyWorkerConfig(ConfigClass):
|
|
14
|
+
service_name: str = dataclasses.field(metadata=dict(short="-sn", help="symphony service name"))
|
|
15
|
+
|
|
16
|
+
web_config: WebConfig
|
|
17
|
+
worker_adapter_config: WorkerAdapterConfig
|
|
18
|
+
worker_config: WorkerConfig = dataclasses.field(default_factory=WorkerConfig)
|
|
19
|
+
logging_config: LoggingConfig = dataclasses.field(default_factory=LoggingConfig)
|
|
20
|
+
event_loop: str = dataclasses.field(
|
|
21
|
+
default="builtin",
|
|
22
|
+
metadata=dict(short="-el", choices=EventLoopType.allowed_types(), help="select the event loop type"),
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
worker_io_threads: int = dataclasses.field(
|
|
26
|
+
default=defaults.DEFAULT_IO_THREADS,
|
|
27
|
+
metadata=dict(short="-wit", help="set the number of io threads for io backend per worker"),
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
def __post_init__(self):
|
|
31
|
+
"""Validates configuration values after initialization."""
|
|
32
|
+
if not self.service_name:
|
|
33
|
+
raise ValueError("service_name cannot be an empty string.")
|
|
34
|
+
if self.worker_io_threads <= 0:
|
|
35
|
+
raise ValueError("worker_io_threads must be a positive integer.")
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
|
|
3
|
+
from scaler.config.config_class import ConfigClass
|
|
4
|
+
from scaler.config.types.zmq import ZMQConfig
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclasses.dataclass
|
|
8
|
+
class TopConfig(ConfigClass):
|
|
9
|
+
monitor_address: ZMQConfig = dataclasses.field(
|
|
10
|
+
metadata=dict(positional=True, help="scheduler monitor address to connect to")
|
|
11
|
+
)
|
|
12
|
+
timeout: int = dataclasses.field(default=5, metadata=dict(short="-t", help="timeout seconds"))
|
|
13
|
+
|
|
14
|
+
def __post_init__(self):
|
|
15
|
+
if self.timeout <= 0:
|
|
16
|
+
raise ValueError("timeout must be a positive integer.")
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
|
|
3
|
+
from scaler.config.common.logging import LoggingConfig
|
|
4
|
+
from scaler.config.config_class import ConfigClass
|
|
5
|
+
from scaler.config.types.zmq import ZMQConfig
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclasses.dataclass
|
|
9
|
+
class WebUIConfig(ConfigClass):
|
|
10
|
+
monitor_address: ZMQConfig = dataclasses.field(
|
|
11
|
+
metadata=dict(positional=True, help="scheduler monitor address to connect to")
|
|
12
|
+
)
|
|
13
|
+
web_host: str = dataclasses.field(default="0.0.0.0", metadata=dict(help="host for webserver to connect to"))
|
|
14
|
+
web_port: int = dataclasses.field(default=50001, metadata=dict(help="host for webserver to connect to"))
|
|
15
|
+
|
|
16
|
+
logging_config: LoggingConfig = dataclasses.field(default_factory=LoggingConfig)
|
|
File without changes
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class NetworkBackend(enum.Enum):
|
|
5
|
+
"""
|
|
6
|
+
Network backend to select when running scaler
|
|
7
|
+
- tcp_zmq means for oss it use raw tcp, for client/scheduler/worker communication it use zmq
|
|
8
|
+
- ymq means all components will use ymq for communication
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
tcp_zmq = enum.auto()
|
|
12
|
+
ymq = enum.auto()
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import ipaddress
|
|
3
|
+
|
|
4
|
+
from scaler.config.mixins import ConfigType
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclasses.dataclass
|
|
8
|
+
class ObjectStorageAddressConfig(ConfigType):
|
|
9
|
+
host: str
|
|
10
|
+
port: int
|
|
11
|
+
identity: str = "ObjectStorageServer"
|
|
12
|
+
|
|
13
|
+
def __post_init__(self):
|
|
14
|
+
try:
|
|
15
|
+
ipaddress.ip_address(self.host)
|
|
16
|
+
except ValueError:
|
|
17
|
+
raise TypeError(f"Host must be a valid IP address, but got '{self.host}'")
|
|
18
|
+
|
|
19
|
+
if not isinstance(self.identity, str):
|
|
20
|
+
raise TypeError(f"Identity should be a string, given {self.identity}")
|
|
21
|
+
|
|
22
|
+
if not isinstance(self.port, int):
|
|
23
|
+
raise TypeError(f"Port should be an integer, given {self.port}")
|
|
24
|
+
|
|
25
|
+
def __str__(self) -> str:
|
|
26
|
+
return self.to_string()
|
|
27
|
+
|
|
28
|
+
def to_string(self) -> str:
|
|
29
|
+
return f"tcp://{self.host}:{self.port}"
|
|
30
|
+
|
|
31
|
+
@classmethod
|
|
32
|
+
def from_string(cls, value: str) -> "ObjectStorageAddressConfig":
|
|
33
|
+
if not value.startswith("tcp://"):
|
|
34
|
+
raise ValueError("Address must start with 'tcp://'")
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
host, port_str = value[6:].rsplit(":", 1)
|
|
38
|
+
port = int(port_str)
|
|
39
|
+
|
|
40
|
+
ipaddress.ip_address(host)
|
|
41
|
+
|
|
42
|
+
except (ValueError, IndexError):
|
|
43
|
+
raise ValueError(f"Invalid address format '{value}'. Expected format is tcp://<ip_address>:<port>")
|
|
44
|
+
|
|
45
|
+
return cls(host=host, port=port)
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import sys
|
|
3
|
+
from typing import Dict, List
|
|
4
|
+
|
|
5
|
+
if sys.version_info >= (3, 11):
|
|
6
|
+
from typing import Self
|
|
7
|
+
else:
|
|
8
|
+
from typing_extensions import Self
|
|
9
|
+
|
|
10
|
+
from scaler.config.mixins import ConfigType
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclasses.dataclass
|
|
14
|
+
class WorkerNames(ConfigType):
|
|
15
|
+
"""Parses a comma-separated string of worker names into a list."""
|
|
16
|
+
|
|
17
|
+
names: List[str] = dataclasses.field(default_factory=list)
|
|
18
|
+
|
|
19
|
+
@classmethod
|
|
20
|
+
def from_string(cls, value: str) -> Self:
|
|
21
|
+
if not value:
|
|
22
|
+
return cls([])
|
|
23
|
+
names = [name.strip() for name in value.split(",")]
|
|
24
|
+
return cls(names)
|
|
25
|
+
|
|
26
|
+
def __str__(self) -> str:
|
|
27
|
+
if self.names:
|
|
28
|
+
return ",".join(self.names)
|
|
29
|
+
return "<empty>"
|
|
30
|
+
|
|
31
|
+
def __len__(self) -> int:
|
|
32
|
+
return len(self.names)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclasses.dataclass
|
|
36
|
+
class WorkerCapabilities(ConfigType):
|
|
37
|
+
"""Parses a string of worker capabilities."""
|
|
38
|
+
|
|
39
|
+
capabilities: Dict[str, int] = dataclasses.field(default_factory=dict)
|
|
40
|
+
|
|
41
|
+
@classmethod
|
|
42
|
+
def from_string(cls, value: str) -> Self:
|
|
43
|
+
capabilities: Dict[str, int] = {}
|
|
44
|
+
if not value:
|
|
45
|
+
return cls(capabilities)
|
|
46
|
+
for item in value.split(","):
|
|
47
|
+
name, _, value = item.partition("=")
|
|
48
|
+
if value != "":
|
|
49
|
+
try:
|
|
50
|
+
capabilities[name.strip()] = int(value)
|
|
51
|
+
except ValueError:
|
|
52
|
+
raise ValueError(f"Invalid capability value for '{name}'. Expected an integer, but got '{value}'.")
|
|
53
|
+
else:
|
|
54
|
+
capabilities[name.strip()] = -1
|
|
55
|
+
return cls(capabilities)
|
|
56
|
+
|
|
57
|
+
def __str__(self) -> str:
|
|
58
|
+
if not self.capabilities:
|
|
59
|
+
return "<empty>"
|
|
60
|
+
|
|
61
|
+
items = []
|
|
62
|
+
for name, cap in self.capabilities.items():
|
|
63
|
+
if cap == -1:
|
|
64
|
+
items.append(name)
|
|
65
|
+
else:
|
|
66
|
+
items.append(f"{name}={cap}")
|
|
67
|
+
return ",".join(items)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import enum
|
|
3
|
+
import sys
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
if sys.version_info >= (3, 11):
|
|
7
|
+
from typing import Self
|
|
8
|
+
else:
|
|
9
|
+
from typing_extensions import Self
|
|
10
|
+
from scaler.config.mixins import ConfigType
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ZMQType(enum.Enum):
|
|
14
|
+
inproc = "inproc"
|
|
15
|
+
ipc = "ipc"
|
|
16
|
+
tcp = "tcp"
|
|
17
|
+
|
|
18
|
+
@staticmethod
|
|
19
|
+
def allowed_types():
|
|
20
|
+
return {t.value for t in ZMQType}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclasses.dataclass
|
|
24
|
+
class ZMQConfig(ConfigType):
|
|
25
|
+
type: ZMQType
|
|
26
|
+
host: str
|
|
27
|
+
port: Optional[int] = None
|
|
28
|
+
|
|
29
|
+
def __post_init__(self):
|
|
30
|
+
if not isinstance(self.type, ZMQType):
|
|
31
|
+
raise TypeError(f"Invalid zmq type {self.type}, available types are: {ZMQType.allowed_types()}")
|
|
32
|
+
|
|
33
|
+
if not isinstance(self.host, str):
|
|
34
|
+
raise TypeError(f"Host should be string, given {self.host}")
|
|
35
|
+
|
|
36
|
+
if self.port is None:
|
|
37
|
+
if self.type == ZMQType.tcp:
|
|
38
|
+
raise ValueError(f"type {self.type.value} should have `port`")
|
|
39
|
+
else:
|
|
40
|
+
if self.type in {ZMQType.inproc, ZMQType.ipc}:
|
|
41
|
+
raise ValueError(f"type {self.type.value} should not have `port`")
|
|
42
|
+
|
|
43
|
+
if not isinstance(self.port, int):
|
|
44
|
+
raise TypeError(f"Port should be integer, given {self.port}")
|
|
45
|
+
|
|
46
|
+
def to_address(self):
|
|
47
|
+
if self.type == ZMQType.tcp:
|
|
48
|
+
return f"tcp://{self.host}:{self.port}"
|
|
49
|
+
|
|
50
|
+
if self.type in {ZMQType.inproc, ZMQType.ipc}:
|
|
51
|
+
return f"{self.type.value}://{self.host}"
|
|
52
|
+
|
|
53
|
+
raise TypeError(f"Unsupported ZMQ type: {self.type}")
|
|
54
|
+
|
|
55
|
+
@classmethod
|
|
56
|
+
def from_string(cls, value: str) -> Self:
|
|
57
|
+
if "://" not in value:
|
|
58
|
+
raise ValueError("valid ZMQ config should be like tcp://127.0.0.1:12345")
|
|
59
|
+
|
|
60
|
+
socket_type, host_port = value.split("://", 1)
|
|
61
|
+
if socket_type not in ZMQType.allowed_types():
|
|
62
|
+
raise ValueError(f"supported ZMQ types are: {ZMQType.allowed_types()}")
|
|
63
|
+
|
|
64
|
+
socket_type_enum = ZMQType(socket_type)
|
|
65
|
+
if socket_type_enum in {ZMQType.inproc, ZMQType.ipc}:
|
|
66
|
+
host = host_port
|
|
67
|
+
port_int = None
|
|
68
|
+
elif socket_type_enum == ZMQType.tcp:
|
|
69
|
+
host, port = host_port.split(":")
|
|
70
|
+
try:
|
|
71
|
+
port_int = int(port)
|
|
72
|
+
except ValueError:
|
|
73
|
+
raise ValueError(f"cannot convert '{port}' to port number")
|
|
74
|
+
else:
|
|
75
|
+
raise ValueError(f"Unsupported ZMQ type: {socket_type}")
|
|
76
|
+
|
|
77
|
+
return cls(socket_type_enum, host, port_int)
|
|
78
|
+
|
|
79
|
+
def __str__(self) -> str:
|
|
80
|
+
return self.to_address()
|
|
81
|
+
|
|
82
|
+
def __repr__(self) -> str:
|
|
83
|
+
return self.to_address()
|
|
File without changes
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from scaler.cluster.cluster import Cluster
|
|
2
|
+
from scaler.config.section.cluster import ClusterConfig
|
|
3
|
+
from scaler.utility.event_loop import register_event_loop
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def main():
|
|
7
|
+
cluster_config = ClusterConfig.parse("Scaler Standalone Compute Cluster", "cluster")
|
|
8
|
+
register_event_loop(cluster_config.event_loop)
|
|
9
|
+
cluster = Cluster(cluster_config)
|
|
10
|
+
cluster.run()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
from scaler.config.section.object_storage_server import ObjectStorageServerConfig
|
|
5
|
+
from scaler.object_storage.object_storage_server import ObjectStorageServer
|
|
6
|
+
from scaler.utility.logging.utility import get_logger_info, setup_logger
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def main():
|
|
10
|
+
oss_config = ObjectStorageServerConfig.parse("Scaler Object Storage Server", "object_storage_server")
|
|
11
|
+
|
|
12
|
+
setup_logger()
|
|
13
|
+
|
|
14
|
+
log_format_str, log_level_str, log_paths = get_logger_info(logging.getLogger())
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
ObjectStorageServer().run(
|
|
18
|
+
oss_config.object_storage_address.host,
|
|
19
|
+
oss_config.object_storage_address.port,
|
|
20
|
+
oss_config.object_storage_address.identity,
|
|
21
|
+
log_level_str,
|
|
22
|
+
log_format_str,
|
|
23
|
+
log_paths,
|
|
24
|
+
)
|
|
25
|
+
except KeyboardInterrupt:
|
|
26
|
+
sys.exit(0)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from scaler.cluster.object_storage_server import ObjectStorageServerProcess
|
|
2
|
+
from scaler.cluster.scheduler import SchedulerProcess
|
|
3
|
+
from scaler.config.section.scheduler import SchedulerConfig
|
|
4
|
+
from scaler.config.types.object_storage_server import ObjectStorageAddressConfig
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def main():
|
|
8
|
+
scheduler_config = SchedulerConfig.parse("Scaler Scheduler", "scheduler")
|
|
9
|
+
|
|
10
|
+
object_storage_address = scheduler_config.object_storage_address
|
|
11
|
+
object_storage = None
|
|
12
|
+
|
|
13
|
+
if object_storage_address is None:
|
|
14
|
+
assert scheduler_config.scheduler_address.port is not None, "Scheduler address must have a port"
|
|
15
|
+
object_storage_address = ObjectStorageAddressConfig(
|
|
16
|
+
host=scheduler_config.scheduler_address.host, port=scheduler_config.scheduler_address.port + 1
|
|
17
|
+
)
|
|
18
|
+
object_storage = ObjectStorageServerProcess(
|
|
19
|
+
object_storage_address=object_storage_address,
|
|
20
|
+
logging_paths=scheduler_config.logging_config.paths,
|
|
21
|
+
logging_config_file=scheduler_config.logging_config.config_file,
|
|
22
|
+
logging_level=scheduler_config.logging_config.level,
|
|
23
|
+
)
|
|
24
|
+
object_storage.start()
|
|
25
|
+
object_storage.wait_until_ready() # object storage should be ready before starting the cluster
|
|
26
|
+
|
|
27
|
+
scheduler = SchedulerProcess(
|
|
28
|
+
address=scheduler_config.scheduler_address,
|
|
29
|
+
object_storage_address=object_storage_address,
|
|
30
|
+
monitor_address=scheduler_config.monitor_address,
|
|
31
|
+
scaling_controller_strategy=scheduler_config.scaling_controller_strategy,
|
|
32
|
+
adapter_webhook_urls=scheduler_config.adapter_webhook_urls,
|
|
33
|
+
io_threads=scheduler_config.worker_io_threads,
|
|
34
|
+
max_number_of_tasks_waiting=scheduler_config.max_number_of_tasks_waiting,
|
|
35
|
+
client_timeout_seconds=scheduler_config.client_timeout_seconds,
|
|
36
|
+
worker_timeout_seconds=scheduler_config.worker_timeout_seconds,
|
|
37
|
+
object_retention_seconds=scheduler_config.object_retention_seconds,
|
|
38
|
+
load_balance_seconds=scheduler_config.load_balance_seconds,
|
|
39
|
+
load_balance_trigger_times=scheduler_config.load_balance_trigger_times,
|
|
40
|
+
protected=scheduler_config.protected,
|
|
41
|
+
allocate_policy=scheduler_config.allocate_policy,
|
|
42
|
+
event_loop=scheduler_config.event_loop,
|
|
43
|
+
logging_paths=scheduler_config.logging_config.paths,
|
|
44
|
+
logging_config_file=scheduler_config.logging_config.config_file,
|
|
45
|
+
logging_level=scheduler_config.logging_config.level,
|
|
46
|
+
)
|
|
47
|
+
scheduler.start()
|
|
48
|
+
|
|
49
|
+
scheduler.join()
|
|
50
|
+
if object_storage is not None:
|
|
51
|
+
object_storage.join()
|