Skip to content

Commit 90da5b9

Browse files
nghuiqinfacebook-github-bot
authored andcommitted
support AppStatus json output format (pytorch#1045)
Summary: Enable json output format to show AppStatus What added: 1. Add `--json` subcommand for `status`, default is False 2. Parse both AppStatus and RoleStatus to dictionary format 3. json.dump dictionary in output 4. Add unit test to check dict format. Differential Revision: D73180040
1 parent bec9317 commit 90da5b9

File tree

3 files changed

+64
-1
lines changed

3 files changed

+64
-1
lines changed

torchx/cli/cmd_status.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ def add_arguments(self, subparser: argparse.ArgumentParser) -> None:
4646
subparser.add_argument(
4747
"--roles", type=str, default="", help="comma separated roles to filter"
4848
)
49+
subparser.add_argument(
50+
"--json",
51+
action="store_true",
52+
help="output the status in JSON format",
53+
)
4954

5055
def run(self, args: argparse.Namespace) -> None:
5156
app_handle = args.app_handle
@@ -54,7 +59,10 @@ def run(self, args: argparse.Namespace) -> None:
5459
app_status = runner.status(app_handle)
5560
filter_roles = parse_list_arg(args.roles)
5661
if app_status:
57-
print(app_status.format(filter_roles))
62+
if args.json:
63+
print(json.dumps(app_status.to_json(filter_roles)))
64+
else:
65+
print(app_status.format(filter_roles))
5866
else:
5967
logger.error(
6068
f"AppDef: {app_id},"

torchx/specs/api.py

+19
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,12 @@ class RoleStatus:
538538
role: str
539539
replicas: List[ReplicaStatus]
540540

541+
def to_json(self) -> Dict[str, Any]:
542+
return {
543+
"role": self.role,
544+
"replicas": [asdict(replica) for replica in self.replicas],
545+
}
546+
541547

542548
@dataclass
543549
class AppStatus:
@@ -657,6 +663,18 @@ def _format_role_status(
657663
replica_data += self._format_replica_status(replica)
658664
return f"{replica_data}"
659665

666+
def to_json(self, filter_roles: Optional[List[str]] = None) -> Dict[str, Any]:
667+
roles = self._get_role_statuses(self.roles, filter_roles)
668+
669+
return {
670+
"state": str(self.state),
671+
"num_restarts": self.num_restarts,
672+
"roles": [role_status.to_json() for role_status in roles],
673+
"msg": self.msg,
674+
"structured_error_msg": self.structured_error_msg,
675+
"url": self.ui_url,
676+
}
677+
660678
def format(
661679
self,
662680
filter_roles: Optional[List[str]] = None,
@@ -672,6 +690,7 @@ def format(
672690
"""
673691
roles_data = ""
674692
roles = self._get_role_statuses(self.roles, filter_roles)
693+
675694
for role_status in roles:
676695
roles_data += self._format_role_status(role_status)
677696
return Template(_APP_STATUS_FORMAT_TEMPLATE).substitute(

torchx/specs/test/api_test.py

+36
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,42 @@ def test_format_app_status(self) -> None:
176176
# Split and compare to aviod AssertionError.
177177
self.assertEqual(expected_message.split(), actual_message.split())
178178

179+
def test_app_status_in_json(self) -> None:
180+
app_status = self._get_test_app_status()
181+
result = app_status.to_json()
182+
error_msg = '{"message":{"message":"error","errorCode":-1,"extraInfo":{"timestamp":1293182}}}'
183+
self.assertDictEqual(
184+
result,
185+
{
186+
"state": "RUNNING",
187+
"num_restarts": 0,
188+
"roles": [
189+
{
190+
"role": "worker",
191+
"replicas": [
192+
{
193+
"id": 0,
194+
"state": 5,
195+
"role": "worker",
196+
"hostname": "localhost",
197+
"structured_error_msg": error_msg,
198+
},
199+
{
200+
"id": 1,
201+
"state": 3,
202+
"role": "worker",
203+
"hostname": "localhost",
204+
"structured_error_msg": "<NONE>",
205+
},
206+
],
207+
}
208+
],
209+
"msg": "",
210+
"structured_error_msg": "<NONE>",
211+
"url": None,
212+
},
213+
)
214+
179215

180216
class ResourceTest(unittest.TestCase):
181217
def test_copy_resource(self) -> None:

0 commit comments

Comments
 (0)