idmtools-platform-comps 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.
Files changed (64) hide show
  1. idmtools_platform_comps/__init__.py +25 -8
  2. idmtools_platform_comps/cli/__init__.py +4 -0
  3. idmtools_platform_comps/cli/cli_functions.py +50 -0
  4. idmtools_platform_comps/cli/comps.py +492 -0
  5. idmtools_platform_comps/comps_cli.py +48 -0
  6. idmtools_platform_comps/comps_operations/__init__.py +6 -0
  7. idmtools_platform_comps/comps_operations/asset_collection_operations.py +263 -0
  8. idmtools_platform_comps/comps_operations/experiment_operations.py +569 -0
  9. idmtools_platform_comps/comps_operations/simulation_operations.py +678 -0
  10. idmtools_platform_comps/comps_operations/suite_operations.py +228 -0
  11. idmtools_platform_comps/comps_operations/workflow_item_operations.py +269 -0
  12. idmtools_platform_comps/comps_platform.py +309 -0
  13. idmtools_platform_comps/plugin_info.py +168 -0
  14. idmtools_platform_comps/ssmt_operations/__init__.py +6 -0
  15. idmtools_platform_comps/ssmt_operations/simulation_operations.py +77 -0
  16. idmtools_platform_comps/ssmt_operations/workflow_item_operations.py +73 -0
  17. idmtools_platform_comps/ssmt_platform.py +44 -0
  18. idmtools_platform_comps/ssmt_work_items/__init__.py +4 -0
  19. idmtools_platform_comps/ssmt_work_items/comps_work_order_task.py +29 -0
  20. idmtools_platform_comps/ssmt_work_items/comps_workitems.py +113 -0
  21. idmtools_platform_comps/ssmt_work_items/icomps_workflowitem.py +71 -0
  22. idmtools_platform_comps/ssmt_work_items/work_order.py +54 -0
  23. idmtools_platform_comps/utils/__init__.py +4 -0
  24. idmtools_platform_comps/utils/assetize_output/__init__.py +4 -0
  25. idmtools_platform_comps/utils/assetize_output/assetize_output.py +125 -0
  26. idmtools_platform_comps/utils/assetize_output/assetize_ssmt_script.py +144 -0
  27. idmtools_platform_comps/utils/base_singularity_work_order.json +6 -0
  28. idmtools_platform_comps/utils/download/__init__.py +4 -0
  29. idmtools_platform_comps/utils/download/download.py +178 -0
  30. idmtools_platform_comps/utils/download/download_ssmt.py +81 -0
  31. idmtools_platform_comps/utils/download_experiment.py +116 -0
  32. idmtools_platform_comps/utils/file_filter_workitem.py +519 -0
  33. idmtools_platform_comps/utils/general.py +358 -0
  34. idmtools_platform_comps/utils/linux_mounts.py +73 -0
  35. idmtools_platform_comps/utils/lookups.py +123 -0
  36. idmtools_platform_comps/utils/package_version.py +489 -0
  37. idmtools_platform_comps/utils/python_requirements_ac/__init__.py +4 -0
  38. idmtools_platform_comps/utils/python_requirements_ac/create_asset_collection.py +155 -0
  39. idmtools_platform_comps/utils/python_requirements_ac/install_requirements.py +109 -0
  40. idmtools_platform_comps/utils/python_requirements_ac/requirements_to_asset_collection.py +374 -0
  41. idmtools_platform_comps/utils/python_version.py +40 -0
  42. idmtools_platform_comps/utils/scheduling.py +154 -0
  43. idmtools_platform_comps/utils/singularity_build.py +491 -0
  44. idmtools_platform_comps/utils/spatial_output.py +76 -0
  45. idmtools_platform_comps/utils/ssmt_utils/__init__.py +6 -0
  46. idmtools_platform_comps/utils/ssmt_utils/common.py +70 -0
  47. idmtools_platform_comps/utils/ssmt_utils/file_filter.py +568 -0
  48. idmtools_platform_comps/utils/sweeping.py +162 -0
  49. idmtools_platform_comps-0.0.2.dist-info/METADATA +100 -0
  50. idmtools_platform_comps-0.0.2.dist-info/RECORD +62 -0
  51. idmtools_platform_comps-0.0.2.dist-info/entry_points.txt +9 -0
  52. idmtools_platform_comps-0.0.2.dist-info/licenses/LICENSE.TXT +3 -0
  53. {idmtools_platform_comps-0.0.0.dev0.dist-info → idmtools_platform_comps-0.0.2.dist-info}/top_level.txt +1 -0
  54. ssmt_image/Dockerfile +52 -0
  55. ssmt_image/Makefile +21 -0
  56. ssmt_image/__init__.py +6 -0
  57. ssmt_image/bootstrap.sh +30 -0
  58. ssmt_image/build_docker_image.py +161 -0
  59. ssmt_image/pip.conf +3 -0
  60. ssmt_image/push_docker_image.py +49 -0
  61. ssmt_image/requirements.txt +9 -0
  62. idmtools_platform_comps-0.0.0.dev0.dist-info/METADATA +0 -41
  63. idmtools_platform_comps-0.0.0.dev0.dist-info/RECORD +0 -5
  64. {idmtools_platform_comps-0.0.0.dev0.dist-info → idmtools_platform_comps-0.0.2.dist-info}/WHEEL +0 -0
@@ -0,0 +1,228 @@
1
+ """idmtools comps suite operations.
2
+
3
+ Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
4
+ """
5
+ from dataclasses import dataclass, field
6
+ from typing import Any, List, Dict, Tuple, Union, Type, TYPE_CHECKING, Optional
7
+ from uuid import UUID
8
+ from logging import getLogger
9
+ from COMPS.Data import Suite as COMPSSuite, QueryCriteria, Experiment as COMPSExperiment, WorkItem
10
+ from idmtools.core import ItemType
11
+ from idmtools.entities import Suite
12
+ from idmtools.entities.iplatform_ops.iplatform_suite_operations import IPlatformSuiteOperations
13
+
14
+ if TYPE_CHECKING: # pragma: no cover
15
+ from idmtools_platform_comps.comps_platform import COMPSPlatform
16
+
17
+ logger = getLogger(__name__)
18
+ user_logger = getLogger('user')
19
+
20
+
21
+ @dataclass
22
+ class CompsPlatformSuiteOperations(IPlatformSuiteOperations):
23
+ """
24
+ Provides Suite operation to the COMPSPlatform.
25
+ """
26
+ platform: 'COMPSPlatform' # noqa F821
27
+ platform_type: Type = field(default=COMPSSuite)
28
+
29
+ def get(self, suite_id: UUID, columns: Optional[List[str]] = None, load_children: Optional[List[str]] = None,
30
+ query_criteria: Optional[QueryCriteria] = None, **kwargs) -> COMPSSuite:
31
+ """
32
+ Get COMPS Suite.
33
+
34
+ Args:
35
+ suite_id: Suite id
36
+ columns: Optional list of columns. Defaults to id and name
37
+ load_children: Optional list of children to load. Defaults to "tags", "configuration"
38
+ query_criteria: Optional query criteria
39
+ **kwargs:
40
+
41
+ Returns:
42
+ COMPSSuite
43
+ """
44
+ columns = columns or ["id", "name"]
45
+ children = load_children if load_children is not None else ["tags", "configuration"]
46
+ # Comps doesn't like getting uuids for some reason
47
+ query_criteria = query_criteria or QueryCriteria().select(columns).select_children(children)
48
+ s = COMPSSuite.get(id=str(suite_id), query_criteria=query_criteria)
49
+ return s
50
+
51
+ def platform_create(self, suite: Suite, **kwargs) -> Tuple[COMPSSuite, UUID]:
52
+ """
53
+ Create suite on COMPS.
54
+
55
+ Args:
56
+ suite: Suite to create
57
+ **kwargs:
58
+
59
+ Returns:
60
+ COMPS Suite object and a UUID
61
+ """
62
+ self.platform._login()
63
+
64
+ # Create suite
65
+ comps_suite = COMPSSuite(name=suite.name, description=suite.description)
66
+ comps_suite.set_tags(suite.tags)
67
+ comps_suite.save()
68
+
69
+ # Update suite uid
70
+ suite.uid = comps_suite.id
71
+ return comps_suite, suite.uid
72
+
73
+ def get_parent(self, suite: COMPSSuite, **kwargs) -> Any:
74
+ """
75
+ Get parent of suite. We always return None on COMPS.
76
+
77
+ Args:
78
+ suite:Suite to get parent of
79
+ **kwargs:
80
+
81
+ Returns:
82
+ None
83
+ """
84
+ return None
85
+
86
+ def get_children(self, suite: COMPSSuite, **kwargs) -> List[Union[COMPSExperiment, WorkItem]]:
87
+ """
88
+ Get children for a suite.
89
+
90
+ Args:
91
+ suite: Suite to get children for
92
+ **kwargs: Any arguments to pass on to loading functions
93
+
94
+ Returns:
95
+ List of COMPS Experiments/Workitems that are part of the suite
96
+ """
97
+ cols = kwargs.get("cols")
98
+ children = kwargs.get("children")
99
+ cols = cols or ["id", "name", "suite_id"]
100
+ children = children if children is not None else ["tags"]
101
+
102
+ children = suite.get_experiments(query_criteria=QueryCriteria().select(cols).select_children(children))
103
+ return children
104
+
105
+ def refresh_status(self, suite: Suite, **kwargs):
106
+ """
107
+ Refresh the status of a suite. On comps, this is done by refreshing all experiments.
108
+
109
+ Args:
110
+ suite: Suite to refresh status of
111
+ **kwargs:
112
+
113
+ Returns:
114
+ None
115
+ """
116
+ for experiment in suite.experiments:
117
+ self.platform.refresh_status(experiment)
118
+
119
+ def to_entity(self, suite: COMPSSuite, children: bool = True, **kwargs) -> Suite:
120
+ """
121
+ Convert a COMPS Suite to an IDM Suite.
122
+
123
+ Args:
124
+ suite: Suite to Convert
125
+ children: When true, load simulations, false otherwise
126
+ **kwargs:
127
+
128
+ Returns:
129
+ IDM Suite
130
+ """
131
+ # Creat a suite
132
+ obj = Suite()
133
+
134
+ # Set its correct attributes
135
+ obj.uid = suite.id
136
+ obj.name = suite.name
137
+ obj.description = suite.description
138
+ obj.tags = suite.tags
139
+ obj._platform_object = suite
140
+
141
+ # Convert all experiments
142
+ if children:
143
+ comps_exps = suite.get_experiments()
144
+ obj.experiments = []
145
+ for exp in comps_exps:
146
+ self.platform._experiments.to_entity(exp, parent=obj, **kwargs)
147
+ return obj
148
+
149
+ def create_sim_directory_map(self, suite_id: str) -> Dict:
150
+ """
151
+ Build simulation working directory mapping.
152
+ Args:
153
+ suite_id: suite id
154
+
155
+ Returns:
156
+ Dict of simulation id as key and working dir as value
157
+ """
158
+ # s = Suite.get(suite_id)
159
+ comps_suite = self.platform.get_item(suite_id, ItemType.SUITE, raw=True, force=True)
160
+ comps_exps = comps_suite.get_experiments(QueryCriteria().select('id'))
161
+ sims_map = {}
162
+ for exp in comps_exps:
163
+ r = self.platform._experiments.create_sim_directory_map(exp.id)
164
+ sims_map = {**sims_map, **r}
165
+ return sims_map
166
+
167
+ def platform_delete(self, suite_id: str) -> None:
168
+ """
169
+ Delete platform suite.
170
+ Args:
171
+ suite_id: platform suite id
172
+ Returns:
173
+ None
174
+ """
175
+ try:
176
+ comps_suite = self.platform.get_item(suite_id, ItemType.SUITE, raw=True)
177
+ except RuntimeError:
178
+ return
179
+
180
+ comps_exps = comps_suite.get_experiments()
181
+ for comps_exp in comps_exps:
182
+ try:
183
+ comps_exp.delete()
184
+ except RuntimeError:
185
+ logger.info(f"Could not delete the associated experiment ({comps_exp.id})...")
186
+ return
187
+ try:
188
+ comps_suite.delete()
189
+ except RuntimeError:
190
+ logger.info(f"Could not delete suite ({suite_id})...")
191
+ return
192
+
193
+ def get_assets(self, suite: Suite, files: List[str], **kwargs) -> Dict[str, bytearray]:
194
+ """
195
+ Fetch the files associated with a suite.
196
+
197
+ Args:
198
+ suite (Suite): The suite object.
199
+ files (List[str]): List of filenames to download.
200
+ **kwargs: Additional keyword arguments for platform-specific options.
201
+
202
+ Returns:
203
+ Dict[str, Dict[str, Dict[str, Dict[str, Union[str, bytearray]]]]]: A nested dictionary structured as::
204
+
205
+ {
206
+ "suite_id": {
207
+ "experiment_id": {
208
+ "simulation_id": {
209
+ "filename": file_content,
210
+ ...
211
+ },
212
+ ...
213
+ },
214
+ ...
215
+ }
216
+ }
217
+
218
+ File content may be returned as either a decoded string or a bytearray.
219
+ """
220
+ ret = dict()
221
+ if isinstance(suite, COMPSSuite):
222
+ comps_suite = suite
223
+ else:
224
+ comps_suite = suite.get_platform_object()
225
+ children = self.platform._get_children_for_platform_item(comps_suite)
226
+ for child in children:
227
+ ret[child.id] = self.platform._experiments.get_assets(child, files, **kwargs)
228
+ return ret
@@ -0,0 +1,269 @@
1
+ """idmtools comps workflow item operations.
2
+
3
+ Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
4
+ """
5
+ import json
6
+ import typing
7
+ from dataclasses import dataclass, field
8
+ from logging import getLogger, DEBUG
9
+ from typing import Any, Dict, List, Tuple, Type, Optional
10
+ from uuid import UUID
11
+ from COMPS.Data import QueryCriteria, WorkItem as COMPSWorkItem, WorkItemFile
12
+ from COMPS.Data.WorkItem import RelationType, WorkerOrPluginKey
13
+ from idmtools import IdmConfigParser
14
+ from idmtools.assets import AssetCollection
15
+ from idmtools.core import ItemType
16
+ from idmtools.core.interfaces.ientity import IEntity
17
+ from idmtools.entities.generic_workitem import GenericWorkItem
18
+ from idmtools.entities.iplatform_ops.iplatform_workflowitem_operations import IPlatformWorkflowItemOperations
19
+ from idmtools.entities.iworkflow_item import IWorkflowItem
20
+ from idmtools_platform_comps.utils.general import convert_comps_workitem_status
21
+
22
+ if typing.TYPE_CHECKING:
23
+ from idmtools_platform_comps.comps_platform import COMPSPlatform
24
+
25
+ logger = getLogger(__name__)
26
+ user_logger = getLogger('user')
27
+
28
+
29
+ @dataclass
30
+ class CompsPlatformWorkflowItemOperations(IPlatformWorkflowItemOperations):
31
+ """Provides IWorkflowItem COMPSPlatform."""
32
+ platform: 'COMPSPlatform' # noqa F821
33
+ platform_type: Type = field(default=COMPSWorkItem)
34
+
35
+ def get(self, workflow_item_id: UUID, columns: Optional[List[str]] = None, load_children: Optional[List[str]] = None,
36
+ query_criteria: Optional[QueryCriteria] = None, **kwargs) -> \
37
+ COMPSWorkItem:
38
+ """
39
+ Get COMPSWorkItem.
40
+
41
+ Args:
42
+ workflow_item_id: Item id
43
+ columns: Optional columns to load. Defaults to "id", "name", "state"
44
+ load_children: Optional list of COMPS Children objects to load. Defaults to "Tags"
45
+ query_criteria: Optional QueryCriteria
46
+ **kwargs:
47
+
48
+ Returns:
49
+ COMPSWorkItem
50
+ """
51
+ columns = columns or ["id", "name", "state", "environment_name"]
52
+ load_children = load_children if load_children is not None else ["tags"]
53
+ query_criteria = query_criteria or QueryCriteria().select(columns).select_children(load_children)
54
+ return COMPSWorkItem.get(workflow_item_id, query_criteria=query_criteria)
55
+
56
+ def platform_create(self, work_item: IWorkflowItem, **kwargs) -> Tuple[Any]:
57
+ """
58
+ Creates an workflow_item from an IDMTools work_item object.
59
+
60
+ Args:
61
+ work_item: WorkflowItem to create
62
+ **kwargs: Optional arguments mainly for extensibility
63
+
64
+ Returns:
65
+ Created platform item and the UUID of said item
66
+ """
67
+ self.send_assets(work_item)
68
+
69
+ if logger.isEnabledFor(DEBUG):
70
+ logger.debug(f"Creating workitem {work_item.name} of type {work_item.work_item_type}, "
71
+ f"{work_item.plugin_key} in {self.platform.environment}")
72
+ # Create a WorkItem
73
+ wi = COMPSWorkItem(name=work_item.name,
74
+ worker=WorkerOrPluginKey(work_item.work_item_type, work_item.plugin_key),
75
+ environment_name=self.platform.environment,
76
+ asset_collection_id=work_item.assets.id if len(work_item.assets) else None)
77
+
78
+ # Set tags
79
+ wi.set_tags({})
80
+ if work_item.tags:
81
+ wi.set_tags(work_item.tags)
82
+
83
+ # Update tags
84
+ wi.tags.update({'WorkItem_Type': work_item.work_item_type})
85
+
86
+ # Add work order file
87
+ wo = work_item.get_base_work_order()
88
+ wo.update(work_item.work_order)
89
+ wi.add_work_order(data=json.dumps(wo).encode('utf-8'))
90
+
91
+ # Add additional files
92
+ for af in work_item.transient_assets:
93
+ wi_file = WorkItemFile(af.filename, "input")
94
+ # Either the file has an absolute path or content
95
+ if af.absolute_path:
96
+ wi.add_file(wi_file, af.absolute_path)
97
+ else:
98
+ wi.add_file(wi_file, data=af.bytes)
99
+
100
+ if logger.isEnabledFor(DEBUG):
101
+ logger.debug("Sending workitem to COMPS")
102
+ # Save the work-item to the server
103
+ wi.save()
104
+ wi.refresh()
105
+
106
+ if logger.isEnabledFor(DEBUG):
107
+ logger.debug("Set the relationships")
108
+
109
+ # Ensure any items that are objects are converted to ids
110
+ for attr_name in ['related_experiments', 'related_simulations', 'related_work_items', 'related_asset_collections']:
111
+ if getattr(work_item, attr_name):
112
+ for item in getattr(work_item, attr_name):
113
+ getattr(wi, f'add_{attr_name[:-1]}')(item.id if isinstance(item, IEntity) else item, RelationType.DependsOn)
114
+
115
+ wi.save()
116
+
117
+ # Set the ID back in the object
118
+ work_item.uid = wi.id
119
+
120
+ return work_item
121
+
122
+ def platform_run_item(self, work_item: IWorkflowItem, **kwargs):
123
+ """
124
+ Start to rum COMPS WorkItem created from work_item.
125
+
126
+ Args:
127
+ work_item: workflow item
128
+
129
+ Returns: None
130
+ """
131
+ work_item.get_platform_object().commission()
132
+ if IdmConfigParser.is_output_enabled():
133
+ user_logger.info(
134
+ f"\nThe running WorkItem can be viewed at {self.platform.get_workitem_link(work_item)}\n"
135
+ )
136
+
137
+ def get_parent(self, work_item: IWorkflowItem, **kwargs) -> Any:
138
+ """
139
+ Returns the parent of item. If the platform doesn't support parents, you should throw a TopLevelItem error.
140
+
141
+ Args:
142
+ work_item: COMPS WorkItem
143
+ **kwargs: Optional arguments mainly for extensibility
144
+
145
+ Returns: item parent
146
+
147
+ Raise:
148
+ TopLevelItem
149
+ """
150
+ return None
151
+
152
+ def get_children(self, work_item: IWorkflowItem, **kwargs) -> List[Any]:
153
+ """
154
+ Returns the children of an workflow_item object.
155
+
156
+ Args:
157
+ work_item: WorkflowItem object
158
+ **kwargs: Optional arguments mainly for extensibility
159
+
160
+ Returns:
161
+ Children of work_item object
162
+ """
163
+ return None
164
+
165
+ def refresh_status(self, workflow_item: IWorkflowItem, **kwargs):
166
+ """
167
+ Refresh status for workflow item.
168
+
169
+ Args:
170
+ work_item: Item to refresh status for
171
+
172
+ Returns:
173
+ None
174
+ """
175
+ wi = self.get(workflow_item.uid, columns=["id", "state"], load_children=[])
176
+ workflow_item.status = convert_comps_workitem_status(wi.state) # convert_COMPS_status(wi.state)
177
+
178
+ def send_assets(self, workflow_item: IWorkflowItem, **kwargs):
179
+ """
180
+ Add asset as WorkItemFile.
181
+
182
+ Args:
183
+ workflow_item: workflow item
184
+
185
+ Returns: None
186
+ """
187
+ # Collect asset files
188
+ if workflow_item.assets and len(workflow_item.assets):
189
+ if logger.isEnabledFor(DEBUG):
190
+ logger.debug("Uploading assets for Workitem")
191
+ self.platform._assets.create(workflow_item.assets)
192
+
193
+ def list_assets(self, workflow_item: IWorkflowItem, **kwargs) -> List[str]:
194
+ """
195
+ Get list of asset files.
196
+
197
+ Args:
198
+ workflow_item: workflow item
199
+ **kwargs: Optional arguments mainly for extensibility
200
+
201
+ Returns: list of assets associated with WorkItem
202
+ """
203
+ wi: COMPSWorkItem = workflow_item.get_platform_object()
204
+ return wi.files
205
+
206
+ def get_assets(self, workflow_item: IWorkflowItem, files: List[str], **kwargs) -> Dict[str, bytearray]:
207
+ """
208
+ Retrieve files association with WorkItem.
209
+
210
+ Args:
211
+ workflow_item: workflow item
212
+ files: list of file paths
213
+ **kwargs: Optional arguments mainly for extensibility
214
+
215
+ Returns: dict with key/value: file_path/file_content
216
+ """
217
+ byte_arrs = workflow_item.retrieve_output_files(files)
218
+ return dict(zip(files, byte_arrs))
219
+
220
+ def to_entity(self, work_item: COMPSWorkItem, **kwargs) -> IWorkflowItem:
221
+ """
222
+ Converts the platform representation of workflow_item to idmtools representation.
223
+
224
+ Args:
225
+ work_item:Platform workflow_item object
226
+ kwargs: optional arguments mainly for extensibility
227
+
228
+ Returns:
229
+ IDMTools workflow item
230
+ """
231
+ # Creat a workflow item
232
+ # Eventually it would be nice to put the actual command here, but this requires fetching the work-order which is a bit to much overhead
233
+ obj = GenericWorkItem(name=work_item.name, command="")
234
+
235
+ # Set its correct attributes
236
+ obj.platform = self.platform
237
+ obj._platform_object = work_item
238
+ obj.uid = work_item.id
239
+ if work_item.asset_collection_id:
240
+ obj.assets = AssetCollection.from_id(work_item.asset_collection_id, platform=self.platform)
241
+ if work_item.files:
242
+ obj.transient_assets = self.platform._assets.to_entity(work_item.files)
243
+ obj.tags = work_item.tags
244
+ obj.status = convert_comps_workitem_status(work_item.state)
245
+ return obj
246
+
247
+ # def platform_run_item(self, workflow_item: IWorkflowItem, **kwargs):
248
+ # raise NotImplementedError("Running workflow items is not supported")
249
+
250
+ def get_related_items(self, item: IWorkflowItem, relation_type: RelationType) -> Dict[str, List[Dict[str, str]]]:
251
+ """
252
+ Get related WorkItems, Suites, Experiments, Simulations and AssetCollections.
253
+
254
+ Args:
255
+ item: workflow item
256
+ relation_type: RelationType
257
+
258
+ Returns: Dict
259
+ """
260
+ wi = self.platform.get_item(item.uid, ItemType.WORKFLOW_ITEM, raw=True)
261
+
262
+ ret = {}
263
+ ret['work_item'] = wi.get_related_work_items(relation_type)
264
+ ret['suite'] = wi.get_related_suites(relation_type)
265
+ ret['experiment'] = wi.get_related_experiments(relation_type)
266
+ ret['simulation'] = wi.get_related_simulations(relation_type)
267
+ ret['asset_collection'] = wi.get_related_asset_collections(relation_type)
268
+
269
+ return ret