Skip to content

Commit 31e5021

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 json format 3. json.dump dictionary in output 4. Add unit test to check dict format. Reviewed By: kiukchung Differential Revision: D73180040
1 parent bec9317 commit 31e5021

File tree

3 files changed

+71
-1
lines changed

3 files changed

+71
-1
lines changed

torchx/cli/cmd_status.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
# pyre-strict
99

1010
import argparse
11+
import json
1112
import logging
1213
import sys
1314
from typing import List, Optional
@@ -46,6 +47,11 @@ def add_arguments(self, subparser: argparse.ArgumentParser) -> None:
4647
subparser.add_argument(
4748
"--roles", type=str, default="", help="comma separated roles to filter"
4849
)
50+
subparser.add_argument(
51+
"--json",
52+
action="store_true",
53+
help="output the status in JSON format",
54+
)
4955

5056
def run(self, args: argparse.Namespace) -> None:
5157
app_handle = args.app_handle
@@ -54,7 +60,10 @@ def run(self, args: argparse.Namespace) -> None:
5460
app_status = runner.status(app_handle)
5561
filter_roles = parse_list_arg(args.roles)
5662
if app_status:
57-
print(app_status.format(filter_roles))
63+
if args.json:
64+
print(json.dumps(app_status.to_json(filter_roles)))
65+
else:
66+
print(app_status.format(filter_roles))
5867
else:
5968
logger.error(
6069
f"AppDef: {app_id},"

torchx/specs/api.py

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

541+
def to_json(self) -> Dict[str, Any]:
542+
"""
543+
Convert the RoleStatus to a json object.
544+
"""
545+
return {
546+
"role": self.role,
547+
"replicas": [asdict(replica) for replica in self.replicas],
548+
}
549+
541550

542551
@dataclass
543552
class AppStatus:
@@ -657,6 +666,21 @@ def _format_role_status(
657666
replica_data += self._format_replica_status(replica)
658667
return f"{replica_data}"
659668

669+
def to_json(self, filter_roles: Optional[List[str]] = None) -> Dict[str, Any]:
670+
"""
671+
Convert the AppStatus to a json object, including RoleStatus.
672+
"""
673+
roles = self._get_role_statuses(self.roles, filter_roles)
674+
675+
return {
676+
"state": str(self.state),
677+
"num_restarts": self.num_restarts,
678+
"roles": [role_status.to_json() for role_status in roles],
679+
"msg": self.msg,
680+
"structured_error_msg": self.structured_error_msg,
681+
"url": self.ui_url,
682+
}
683+
660684
def format(
661685
self,
662686
filter_roles: Optional[List[str]] = None,
@@ -672,6 +696,7 @@ def format(
672696
"""
673697
roles_data = ""
674698
roles = self._get_role_statuses(self.roles, filter_roles)
699+
675700
for role_status in roles:
676701
roles_data += self._format_role_status(role_status)
677702
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)