Skip to content

Commit 3ed469e

Browse files
committed
allowing dws partition with use_job_duration to work with non-exlusive partition as well.
1 parent 17063f5 commit 3ed469e

5 files changed

Lines changed: 52 additions & 12 deletions

File tree

community/modules/compute/schedmd-slurm-gcp-v6-nodeset/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ modules. For support with the underlying modules, see the instructions in the
174174
| <a name="input_disk_resource_manager_tags"></a> [disk\_resource\_manager\_tags](#input\_disk\_resource\_manager\_tags) | (Optional) A set of key/value resource manager tag pairs to bind to the instance disks. Keys must be in the format tagKeys/{tag\_key\_id}, and values are in the format tagValues/456. | `map(string)` | `{}` | no |
175175
| <a name="input_disk_size_gb"></a> [disk\_size\_gb](#input\_disk\_size\_gb) | Size of boot disk to create for the partition compute nodes. | `number` | `50` | no |
176176
| <a name="input_disk_type"></a> [disk\_type](#input\_disk\_type) | Boot disk type, can be either hyperdisk-balanced, pd-ssd, pd-standard, pd-balanced, or pd-extreme. | `string` | `"pd-standard"` | no |
177-
| <a name="input_dws_flex"></a> [dws\_flex](#input\_dws\_flex) | If set and `enabled = true`, will utilize the DWS Flex Start to provision nodes.<br/> See: https://cloud.google.com/blog/products/compute/introducing-dynamic-workload-scheduler<br/> Options:<br/> - enable: Enable DWS Flex Start<br/> - max\_run\_duration: Maximum duration in seconds for the job to run, should not exceed 604,800 (one week).<br/> - use\_job\_duration: Use the job duration to determine the max\_run\_duration, if job duration is not set, max\_run\_duration will be used.<br/> - use\_bulk\_insert: Uses the legacy implementation of DWS Flex Start with Bulk Insert for non-accelerator instances<br/><br/>Limitations:<br/> - CAN NOT be used with reservations;<br/> - CAN NOT be used with placement groups;<br/> - If `use_job_duration` is enabled nodeset can be used in "exclusive" partitions only | <pre>object({<br/> enabled = optional(bool, true)<br/> max_run_duration = optional(number, 604800) # one week<br/> use_job_duration = optional(bool, false)<br/> use_bulk_insert = optional(bool, false)<br/> })</pre> | <pre>{<br/> "enabled": false<br/>}</pre> | no |
177+
| <a name="input_dws_flex"></a> [dws\_flex](#input\_dws\_flex) | If set and `enabled = true`, will utilize the DWS Flex Start to provision nodes.<br/> See: https://cloud.google.com/blog/products/compute/introducing-dynamic-workload-scheduler<br/> Options:<br/> - enable: Enable DWS Flex Start<br/> - max\_run\_duration: Maximum duration in seconds for the job to run, should not exceed 604,800 (one week).<br/> - use\_job\_duration: Use the job duration to determine the max\_run\_duration, if job duration is not set, max\_run\_duration will be used.<br/> - use\_bulk\_insert: Uses the legacy implementation of DWS Flex Start with Bulk Insert for non-accelerator instances<br/><br/>Limitations:<br/> - CAN NOT be used with reservations;<br/> - CAN NOT be used with placement groups; | <pre>object({<br/> enabled = optional(bool, true)<br/> max_run_duration = optional(number, 604800) # one week<br/> use_job_duration = optional(bool, false)<br/> use_bulk_insert = optional(bool, false)<br/> })</pre> | <pre>{<br/> "enabled": false<br/>}</pre> | no |
178178
| <a name="input_enable_confidential_vm"></a> [enable\_confidential\_vm](#input\_enable\_confidential\_vm) | Enable the Confidential VM configuration. Note: the instance image must support option. | `bool` | `false` | no |
179179
| <a name="input_enable_maintenance_reservation"></a> [enable\_maintenance\_reservation](#input\_enable\_maintenance\_reservation) | Enables slurm reservation for scheduled maintenance. | `bool` | `false` | no |
180180
| <a name="input_enable_opportunistic_maintenance"></a> [enable\_opportunistic\_maintenance](#input\_enable\_opportunistic\_maintenance) | On receiving maintenance notification, maintenance will be performed as soon as nodes becomes idle. | `bool` | `false` | no |

community/modules/compute/schedmd-slurm-gcp-v6-nodeset/variables.tf

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,6 @@ variable "dws_flex" {
610610
Limitations:
611611
- CAN NOT be used with reservations;
612612
- CAN NOT be used with placement groups;
613-
- If `use_job_duration` is enabled nodeset can be used in "exclusive" partitions only
614613
615614
EOD
616615

community/modules/compute/schedmd-slurm-gcp-v6-partition/main.tf

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@
1313
# limitations under the License.
1414

1515
locals {
16-
use_static = [for ns in concat(var.nodeset, var.nodeset_tpu) : ns.nodeset_name if ns.node_count_static > 0]
17-
uses_job_duration = length([for ns in var.nodeset : ns.dws_flex.use_job_duration if ns.dws_flex.use_job_duration]) > 0
16+
use_static = [for ns in concat(var.nodeset, var.nodeset_tpu) : ns.nodeset_name if ns.node_count_static > 0]
1817

1918
has_node = length(var.nodeset) > 0
2019
has_dyn = length(var.nodeset_dyn) > 0

community/modules/compute/schedmd-slurm-gcp-v6-partition/outputs.tf

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,6 @@ output "partitions" {
3232
condition = sum([for b in [local.has_node, local.has_dyn, local.has_tpu] : b ? 1 : 0]) == 1
3333
error_message = "Partition must contain exactly one type of nodeset."
3434
}
35-
36-
precondition {
37-
condition = !local.uses_job_duration || var.exclusive
38-
error_message = "`use_job_duration` can only be used in exclusive partitions"
39-
}
4035
}
4136

4237
output "nodeset" {

community/modules/scheduler/schedmd-slurm-gcp-v6-controller/modules/slurm_files/scripts/mig_flex.py

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,14 @@
1717
import util
1818
import uuid
1919
from addict import Dict as NSDict # type: ignore
20-
from datetime import timedelta
20+
from datetime import datetime, timedelta
2121
from collections import defaultdict
2222
import logging
2323
from time import sleep
2424

2525
log = logging.getLogger()
2626

27+
DWS_EOL_RESERVATION_DURATION = 10 # minutes
2728

2829
def _duration(flex_options: NSDict, job_id: Optional[int], lkp: util.Lookup) -> int:
2930
dur = flex_options.max_run_duration
@@ -40,6 +41,29 @@ def _duration(flex_options: NSDict, job_id: Optional[int], lkp: util.Lookup) ->
4041
log.info("Job TimeLimit cannot be less than 10 minutes or exceed one week")
4142
return dur
4243

44+
def _create_slurm_reservation(node_name: str, boot_time: datetime, run_duration: int, lkp: util.Lookup):
45+
"""
46+
Create a Slurm reservation starting at EOL - buffer time.
47+
"""
48+
eol = boot_time + timedelta(seconds=run_duration)
49+
start_str = eol.strftime("%Y-%m-%dT%H:%M:%S")
50+
reservation_name = f"dws-eol-{node_name}"
51+
log.debug(f"creating slurm reservation for {node_name}")
52+
try:
53+
util.run(f"{lkp.scontrol} create reservation user=slurm starttime={start_str} duration={DWS_EOL_RESERVATION_DURATION} nodes={node_name} reservationname={reservation_name} flags=maint,ignore_jobs")
54+
except Exception as e:
55+
log.error(f"Failed to create reservation for {node_name}: {e}")
56+
57+
def _delete_slurm_reservation(node_name: str, lkp: util.Lookup):
58+
"""
59+
Delete the Slurm reservation for the given node.
60+
"""
61+
reservation_name = f"dws-eol-{node_name}"
62+
try:
63+
util.run(f"{lkp.scontrol} delete reservation {reservation_name}")
64+
log.debug(f"Deleted Slurm reservation {reservation_name} for {node_name}")
65+
except Exception as e:
66+
log.error(f"Failed to delete reservation for {node_name}: {e}")
4367

4468
def resume_flex_chunk(nodes: List[str], job_id: Optional[int], lkp: util.Lookup) -> None:
4569
assert nodes
@@ -77,8 +101,9 @@ def resume_flex_chunk(nodes: List[str], job_id: Optional[int], lkp: util.Lookup)
77101
op = req.execute()
78102
res = util.wait_for_operation(op)
79103
assert "error" not in res, f"{res}"
80-
104+
81105
# Create resize request
106+
duration_seconds = _duration(nodeset.dws_flex, job_id, lkp)
82107
req = lkp.compute.regionInstanceGroupManagerResizeRequests().insert(
83108
project=lkp.project,
84109
region=region,
@@ -87,13 +112,29 @@ def resume_flex_chunk(nodes: List[str], job_id: Optional[int], lkp: util.Lookup)
87112
name="initial-resize",
88113
instances=[dict(name=n) for n in nodes],
89114
requested_run_duration=dict(
90-
seconds=_duration(nodeset.dws_flex, job_id, lkp)
115+
seconds=duration_seconds
91116
)
92117
)
93118
)
94119
util.log_api_request(req)
95120
op = req.execute()
96121
res = util.wait_for_operation(op)
122+
123+
# Create Slurm reservations if use_job_duration is set
124+
if nodeset.dws_flex.use_job_duration:
125+
# Get run duration (seconds)
126+
run_duration = duration_seconds
127+
for node_name in nodes:
128+
# Fetch instance creation time from GCP instance (via util.py)
129+
instance = lkp.instance(node_name)
130+
if(instance and instance.creation_timestamp):
131+
log.debug("creating with creation_timestamp")
132+
boot_time = instance.creation_timestamp # Already a datetime object
133+
else:
134+
boot_time = datetime.utcnow()
135+
log.debug("creating with utcnow time: {boot_time}")
136+
_create_slurm_reservation(node_name, boot_time, run_duration, lkp)
137+
97138
assert "error" not in res, f"{res}"
98139

99140
def _suspend_flex_mig(mig_self_link: str, nodes: List[str], lkp: util.Lookup) -> None:
@@ -144,6 +185,12 @@ def _suspend_flex_mig(mig_self_link: str, nodes: List[str], lkp: util.Lookup) ->
144185
op = req.execute()
145186

146187
res = util.wait_for_operation(op)
188+
189+
# Delete Slurm reservations for nodes being deprovisioned
190+
for node_name in nodes:
191+
log.info("delete dws reservation")
192+
_delete_slurm_reservation(node_name, lkp)
193+
147194
assert "error" not in res, f"{res}"
148195

149196
def _suspend_provisioning_inst(nodes:List[str], node_template:str, lkp: util.Lookup) -> None:

0 commit comments

Comments
 (0)