Skip to content

Commit 17f6f3d

Browse files
authored
Merge pull request #1123 from jameshcorbett/resource-pool-status
Resource pool status
2 parents d51c81f + 801329f commit 17f6f3d

File tree

5 files changed

+267
-20
lines changed

5 files changed

+267
-20
lines changed

src/python/fluxion/resourcegraph/V1.py

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ def __init__(
3535
size,
3636
properties,
3737
path,
38+
status=0,
3839
):
3940
"""Constructor
4041
@@ -51,26 +52,26 @@ def __init__(
5152
size -- Amount of individual resources in this resource pool in unit
5253
properties -- Comma-separated list of property strings
5354
paths -- Fully qualified paths dictionary
55+
status -- Resource status (0 for 'up', 1 for 'down'), defaults to 0
5456
"""
5557
if not self.constraints(resType):
5658
raise ValueError(f"resource type={resType} unsupported by RV1")
57-
58-
super(FluxionResourcePoolV1, self).__init__(
59-
vtxId,
60-
metadata={
61-
"type": resType,
62-
"basename": basename,
63-
"name": name,
64-
"id": iden,
65-
"uniq_id": uniqId,
66-
"rank": rank,
67-
"exclusive": exclusive,
68-
"unit": unit,
69-
"size": size,
70-
"properties": properties,
71-
"paths": {"containment": path},
72-
},
73-
)
59+
metadata = {
60+
"type": resType,
61+
"basename": basename,
62+
"name": name,
63+
"id": iden,
64+
"uniq_id": uniqId,
65+
"rank": rank,
66+
"exclusive": exclusive,
67+
"unit": unit,
68+
"size": size,
69+
"properties": properties,
70+
"paths": {"containment": path},
71+
}
72+
if status != 0: # reduce the footprint by only adding status if nonzero
73+
metadata["status"] = status
74+
super().__init__(vtxId, metadata=metadata)
7475

7576
@staticmethod
7677
def constraints(resType):
@@ -87,7 +88,7 @@ def __init__(self, parentId, vtxId):
8788
parentId -- Parent vertex Id
8889
vtxId -- Child vertex Id
8990
"""
90-
super(FluxionResourceRelationshipV1, self).__init__(
91+
super().__init__(
9192
parentId,
9293
vtxId,
9394
directed=True,
@@ -105,7 +106,7 @@ def __init__(self, rv1):
105106
rv1 -- RV1 Dictorary that conforms to Flux RFC 20:
106107
Resource Set Specification Version 1
107108
"""
108-
super(FluxionResourceGraphV1, self).__init__()
109+
super().__init__()
109110
self._uniqId = 0
110111
self._rv1NoSched = rv1
111112
self._encode()

t/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ set(ALL_TESTS
8585
t6002-graph-hwloc.t
8686
t8001-util-ion-R.t
8787
t9001-golang-basic.t
88+
python/t10001-resourcegraph.py
8889
)
8990
foreach(test ${ALL_TESTS})
9091
flux_add_test(NAME ${test}

t/Makefile.am

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@ TESTS = \
102102
t6001-match-formats.t \
103103
t6002-graph-hwloc.t \
104104
t8001-util-ion-R.t \
105-
t9001-golang-basic.t
105+
t9001-golang-basic.t \
106+
python/t10001-resourcegraph.py
106107

107108
check_SCRIPTS = $(TESTS)
108109

t/python/pycotap/__init__.py

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
#!/usr/bin/env python
2+
# coding=utf-8
3+
4+
# Copyright (c) 2015 Remko Tronçon (https://el-tramo.be)
5+
# Released under the MIT license
6+
# See COPYING for details
7+
8+
9+
import base64
10+
import sys
11+
import unittest
12+
13+
if sys.hexversion >= 0x03000000:
14+
from io import StringIO
15+
else:
16+
from StringIO import StringIO
17+
18+
# Log modes
19+
class LogMode(object):
20+
LogToError, LogToDiagnostics, LogToYAML, LogToAttachment = range(4)
21+
22+
23+
class TAPTestResult(unittest.TestResult):
24+
def __init__(self, output_stream, error_stream, message_log, test_output_log):
25+
super(TAPTestResult, self).__init__(self, output_stream)
26+
self.output_stream = output_stream
27+
self.error_stream = error_stream
28+
self.orig_stdout = None
29+
self.orig_stderr = None
30+
self.message = error_stream
31+
self.test_output = None
32+
self.message_log = message_log
33+
self.test_output_log = test_output_log
34+
self.output_stream.write("TAP version 13\n")
35+
36+
def print_raw(self, text):
37+
self.output_stream.write(text)
38+
self.output_stream.flush()
39+
40+
def print_result(self, result, test, directive=None):
41+
self.output_stream.write("%s %d %s" % (result, self.testsRun, test.id()))
42+
if directive:
43+
self.output_stream.write(" # " + directive)
44+
self.output_stream.write("\n")
45+
self.output_stream.flush()
46+
47+
def ok(self, test, directive=None):
48+
self.print_result("ok", test, directive)
49+
50+
def not_ok(self, test):
51+
self.print_result("not ok", test)
52+
53+
def startTest(self, test):
54+
self.orig_stdout = sys.stdout
55+
self.orig_stderr = sys.stderr
56+
if self.message_log == LogMode.LogToError:
57+
self.message = self.error_stream
58+
else:
59+
self.message = StringIO()
60+
if self.test_output_log == LogMode.LogToError:
61+
self.test_output = self.error_stream
62+
else:
63+
self.test_output = StringIO()
64+
65+
if self.message_log == self.test_output_log:
66+
self.test_output = self.message
67+
68+
sys.stdout = sys.stderr = self.test_output
69+
super(TAPTestResult, self).startTest(test)
70+
71+
def stopTest(self, test):
72+
super(TAPTestResult, self).stopTest(test)
73+
sys.stdout = self.orig_stdout
74+
sys.stderr = self.orig_stderr
75+
if self.message_log == self.test_output_log:
76+
logs = [(self.message_log, self.message, "output")]
77+
else:
78+
logs = [
79+
(self.test_output_log, self.test_output, "test_output"),
80+
(self.message_log, self.message, "message"),
81+
]
82+
for log_mode, log, log_name in logs:
83+
if log_mode != LogMode.LogToError:
84+
output = log.getvalue()
85+
if len(output):
86+
if log_mode == LogMode.LogToYAML:
87+
self.print_raw(" ---\n")
88+
self.print_raw(" " + log_name + ": |\n")
89+
self.print_raw(
90+
" " + output.rstrip().replace("\n", "\n ") + "\n"
91+
)
92+
self.print_raw(" ...\n")
93+
elif log_mode == LogMode.LogToAttachment:
94+
self.print_raw(" ---\n")
95+
self.print_raw(" " + log_name + ":\n")
96+
self.print_raw(" File-Name: " + log_name + ".txt\n")
97+
self.print_raw(" File-Type: text/plain\n")
98+
self.print_raw(
99+
" File-Content: " + base64.b64encode(output) + "\n"
100+
)
101+
self.print_raw(" ...\n")
102+
else:
103+
self.print_raw(
104+
"# " + output.rstrip().replace("\n", "\n# ") + "\n"
105+
)
106+
107+
def addSuccess(self, test):
108+
super(TAPTestResult, self).addSuccess(test)
109+
self.ok(test)
110+
111+
def addError(self, test, err):
112+
super(TAPTestResult, self).addError(test, err)
113+
self.message.write(self.errors[-1][1] + "\n")
114+
self.not_ok(test)
115+
116+
def addFailure(self, test, err):
117+
super(TAPTestResult, self).addFailure(test, err)
118+
self.message.write(self.failures[-1][1] + "\n")
119+
self.not_ok(test)
120+
121+
def addSkip(self, test, reason):
122+
super(TAPTestResult, self).addSkip(test, reason)
123+
self.ok(test, "SKIP " + reason)
124+
125+
def addExpectedFailure(self, test, err):
126+
super(TAPTestResult, self).addExpectedFailure(test, err)
127+
self.message.write(self.expectedFailures[-1][1] + "\n")
128+
self.ok(test)
129+
130+
def addUnexpectedSuccess(self, test):
131+
super(TAPTestResult, self).addUnexpectedSuccess(self, test)
132+
self.not_ok(test)
133+
134+
def printErrors(self):
135+
self.print_raw("1..%d\n" % self.testsRun)
136+
137+
138+
class TAPTestRunner(unittest.TextTestRunner):
139+
def __init__(
140+
self,
141+
message_log=LogMode.LogToYAML,
142+
test_output_log=LogMode.LogToDiagnostics,
143+
output_stream=sys.stdout,
144+
error_stream=sys.stderr,
145+
):
146+
self.output_stream = output_stream
147+
self.error_stream = error_stream
148+
self.message_log = message_log
149+
self.test_output_log = test_output_log
150+
151+
def run(self, test):
152+
result = TAPTestResult(
153+
self.output_stream,
154+
self.error_stream,
155+
self.message_log,
156+
self.test_output_log,
157+
)
158+
test(result)
159+
result.printErrors()
160+
161+
return result

t/python/t10001-resourcegraph.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#!/usr/bin/env python3
2+
3+
###############################################################
4+
# Copyright 2023 Lawrence Livermore National Security, LLC
5+
# (c.f. AUTHORS, NOTICE.LLNS, COPYING)
6+
#
7+
# This file is part of the Flux resource manager framework.
8+
# For details, see https://github.com/flux-framework.
9+
#
10+
# SPDX-License-Identifier: LGPL-3.0
11+
12+
import unittest
13+
import json
14+
import sys
15+
import pathlib
16+
17+
from pycotap import TAPTestRunner
18+
19+
# add fluxion to sys.path
20+
sys.path.insert(0, str(pathlib.Path(__file__).absolute().parents[2] / "src" / "python"))
21+
22+
from fluxion.resourcegraph.V1 import (
23+
FluxionResourceGraphV1,
24+
FluxionResourcePoolV1,
25+
FluxionResourceRelationshipV1,
26+
)
27+
28+
RV1 = {
29+
"version": 1,
30+
"execution": {
31+
"R_lite": [{"rank": "0", "children": {"core": "0-4"}}],
32+
"starttime": 0.0,
33+
"expiration": 0.0,
34+
"nodelist": ["compute01"],
35+
},
36+
}
37+
38+
RV1_2 = {
39+
"version": 1,
40+
"execution": {
41+
"R_lite": [{"rank": "0-10", "children": {"gpu": "0-1", "core": "0-7"}}],
42+
"starttime": 0.0,
43+
"expiration": 0.0,
44+
"nodelist": ["compute[0-10]"],
45+
},
46+
}
47+
48+
49+
class TestResourceGraph(unittest.TestCase):
50+
"""Test for the ResourceGraph class."""
51+
52+
def _check_metadata(self, metadata):
53+
if metadata["type"] in ("node", "core", "gpu", "cluster"):
54+
self.assertEqual(metadata["unit"], "")
55+
self.assertEqual(metadata["size"], 1)
56+
self.assertEqual(metadata["properties"], [])
57+
else:
58+
raise ValueError(metadata["type"])
59+
60+
def test_basic(self):
61+
graph = FluxionResourceGraphV1(RV1)
62+
self.assertTrue(graph.is_directed())
63+
j = graph.to_JSON()
64+
json.dumps(j) # make sure it doesn't throw an error
65+
self.assertTrue(j["graph"]["directed"])
66+
self.assertEqual(len(j["graph"]["nodes"]), len(graph.get_nodes()))
67+
self.assertEqual(len(j["graph"]["edges"]), len(graph.get_edges()))
68+
for node in graph.get_nodes():
69+
self._check_metadata(node.get_metadata())
70+
71+
def test_basic_2(self):
72+
graph = FluxionResourceGraphV1(RV1_2)
73+
self.assertTrue(graph.is_directed())
74+
j = graph.to_JSON()
75+
json.dumps(j)
76+
self.assertTrue(j["graph"]["directed"])
77+
self.assertEqual(len(j["graph"]["nodes"]), len(graph.get_nodes()))
78+
self.assertEqual(len(j["graph"]["edges"]), len(graph.get_edges()))
79+
for node in graph.get_nodes():
80+
self._check_metadata(node.get_metadata())
81+
82+
83+
unittest.main(testRunner=TAPTestRunner())

0 commit comments

Comments
 (0)