idmtools 0.0.0.dev0__py3-none-any.whl → 0.0.2__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.
- idmtools/__init__.py +27 -8
- idmtools/analysis/__init__.py +5 -0
- idmtools/analysis/add_analyzer.py +89 -0
- idmtools/analysis/analyze_manager.py +490 -0
- idmtools/analysis/csv_analyzer.py +103 -0
- idmtools/analysis/download_analyzer.py +96 -0
- idmtools/analysis/map_worker_entry.py +100 -0
- idmtools/analysis/platform_analysis_bootstrap.py +94 -0
- idmtools/analysis/platform_anaylsis.py +291 -0
- idmtools/analysis/tags_analyzer.py +93 -0
- idmtools/assets/__init__.py +9 -0
- idmtools/assets/asset.py +453 -0
- idmtools/assets/asset_collection.py +514 -0
- idmtools/assets/content_handlers.py +19 -0
- idmtools/assets/errors.py +23 -0
- idmtools/assets/file_list.py +191 -0
- idmtools/builders/__init__.py +11 -0
- idmtools/builders/arm_simulation_builder.py +152 -0
- idmtools/builders/csv_simulation_builder.py +76 -0
- idmtools/builders/simulation_builder.py +348 -0
- idmtools/builders/sweep_arm.py +109 -0
- idmtools/builders/yaml_simulation_builder.py +82 -0
- idmtools/config/__init__.py +7 -0
- idmtools/config/idm_config_parser.py +486 -0
- idmtools/core/__init__.py +10 -0
- idmtools/core/cache_enabled.py +114 -0
- idmtools/core/context.py +68 -0
- idmtools/core/docker_task.py +207 -0
- idmtools/core/enums.py +51 -0
- idmtools/core/exceptions.py +91 -0
- idmtools/core/experiment_factory.py +71 -0
- idmtools/core/id_file.py +70 -0
- idmtools/core/interfaces/__init__.py +5 -0
- idmtools/core/interfaces/entity_container.py +64 -0
- idmtools/core/interfaces/iassets_enabled.py +58 -0
- idmtools/core/interfaces/ientity.py +331 -0
- idmtools/core/interfaces/iitem.py +206 -0
- idmtools/core/interfaces/imetadata_operations.py +89 -0
- idmtools/core/interfaces/inamed_entity.py +17 -0
- idmtools/core/interfaces/irunnable_entity.py +159 -0
- idmtools/core/logging.py +387 -0
- idmtools/core/platform_factory.py +316 -0
- idmtools/core/system_information.py +104 -0
- idmtools/core/task_factory.py +145 -0
- idmtools/entities/__init__.py +10 -0
- idmtools/entities/command_line.py +229 -0
- idmtools/entities/command_task.py +155 -0
- idmtools/entities/experiment.py +787 -0
- idmtools/entities/generic_workitem.py +43 -0
- idmtools/entities/ianalyzer.py +163 -0
- idmtools/entities/iplatform.py +1106 -0
- idmtools/entities/iplatform_default.py +39 -0
- idmtools/entities/iplatform_ops/__init__.py +5 -0
- idmtools/entities/iplatform_ops/iplatform_asset_collection_operations.py +148 -0
- idmtools/entities/iplatform_ops/iplatform_experiment_operations.py +415 -0
- idmtools/entities/iplatform_ops/iplatform_simulation_operations.py +315 -0
- idmtools/entities/iplatform_ops/iplatform_suite_operations.py +322 -0
- idmtools/entities/iplatform_ops/iplatform_workflowitem_operations.py +301 -0
- idmtools/entities/iplatform_ops/utils.py +185 -0
- idmtools/entities/itask.py +316 -0
- idmtools/entities/iworkflow_item.py +167 -0
- idmtools/entities/platform_requirements.py +20 -0
- idmtools/entities/relation_type.py +14 -0
- idmtools/entities/simulation.py +255 -0
- idmtools/entities/suite.py +188 -0
- idmtools/entities/task_proxy.py +37 -0
- idmtools/entities/templated_simulation.py +325 -0
- idmtools/frozen/frozen_dict.py +71 -0
- idmtools/frozen/frozen_list.py +66 -0
- idmtools/frozen/frozen_set.py +86 -0
- idmtools/frozen/frozen_tuple.py +18 -0
- idmtools/frozen/frozen_utils.py +179 -0
- idmtools/frozen/ifrozen.py +66 -0
- idmtools/plugins/__init__.py +5 -0
- idmtools/plugins/git_commit.py +117 -0
- idmtools/registry/__init__.py +4 -0
- idmtools/registry/experiment_specification.py +105 -0
- idmtools/registry/functions.py +28 -0
- idmtools/registry/hook_specs.py +132 -0
- idmtools/registry/master_plugin_registry.py +51 -0
- idmtools/registry/platform_specification.py +138 -0
- idmtools/registry/plugin_specification.py +129 -0
- idmtools/registry/task_specification.py +104 -0
- idmtools/registry/utils.py +119 -0
- idmtools/services/__init__.py +5 -0
- idmtools/services/ipersistance_service.py +135 -0
- idmtools/services/platforms.py +13 -0
- idmtools/utils/__init__.py +5 -0
- idmtools/utils/caller.py +24 -0
- idmtools/utils/collections.py +246 -0
- idmtools/utils/command_line.py +45 -0
- idmtools/utils/decorators.py +300 -0
- idmtools/utils/display/__init__.py +22 -0
- idmtools/utils/display/displays.py +181 -0
- idmtools/utils/display/settings.py +25 -0
- idmtools/utils/dropbox_location.py +30 -0
- idmtools/utils/entities.py +127 -0
- idmtools/utils/file.py +72 -0
- idmtools/utils/file_parser.py +151 -0
- idmtools/utils/filter_simulations.py +182 -0
- idmtools/utils/filters/__init__.py +5 -0
- idmtools/utils/filters/asset_filters.py +88 -0
- idmtools/utils/general.py +286 -0
- idmtools/utils/gitrepo.py +336 -0
- idmtools/utils/hashing.py +239 -0
- idmtools/utils/info.py +124 -0
- idmtools/utils/json.py +82 -0
- idmtools/utils/language.py +107 -0
- idmtools/utils/local_os.py +40 -0
- idmtools/utils/time.py +22 -0
- idmtools-0.0.2.dist-info/METADATA +120 -0
- idmtools-0.0.2.dist-info/RECORD +116 -0
- idmtools-0.0.2.dist-info/entry_points.txt +9 -0
- idmtools-0.0.2.dist-info/licenses/LICENSE.TXT +3 -0
- idmtools-0.0.0.dev0.dist-info/METADATA +0 -41
- idmtools-0.0.0.dev0.dist-info/RECORD +0 -5
- {idmtools-0.0.0.dev0.dist-info → idmtools-0.0.2.dist-info}/WHEEL +0 -0
- {idmtools-0.0.0.dev0.dist-info → idmtools-0.0.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Manages the creation of our platforms.
|
|
3
|
+
|
|
4
|
+
The Platform allows us to lookup a platform via its plugin name, "COMPS" or via configuration aliases defined in a platform plugins, such as CALCULON.
|
|
5
|
+
|
|
6
|
+
Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
|
|
7
|
+
"""
|
|
8
|
+
import json
|
|
9
|
+
import os
|
|
10
|
+
from contextlib import contextmanager
|
|
11
|
+
from dataclasses import fields
|
|
12
|
+
from logging import getLogger, DEBUG
|
|
13
|
+
from typing import TYPE_CHECKING
|
|
14
|
+
from idmtools.config import IdmConfigParser
|
|
15
|
+
from idmtools.core import TRUTHY_VALUES
|
|
16
|
+
from idmtools.core.context import set_current_platform, remove_current_platform
|
|
17
|
+
from idmtools.utils.entities import validate_user_inputs_against_dataclass
|
|
18
|
+
from idmtools.utils.json import IDMJSONEncoder
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
21
|
+
from idmtools.entities.iplatform import IPlatform
|
|
22
|
+
|
|
23
|
+
logger = getLogger(__name__)
|
|
24
|
+
user_logger = getLogger('user')
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@contextmanager
|
|
28
|
+
def platform(*args, **kwargs):
|
|
29
|
+
"""
|
|
30
|
+
Utility function to create platform.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
*args: Arguments to pass to platform
|
|
34
|
+
**kwargs: Keyword args to pass to platform
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
Platform created.
|
|
38
|
+
"""
|
|
39
|
+
logger.debug(f'Acquiring platform context with options: {str(*args)}')
|
|
40
|
+
try:
|
|
41
|
+
# check if we are already in a platform context and if so add to stack
|
|
42
|
+
pl = Platform(*args, **kwargs)
|
|
43
|
+
set_current_platform(pl)
|
|
44
|
+
yield pl
|
|
45
|
+
finally:
|
|
46
|
+
# Code to release resource, e.g.:
|
|
47
|
+
logger.debug('Un-setting current platform context')
|
|
48
|
+
remove_current_platform()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class Platform:
|
|
52
|
+
"""
|
|
53
|
+
Platform Factory.
|
|
54
|
+
"""
|
|
55
|
+
_aliases = None
|
|
56
|
+
_platform_plugins = None
|
|
57
|
+
|
|
58
|
+
def __new__(cls, block: str = None, **kwargs):
|
|
59
|
+
"""
|
|
60
|
+
Create a platform based on the block and all other inputs.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
block(str, optional): The INI configuration file block name.
|
|
64
|
+
|
|
65
|
+
COMPSPlatform Keyword Args:
|
|
66
|
+
- endpoint (str, optional): URL of the COMPS endpoint to use. Default is 'https://comps.idmod.org'
|
|
67
|
+
- environment (str, optional): Name of the COMPS environment to target, Default is Calculon, Options are Calculon, IDMcloud, SlurmStage, Cumulus, etc
|
|
68
|
+
- priority (str, optional): Priority of the job. Default is 'Lowest'. Options are Lowest, BelowNormal, Normal, AboveNormal, Highest
|
|
69
|
+
- node_group (str, optional): node group to target. Default is None. Options are 'idm_abcd', 'idm_ab', idm_cd', 'idm_a', 'idm_b', 'idm_c', 'idm_d', 'idm_48cores'
|
|
70
|
+
- num_retries (int, optional): How retries if the simulation fails, Default is 0, max is 10
|
|
71
|
+
- num_cores (int, optional): How many cores per simulation. Default is 1, max is 32
|
|
72
|
+
- max_workers (int, optional): The number of processes to spawn locally. Defaults to 16, min is 1, max is 32
|
|
73
|
+
- batch_size (int, optional): How many simulations per batch. Default is 10, min is 1 and max is 100
|
|
74
|
+
- exclusive (bool, optional): Enable exclusive mode? (one simulation per node on the cluster). Default is False
|
|
75
|
+
- docker_image (str, optional): Docker image to use for the simulation. Default is None
|
|
76
|
+
|
|
77
|
+
SlurmPlatform Keyword Args:
|
|
78
|
+
- nodes (int, optional): How many nodes to be used. Default is None
|
|
79
|
+
- ntasks (int, optional): Num of tasks. Default is None
|
|
80
|
+
- cpus_per_task (int, optional): CPU # per task. Default is None
|
|
81
|
+
- ntasks_per_core (int, optional): Task # per core. Default is None
|
|
82
|
+
- max_running_jobs (int, optional): Maximum of running jobs(Per experiment). Default is None
|
|
83
|
+
- mem (int, optional): Memory per core: MB of memory. Default is None
|
|
84
|
+
- mem_per_cpu (int, optional): Memory per core: MB of memory. Default is None
|
|
85
|
+
- partition (str, optional): Which partition to use. Default is None
|
|
86
|
+
- constraint (str, optional): Specify compute node. Default is None
|
|
87
|
+
- time (str, optional): Limit time on this job hrs:min:sec. Default is None
|
|
88
|
+
- account (str, optional): if set to something, jobs will run with the specified account in slurm. Default is None
|
|
89
|
+
- exclusive (bool, optional): Allocated nodes can not be shared with other jobs/users. Default is False
|
|
90
|
+
- requeue (bool, optional): Specifies that the batch job should be eligible for re-queuing. Default is True
|
|
91
|
+
- retries (int, optional): Default retries for jobs. Default is 1
|
|
92
|
+
- sbatch_custom (str, optional): Pass custom commands to sbatch generation script. Default is None
|
|
93
|
+
- modules (list, optional): modules to be load, for example load 'mpi' module. Default is []
|
|
94
|
+
- dir_exist_ok (bool, optional): Specifies default setting of whether slurm should fail if item directory already exists. Default is False
|
|
95
|
+
- array_batch_size (int, optional): Set array max size for Slurm job. Default is None
|
|
96
|
+
- run_on_slurm (bool, optional): determine if run script as Slurm job. Default is False
|
|
97
|
+
- mpi_type (str, optional): MPI type to use in slurm. Default is pmi2. Options are pmi2, pmix, mpirun
|
|
98
|
+
|
|
99
|
+
ContainerPlatform Keyword Args:
|
|
100
|
+
- job_directory (str, optional): Job directory. Default is None
|
|
101
|
+
- docker_image (str, optional): Docker image to use for the simulation. Default is None
|
|
102
|
+
- extra_packages (list, optional): Extra packages to install. Default is None
|
|
103
|
+
- data_mount (str, optional): Data mount point. Default is None
|
|
104
|
+
- user_mounts (dict, optional): User mounts. Default is None
|
|
105
|
+
- container_prefix (str, optional): Prefix for container name. Default is None
|
|
106
|
+
- force_start (bool, optional): Force start container. Default is False
|
|
107
|
+
- new_container (bool, optional): Start a new container. Default is False
|
|
108
|
+
- include_stopped (bool, optional): Include stopped containers. Default is False
|
|
109
|
+
- container_id (str, optional): The ID of the container being used.
|
|
110
|
+
- max_job (int, optional): Max job. Default is 4
|
|
111
|
+
- modules (list, optional): Modules to load. Default is None
|
|
112
|
+
- debug (bool, optional): Debug mode. Default is False
|
|
113
|
+
- retries (int, optional): The number of retries to attempt for a job.
|
|
114
|
+
- ntasks (int, optional): Number of MPI processes. Default is 1
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
The requested platform.
|
|
118
|
+
|
|
119
|
+
Raises:
|
|
120
|
+
ValueError or Exception: If the platform is of an unknown type.
|
|
121
|
+
"""
|
|
122
|
+
from idmtools.registry.platform_specification import PlatformPlugins
|
|
123
|
+
|
|
124
|
+
IdmConfigParser.ensure_init()
|
|
125
|
+
|
|
126
|
+
# Load all Platform plugins
|
|
127
|
+
cls._platform_plugins = PlatformPlugins().get_plugin_map()
|
|
128
|
+
cls._aliases = PlatformPlugins().get_aliases()
|
|
129
|
+
cls._type_map = {key.upper(): key for key in cls._platform_plugins.keys()}
|
|
130
|
+
|
|
131
|
+
_platform = cls._create_platform_from_block(block, **kwargs)
|
|
132
|
+
set_current_platform(_platform)
|
|
133
|
+
_platform._config_block = block
|
|
134
|
+
_platform._kwargs = kwargs
|
|
135
|
+
return _platform
|
|
136
|
+
|
|
137
|
+
@classmethod
|
|
138
|
+
def _validate_platform_type(cls, platform_type):
|
|
139
|
+
"""
|
|
140
|
+
Check if the requested platform exists.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
platform_type: The platform type.
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
None
|
|
147
|
+
|
|
148
|
+
Raise:
|
|
149
|
+
ValueError: when the platform is of an unknown type
|
|
150
|
+
"""
|
|
151
|
+
if platform_type is None or platform_type.upper() not in cls._type_map:
|
|
152
|
+
raise ValueError(f"{platform_type} is an unknown Platform Type. "
|
|
153
|
+
f"Supported platforms are {', '.join(cls._platform_plugins.keys())}")
|
|
154
|
+
|
|
155
|
+
@classmethod
|
|
156
|
+
def _create_platform_from_block(cls, block: str, **kwargs) -> 'IPlatform':
|
|
157
|
+
"""
|
|
158
|
+
Create a platform based on the block and all other inputs.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
block: The section name in the configuration file or platform alias.
|
|
162
|
+
kwargs: Keyword args to pass to platform
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
A platform instance.
|
|
166
|
+
"""
|
|
167
|
+
# Get the type of the platform and the section from block and kwargs
|
|
168
|
+
platform_type, section, is_alias = cls._get_platform_type(block, **kwargs)
|
|
169
|
+
if 'type' in kwargs:
|
|
170
|
+
platform_type = kwargs['type']
|
|
171
|
+
kwargs.pop('type')
|
|
172
|
+
|
|
173
|
+
# Make sure we support platform_type
|
|
174
|
+
cls._validate_platform_type(platform_type)
|
|
175
|
+
|
|
176
|
+
# Find the correct Platform type
|
|
177
|
+
platform_type = cls._type_map[platform_type.upper()]
|
|
178
|
+
platform_spec = cls._platform_plugins.get(platform_type)
|
|
179
|
+
platform_cls = platform_spec.get_type()
|
|
180
|
+
|
|
181
|
+
# Collect fields types
|
|
182
|
+
fds = fields(platform_cls)
|
|
183
|
+
field_name = [f.name for f in fds if f.metadata and 'help' in f.metadata]
|
|
184
|
+
field_type = {f.name: f.type for f in fds}
|
|
185
|
+
|
|
186
|
+
# Make data to the requested type
|
|
187
|
+
inputs = IdmConfigParser.retrieve_dict_config_block(field_type, section)
|
|
188
|
+
inputs.pop('type', None) # Remove 'type' dict from inputs since it is not a field to create platform
|
|
189
|
+
# Make sure the user values have the requested type
|
|
190
|
+
fs_kwargs = validate_user_inputs_against_dataclass(field_type, kwargs) # noqa: F841
|
|
191
|
+
|
|
192
|
+
# Update attr based on priority: #1 Code, #2 INI, #3 Default
|
|
193
|
+
for fn in set(kwargs.keys()).intersection(set(field_name)):
|
|
194
|
+
inputs[fn] = kwargs[fn]
|
|
195
|
+
|
|
196
|
+
extra_kwargs = set(kwargs.keys()) - set(field_name)
|
|
197
|
+
if len(extra_kwargs) > 0:
|
|
198
|
+
field_not_used_display = [" - {} = {}".format(fn, kwargs[fn]) for fn in extra_kwargs]
|
|
199
|
+
logger.warning("\n/!\\ WARNING: The following User Inputs are not used:")
|
|
200
|
+
logger.warning("\n".join(field_not_used_display))
|
|
201
|
+
|
|
202
|
+
field_not_used = set(inputs.keys()) - set(field_type.keys())
|
|
203
|
+
if len(field_not_used) > 0:
|
|
204
|
+
field_not_used_display = [" - {} = {}".format(fn, inputs[fn]) for fn in field_not_used]
|
|
205
|
+
logger.warning(f"\n[{block}]: /!\\ WARNING: the following Config Settings are not used when creating "
|
|
206
|
+
f"Platform:")
|
|
207
|
+
logger.warning("\n".join(field_not_used_display))
|
|
208
|
+
|
|
209
|
+
# Remove extra fields
|
|
210
|
+
for f in field_not_used:
|
|
211
|
+
inputs.pop(f)
|
|
212
|
+
|
|
213
|
+
# Display input info
|
|
214
|
+
cls._display_inputs(platform_cls, inputs)
|
|
215
|
+
|
|
216
|
+
# Now create Platform using the data with the correct data types
|
|
217
|
+
return platform_cls(**inputs)
|
|
218
|
+
|
|
219
|
+
@classmethod
|
|
220
|
+
def _get_platform_type(cls, block: str, **kwargs):
|
|
221
|
+
"""
|
|
222
|
+
Get the type of the platform from the INI configuration file, platform alias, or platform_kwargs.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
block: The section name in the configuration file or alias name.
|
|
226
|
+
kwargs: Keyword args to pass to platform
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
The type of the platform, section, and whether it is an alias.
|
|
230
|
+
"""
|
|
231
|
+
# If block is an alias
|
|
232
|
+
if block and block.upper() in cls._aliases:
|
|
233
|
+
platform_type, section, is_alias = cls._get_type_from_platform_alias(block)
|
|
234
|
+
|
|
235
|
+
# Else if block is a section in the idmtools.ini file
|
|
236
|
+
elif block and IdmConfigParser.has_section(block):
|
|
237
|
+
platform_type, section, is_alias = cls._get_type_from_ini(block)
|
|
238
|
+
|
|
239
|
+
# Else, all other cases
|
|
240
|
+
else:
|
|
241
|
+
platform_type, section, is_alias = cls._get_type_from_platform_kwargs(**kwargs)
|
|
242
|
+
|
|
243
|
+
return platform_type, section, is_alias
|
|
244
|
+
|
|
245
|
+
@classmethod
|
|
246
|
+
def _get_type_from_ini(cls, block: str):
|
|
247
|
+
"""
|
|
248
|
+
Get the type of the platform from the INI configuration file.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
block: The section name in the configuration file.
|
|
252
|
+
|
|
253
|
+
Returns:
|
|
254
|
+
The type of the platform, section, and whether it is an alias.
|
|
255
|
+
"""
|
|
256
|
+
section = IdmConfigParser.get_section(block)
|
|
257
|
+
platform_type = IdmConfigParser.get_option(block, 'type')
|
|
258
|
+
is_alias = False
|
|
259
|
+
return platform_type, section, is_alias
|
|
260
|
+
|
|
261
|
+
@classmethod
|
|
262
|
+
def _get_type_from_platform_alias(cls, block: str):
|
|
263
|
+
"""
|
|
264
|
+
Get the type of the platform from the platform alias.
|
|
265
|
+
|
|
266
|
+
Args:
|
|
267
|
+
block: The alias name.
|
|
268
|
+
|
|
269
|
+
Returns:
|
|
270
|
+
The type of the platform, section, and whether it is an alias.
|
|
271
|
+
"""
|
|
272
|
+
if logger.isEnabledFor(DEBUG):
|
|
273
|
+
logger.debug(f"Loading plugin from alias {block.upper()}")
|
|
274
|
+
props = cls._aliases[block.upper()]
|
|
275
|
+
platform_type = props[0].get_name()
|
|
276
|
+
section = props[1]
|
|
277
|
+
is_alias = True
|
|
278
|
+
return platform_type, section, is_alias
|
|
279
|
+
|
|
280
|
+
@classmethod
|
|
281
|
+
def _get_type_from_platform_kwargs(cls, **kwargs):
|
|
282
|
+
"""
|
|
283
|
+
Get the type of the platform from platform_kwargs.
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
kwargs: Keyword args to pass to platform
|
|
287
|
+
|
|
288
|
+
Returns:
|
|
289
|
+
The type of the platform, section, and whether it is an alias.
|
|
290
|
+
"""
|
|
291
|
+
section = kwargs
|
|
292
|
+
is_alias = False
|
|
293
|
+
platform_type = section.pop('type', None)
|
|
294
|
+
if not platform_type:
|
|
295
|
+
raise ValueError("Type must be specified in Platform constructor.")
|
|
296
|
+
return platform_type, section, is_alias
|
|
297
|
+
|
|
298
|
+
@classmethod
|
|
299
|
+
def _display_inputs(cls, platform_cls: object, inputs: dict):
|
|
300
|
+
"""
|
|
301
|
+
Display inputs required for platform creation on the console.
|
|
302
|
+
|
|
303
|
+
Args:
|
|
304
|
+
platform_cls: The platform object.
|
|
305
|
+
inputs: The inputs.
|
|
306
|
+
"""
|
|
307
|
+
from idmtools.core.logging import VERBOSE
|
|
308
|
+
|
|
309
|
+
if IdmConfigParser.is_output_enabled() and IdmConfigParser.get_option(None, "SHOW_PLATFORM_CONFIG",
|
|
310
|
+
't').lower() in TRUTHY_VALUES:
|
|
311
|
+
if os.getenv("IDMTOOLS_NO_CONFIG_WARNING", "F").lower() not in TRUTHY_VALUES:
|
|
312
|
+
user_logger.log(VERBOSE, f"\nInitializing {platform_cls.__name__} with:")
|
|
313
|
+
user_logger.log(VERBOSE, json.dumps(inputs, indent=3, cls=IDMJSONEncoder))
|
|
314
|
+
else:
|
|
315
|
+
user_logger.debug(f"\nInitializing {platform_cls.__name__} with:")
|
|
316
|
+
user_logger.debug(json.dumps(inputs, indent=3, cls=IDMJSONEncoder))
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Utilities functions/classes to fetch info that is useful for troubleshooting user issues.
|
|
3
|
+
|
|
4
|
+
Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
|
|
5
|
+
"""
|
|
6
|
+
import getpass
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from logging import getLogger
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
import platform
|
|
11
|
+
from typing import Optional, List, Dict
|
|
12
|
+
import os
|
|
13
|
+
from idmtools.utils.info import get_packages_list
|
|
14
|
+
|
|
15
|
+
logger = getLogger(__name__)
|
|
16
|
+
default_base_sir = os.getenv('IDMTOOLS_DATA_BASE_DIR', str(Path.home()))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_data_directory() -> str:
|
|
20
|
+
"""
|
|
21
|
+
Get our default data directory for a user.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
Default data directory for a user.
|
|
25
|
+
"""
|
|
26
|
+
return os.path.join(default_base_sir, '.local_data')
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def get_filtered_environment_vars(exclude=None) -> Dict[str, str]:
|
|
30
|
+
"""
|
|
31
|
+
Get environment vars excluding a specific set.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
exclude: If not provided, we default to using ['LS_COLORS', 'XDG_CONFIG_DIRS', 'PS1', 'XDG_DATA_DIRS']
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
Environment vars filtered for items specified
|
|
38
|
+
"""
|
|
39
|
+
ret = dict()
|
|
40
|
+
if exclude is None:
|
|
41
|
+
exclude = ['LS_COLORS', 'XDG_CONFIG_DIRS', 'PS1', 'XDG_DATA_DIRS']
|
|
42
|
+
for k, v in os.environ.items():
|
|
43
|
+
if k not in exclude:
|
|
44
|
+
ret[k] = v
|
|
45
|
+
return ret
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class SystemInformation:
|
|
50
|
+
"""
|
|
51
|
+
Utility class to provide details useful in troubleshooting issues.
|
|
52
|
+
"""
|
|
53
|
+
data_directory: Optional[str] = field(default=get_data_directory())
|
|
54
|
+
user: Optional[str] = getpass.getuser()
|
|
55
|
+
python_version: str = platform.python_version()
|
|
56
|
+
python_build: str = platform.python_build()
|
|
57
|
+
python_implementation = platform.python_implementation()
|
|
58
|
+
python_packages: List[str] = field(default_factory=get_packages_list)
|
|
59
|
+
environment_variables: Dict[str, str] = field(default_factory=get_filtered_environment_vars)
|
|
60
|
+
os_name: str = platform.system()
|
|
61
|
+
hostname: str = platform.node()
|
|
62
|
+
system_version: str = platform.version()
|
|
63
|
+
system_architecture: str = platform.machine()
|
|
64
|
+
system_processor: str = platform.processor()
|
|
65
|
+
system_architecture_details: str = platform.architecture()
|
|
66
|
+
default_docket_socket_path: str = '/var/run/docker.sock'
|
|
67
|
+
cwd: str = os.getcwd()
|
|
68
|
+
user_group_str: str = os.getenv("CURRENT_UID", "1000:1000")
|
|
69
|
+
version: str = None
|
|
70
|
+
|
|
71
|
+
def __post_init__(self):
|
|
72
|
+
"""
|
|
73
|
+
Load our version dynamically to prevent import issues.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
None
|
|
77
|
+
"""
|
|
78
|
+
from idmtools import __version__
|
|
79
|
+
self.version = __version__
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@dataclass
|
|
83
|
+
class LinuxSystemInformation(SystemInformation):
|
|
84
|
+
"""
|
|
85
|
+
LinuxSystemInformation adds linux specific properties.
|
|
86
|
+
"""
|
|
87
|
+
user_group_str: str = field(default_factory=lambda: os.getenv("CURRENT_UID", f'{os.getuid()}:{os.getgid()}'))
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class WindowsSystemInformation(SystemInformation):
|
|
91
|
+
"""
|
|
92
|
+
WindowsSystemInformation adds windows specific properties.
|
|
93
|
+
"""
|
|
94
|
+
default_docket_socket_path: str = '//var/run/docker.sock'
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def get_system_information() -> SystemInformation:
|
|
98
|
+
"""
|
|
99
|
+
Fetch the system-appropriate information inspection object.
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
:class:`SystemInformation` with platform-specific implementation.
|
|
103
|
+
"""
|
|
104
|
+
return LinuxSystemInformation() if platform.system() in ["Linux", "Darwin"] else WindowsSystemInformation()
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Define our tasks factory. This is crucial to build tasks when fetching from the server.
|
|
3
|
+
|
|
4
|
+
Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
|
|
5
|
+
"""
|
|
6
|
+
from logging import getLogger
|
|
7
|
+
from typing import NoReturn, Type
|
|
8
|
+
from idmtools.entities.itask import ITask
|
|
9
|
+
from idmtools.registry.task_specification import TaskSpecification
|
|
10
|
+
|
|
11
|
+
logger = getLogger(__name__)
|
|
12
|
+
TASK_BUILDERS = None
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class DynamicTaskSpecification(TaskSpecification):
|
|
16
|
+
"""
|
|
17
|
+
This class allows users to quickly define a spec for special tasks.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, task_type: Type[ITask], description: str = ''):
|
|
21
|
+
"""
|
|
22
|
+
Initialize our specification.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
task_type: Task type to register
|
|
26
|
+
description: Description to register with task
|
|
27
|
+
"""
|
|
28
|
+
self.task_type = task_type
|
|
29
|
+
self.description = description
|
|
30
|
+
|
|
31
|
+
def get(self, configuration: dict) -> ITask:
|
|
32
|
+
"""
|
|
33
|
+
Get an instance of our task using configuration.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
configuration: Configuration keyword args.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
Task with configuration specified
|
|
40
|
+
"""
|
|
41
|
+
return self.task_type(**configuration)
|
|
42
|
+
|
|
43
|
+
def get_description(self) -> str:
|
|
44
|
+
"""
|
|
45
|
+
Get description of our plugin.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
Returns the user-defined plugin description.
|
|
49
|
+
"""
|
|
50
|
+
return self.description
|
|
51
|
+
|
|
52
|
+
def get_type(self) -> Type[ITask]:
|
|
53
|
+
"""
|
|
54
|
+
Get our task type.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
Returns our task type
|
|
58
|
+
"""
|
|
59
|
+
return self.task_type
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class TaskFactory:
|
|
63
|
+
"""
|
|
64
|
+
TaskFactory allows creation of tasks that are derived from plugins.
|
|
65
|
+
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
DEFAULT_KEY = 'idmtools.entities.command_task.CommandTask'
|
|
69
|
+
|
|
70
|
+
def __init__(self):
|
|
71
|
+
"""
|
|
72
|
+
Initialize our Factory.
|
|
73
|
+
"""
|
|
74
|
+
global TASK_BUILDERS
|
|
75
|
+
if TASK_BUILDERS is None:
|
|
76
|
+
from idmtools.registry.task_specification import TaskPlugins
|
|
77
|
+
TASK_BUILDERS = TaskPlugins().get_plugin_map()
|
|
78
|
+
self._builders = TASK_BUILDERS
|
|
79
|
+
aliases = dict()
|
|
80
|
+
# register types as full paths as well
|
|
81
|
+
for _model, spec in self._builders.items():
|
|
82
|
+
try:
|
|
83
|
+
aliases[f'{spec.get_type().__module__}.{spec.get_type().__name__}'] = spec
|
|
84
|
+
aliases[f'{spec.get_type().__name__}'] = spec
|
|
85
|
+
except Exception as e:
|
|
86
|
+
logger.warning(f"Could not load alias for {spec}")
|
|
87
|
+
logger.exception(e)
|
|
88
|
+
self._builders.update(aliases)
|
|
89
|
+
|
|
90
|
+
def register(self, spec: TaskSpecification) -> NoReturn:
|
|
91
|
+
"""
|
|
92
|
+
Register a TaskSpecification dynamically.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
spec: Specification to register
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
None
|
|
99
|
+
"""
|
|
100
|
+
type_name = spec.get_type().__name__
|
|
101
|
+
module_name = {spec.get_type().__module__}
|
|
102
|
+
logger.debug(f'Registering task: {type_name} as both {type_name} and as {module_name}.{type_name}')
|
|
103
|
+
self._builders[type_name] = spec
|
|
104
|
+
self._builders[f'{module_name}.{type_name}'] = spec
|
|
105
|
+
|
|
106
|
+
def register_task(self, task: Type[ITask]) -> NoReturn:
|
|
107
|
+
"""
|
|
108
|
+
Dynamically register a class using the DynamicTaskSpecification.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
task: Task to register
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
None
|
|
115
|
+
"""
|
|
116
|
+
spec = DynamicTaskSpecification(task)
|
|
117
|
+
self.register(spec)
|
|
118
|
+
|
|
119
|
+
def create(self, key, fallback=None, **kwargs) -> ITask: # noqa: F821
|
|
120
|
+
"""
|
|
121
|
+
Create a task of type key.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
key: Type of task to create
|
|
125
|
+
fallback: Fallback task type. Default to DEFAULT_KEY if not provided
|
|
126
|
+
**kwargs: Optional arguments to pass to the task
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
Task with option specified
|
|
130
|
+
"""
|
|
131
|
+
if key is None:
|
|
132
|
+
key = self.DEFAULT_KEY
|
|
133
|
+
logger.warning(f'No task type tag found, assuming type: {key}')
|
|
134
|
+
|
|
135
|
+
if key not in self._builders:
|
|
136
|
+
if not fallback:
|
|
137
|
+
raise ValueError(f"The TaskFactory could not create an task of type {key}")
|
|
138
|
+
else:
|
|
139
|
+
return fallback()
|
|
140
|
+
|
|
141
|
+
task_spec: TaskSpecification = self._builders.get(key)
|
|
142
|
+
return task_spec.get(kwargs)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
task_factory = TaskFactory()
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Entities is the core entities used to build experiments or used as abstracts for those classes.
|
|
3
|
+
|
|
4
|
+
Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
|
|
5
|
+
"""
|
|
6
|
+
# flake8: noqa F821
|
|
7
|
+
from idmtools.entities.command_line import CommandLine
|
|
8
|
+
from idmtools.entities.ianalyzer import IAnalyzer
|
|
9
|
+
from idmtools.entities.suite import Suite
|
|
10
|
+
|