Skip to content

Commit 6eba410

Browse files
authored
Merge pull request #1 from compspec/add-transformers
wip: creation of subsystem solver as an interface
2 parents eea6fbd + 4db2cdf commit 6eba410

17 files changed

+3856
-230
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,18 @@ We want to:
1818

1919
See [examples/fractale](examples/fractale) for a detailed walk-through of the above.
2020

21+
For graph tool:
22+
23+
```bash
24+
conda install -c conda-forge graph-tool
25+
```
26+
27+
## Questions
28+
29+
- Should other subsystem types have edges? How used?
30+
- Should we try to map them to nodes in the graph or use another means (or assume global across cluster nodes as we do now)?
31+
- Can we simplify spack subsystem graph (it's really big...)
32+
2133
<!-- ⭐️ [Documentation](https://compspec.github.io/fractale) ⭐️ -->
2234

2335
## License

examples/fractale/README.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ fractale generate --cluster A spack /home/vanessa/Desktop/Code/spack
2525

2626
## Satisfy Request
2727

28+
Satisfy asks two questions:
29+
30+
1. Which clusters have the subsystem resources that I need?
31+
2. Which clusters have the job resources that I need (containment subsystem)?
32+
2833
This is the step where we want to say "Run gromacs on 2-4 nodes with these requirements." Since we haven't formalized a way to do that, I'm going to start with a flux jobspec, and then add attributes that can be used to search our subsystems. For example, I generated [software-gromacs.json](software-gromacs.json) with:
2934

3035
```bash
@@ -47,6 +52,40 @@ fractale satisfy ./examples/fractale/software-curl.yaml
4752
fractale satisfy ./examples/fractale/software-curl.json
4853
```
4954

55+
Try the graph backend, install [graph-tool](https://graph-tool.skewed.de/installation.html) then:
56+
57+
```bash
58+
export PYTHONPATH=/home/vanessa/anaconda3/lib/python3.12/site-packages
59+
fractale satisfy --solver graph ./examples/fractale/software-curl.yaml
60+
fractale satisfy --solver graph ./examples/fractale/software-curl.json
61+
```
62+
```console
63+
=> 🍇 Loading cluster "a" subsystem "containment"
64+
=> 🍇 Loading cluster "a" subsystem "modules"
65+
=> 🍇 Loading cluster "a" subsystem "spack"
66+
=> Exploring cluster "a" containment subsystem
67+
(1/1) satisfied resource core
68+
Cluster "a" is a match
69+
```
70+
71+
Here is for a nested slot:
72+
73+
```bash
74+
fractale satisfy --solver graph ./examples/fractale/software-nested-slot.yaml
75+
```
76+
```console
77+
=> 🍇 Loading cluster "a" subsystem "containment"
78+
=> 🍇 Loading cluster "a" subsystem "modules"
79+
=> 🍇 Loading cluster "a" subsystem "spack"
80+
=> Exploring cluster "a" containment subsystem
81+
(1/1) satisfied resource socket
82+
(1/4) found resource core
83+
(2/4) found resource core
84+
(3/4) found resource core
85+
(4/4) satisfied resource core
86+
Cluster "a" is a match
87+
```
88+
5089
By default, the above assumes subsystems located in the fractale home. If you want to adjust that, set `fractale --config-dir=<path> satisfy...` to adjust that (and note you will need to have generated the tree here. What we basically do with satisfy is build a database with tables for:
5190

5291
- clusters
@@ -67,4 +106,32 @@ And right now the search is just over attributes to find matching clusters. E.g.
67106
}
68107
```
69108

109+
Here is a jobspec that can't be satisfied because we ask for too many resources given the cluster [containment-subsystem.json]([containment-subsystem.json).
110+
111+
```bash
112+
fractale satisfy ./examples/fractale/jobspec-containment-unsatisfied.yaml
113+
```
114+
```console
115+
=> 🍇 Loading cluster "a" subsystem "containment"
116+
=> 🍇 Loading cluster "a" subsystem "modules"
117+
=> 🍇 Loading cluster "a" subsystem "spack"
118+
(2) SELECT * from subsystems WHERE type = 'software';
119+
=> No Matches due to containment
120+
```
70121
We likely want to have a more structured query syntax that can handle AND, OR, and other specifics. The actual search should remain general to support any generic key/value pair of attributes. My database structure and queries are also bad.
122+
123+
## Save
124+
125+
We can save an image of our subystem for a cluster. E.g.,
126+
127+
```bash
128+
$ fractale save a --out ./examples/fractale/cluster-a-containment.png
129+
```
130+
```console
131+
=> 🍇 Loading cluster "a" subsystem "containment"
132+
=> 🍇 Loading cluster "a" subsystem "modules"
133+
=> 🍇 Loading cluster "a" subsystem "spack"
134+
Saving to "./examples/fractale/cluster-a-containment.png"
135+
```
136+
137+
<img src="./cluster-a-containment.svg">

examples/fractale/cluster-a-containment-graph.svg

Lines changed: 2780 additions & 0 deletions
Loading
111 KB
Loading
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
version: 1
2+
resources:
3+
- type: slot
4+
count: 1
5+
with:
6+
- type: core
7+
count: 100
8+
label: task
9+
tasks:
10+
- command:
11+
- gmx
12+
slot: task
13+
count:
14+
per_slot: 1
15+
attributes:
16+
system:
17+
duration: 0
18+
requires:
19+
software:
20+
- name: curl
21+
type: binary
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
version: 1
2+
resources:
3+
- type: slot
4+
count: 2
5+
with:
6+
- type: socket
7+
count: 1
8+
with:
9+
- type: core
10+
count: 4
11+
label: task
12+
tasks:
13+
- command:
14+
- gmx
15+
slot: task
16+
count:
17+
per_slot: 1
18+
attributes:
19+
system:
20+
duration: 0
21+
requires:
22+
software:
23+
- name: curl
24+
type: binary

fractale/cli/__init__.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from compspec.plugin.registry import PluginRegistry
99

1010
import fractale
11+
import fractale.defaults as defaults
1112
from fractale.logger import setup_logger
1213

1314
# Generate the plugin registry to add parsers
@@ -71,15 +72,31 @@ def get_parser():
7172
generate.add_argument("-c", "--cluster", help="cluster name")
7273

7374
# run.add_argument("-t", "--transform", help="transformer to use", default="flux")
75+
save = subparsers.add_parser(
76+
"save",
77+
formatter_class=argparse.RawTextHelpFormatter,
78+
description="save a picture of a subsystem graph",
79+
)
80+
save.add_argument("cluster", help="cluster to save")
81+
save.add_argument(
82+
"--subsystem", help="cluster to save (defaults to containment)", default="containment"
83+
)
84+
save.add_argument("--out", help="output file name")
7485

7586
# This does just the user space subsystem match
7687
satisfy = subparsers.add_parser(
7788
"satisfy",
7889
formatter_class=argparse.RawTextHelpFormatter,
7990
description="determine clusters that satisfy a jobspec based on user subsystems",
8091
)
81-
for cmd in [satisfy]:
82-
cmd.add_argument("jobspec", help="jobspec yaml or json file")
92+
satisfy.add_argument("jobspec", help="jobspec yaml or json file")
93+
for cmd in [satisfy, save]:
94+
cmd.add_argument(
95+
"--solver",
96+
help="subsystem solved backend",
97+
default=defaults.solver_backend_default,
98+
choices=defaults.solver_backends,
99+
)
83100

84101
extractors = generate.add_subparsers(
85102
title="generate",
@@ -131,6 +148,8 @@ def help(return_code=0):
131148
from .generate_subsystem import main
132149
elif args.command == "satisfy":
133150
from .satisfy import main
151+
elif args.command == "save":
152+
from .save import main
134153
else:
135154
help(1)
136155
global registry

fractale/cli/satisfy.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import sys
44

55
from fractale.store import FractaleStore
6-
from fractale.subsystem import get_subsystem_registry
6+
from fractale.subsystem import get_subsystem_solver
77

88

99
def main(args, extra, **kwargs):
@@ -12,6 +12,6 @@ def main(args, extra, **kwargs):
1212
This is a fairly simple (flat) check.
1313
"""
1414
store = FractaleStore(args.config_dir)
15-
registry = get_subsystem_registry(store.clusters_root)
16-
is_satisfied = registry.satisfied(args.jobspec)
15+
solver = get_subsystem_solver(store.clusters_root, args.solver)
16+
is_satisfied = solver.satisfied(args.jobspec)
1717
sys.exit(0 if is_satisfied else -1)

fractale/cli/save.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#!/usr/bin/env python
2+
3+
import sys
4+
5+
from fractale.store import FractaleStore
6+
from fractale.subsystem import get_subsystem_solver
7+
8+
9+
def main(args, extra, **kwargs):
10+
"""
11+
Save a cluster and subsystem graph.
12+
"""
13+
store = FractaleStore(args.config_dir)
14+
solver = get_subsystem_solver(store.clusters_root, args.solver)
15+
outfile = args.out
16+
if not outfile:
17+
outfile = f"cluster-{args.cluster}-{args.subsystem}-{args.solver}.svg"
18+
solver.save(args.cluster, args.subsystem, outfile)

fractale/defaults.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
fractale_dir = ".fractale"
22
valid_settings = {"sharedfs", "stage"}
33
sharedfs = True
4+
solver_backends = ["database", "graph"]
5+
solver_backend_default = "graph"

0 commit comments

Comments
 (0)