From 41600ed85f5d9b1ff75fda69d78e5b69b3eaa9fc Mon Sep 17 00:00:00 2001 From: romanceformoon Date: Mon, 5 Jul 2021 19:26:17 +0900 Subject: [PATCH 1/9] release: Add command to get available resources --- src/ai/backend/client/cli/manager.py | 42 +++++++++++++++++++++++++- src/ai/backend/client/func/resource.py | 11 +++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/ai/backend/client/cli/manager.py b/src/ai/backend/client/cli/manager.py index 8b81c4b5..0914bfa3 100644 --- a/src/ai/backend/client/cli/manager.py +++ b/src/ai/backend/client/cli/manager.py @@ -9,8 +9,9 @@ from . import main from .interaction import ask_yn -from .pretty import print_done, print_error, print_fail, print_info, print_wait +from .pretty import print_done, print_error, print_fail, print_info, print_wait, print_warn from ..session import Session +from .config import get_config @main.group() @@ -204,3 +205,42 @@ def exclude_agents(agent_ids): except Exception as e: print_error(e) sys.exit(1) + + +@main.command() +@click.argument('scaling_group', metavar='SCALING_GROUP', default='default') +@click.argument('group', metavar='GROUP', default='default') +@click.option('-a', '--all', is_flag=True, + help='Get all resources of group.') +def get_resources(scaling_group, group, all): + ''' + Get available resources from the scaling groups. + ''' + config = get_config() + if config.endpoint_type != 'session': + print_warn('To use get-resources, your endpoint type must be "session".') + raise click.Abort() + + with Session() as session: + ret = session.Resource.get_available_resources(scaling_group, group) + print(f'Total remaining resources of group [{group}]:') + print(' CPU:', ret['scaling_group_remaining']['cpu']) + print(' Memory:', ret['scaling_group_remaining']['mem']) + print('Each resources of scaling groups:') + if not all: + print(f' [{scaling_group}]') + print(' Using:') + print(' CPU:', ret['scaling_groups'][scaling_group]['using']['cpu']) + print(' Memory:', ret['scaling_groups'][scaling_group]['using']['mem']) + print(' Remaining:') + print(' CPU:', ret['scaling_groups'][scaling_group]['remaining']['cpu']) + print(' Memory:', ret['scaling_groups'][scaling_group]['remaining']['mem']) + else: + for x in ret['scaling_groups'].keys(): + print(f' [{x}]') + print(' Using:') + print(' CPU:', ret['scaling_groups'][x]['using']['cpu']) + print(' Memory:', ret['scaling_groups'][x]['using']['mem']) + print(' Remaining:') + print(' CPU:', ret['scaling_groups'][x]['remaining']['cpu']) + print(' Memory:', ret['scaling_groups'][x]['remaining']['mem']) diff --git a/src/ai/backend/client/func/resource.py b/src/ai/backend/client/func/resource.py index 1b7c7a8f..c4d3cbf8 100644 --- a/src/ai/backend/client/func/resource.py +++ b/src/ai/backend/client/func/resource.py @@ -34,6 +34,17 @@ async def check_presets(cls): async with rqst.fetch() as resp: return await resp.json() + @api_function + @classmethod + async def get_available_resources(cls, scaling_group: str, group: str): + """ + Lists all resource presets in the current scaling group with additiona + information. + """ + rqst = Request('GET', '/resource/get-resources') + async with rqst.fetch() as resp: + return await resp.json() + @api_function @classmethod async def get_docker_registries(cls): From 10c8a2866dc8d1571f12c707594bbc8bfffc6b23 Mon Sep 17 00:00:00 2001 From: romanceformoon Date: Thu, 8 Jul 2021 12:10:01 +0900 Subject: [PATCH 2/9] docs: Add news fragment --- changes/165.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changes/165.feature diff --git a/changes/165.feature b/changes/165.feature new file mode 100644 index 00000000..97633e43 --- /dev/null +++ b/changes/165.feature @@ -0,0 +1 @@ +Add command to get available resources \ No newline at end of file From 502effe445699b40555733b6ee82d6e4bb2cccb0 Mon Sep 17 00:00:00 2001 From: romanceformoon Date: Thu, 8 Jul 2021 18:25:31 +0900 Subject: [PATCH 3/9] feat: Add group resource view --- src/ai/backend/client/cli/manager.py | 12 +++++++++++- src/ai/backend/client/func/resource.py | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/ai/backend/client/cli/manager.py b/src/ai/backend/client/cli/manager.py index 0914bfa3..3f41aeb6 100644 --- a/src/ai/backend/client/cli/manager.py +++ b/src/ai/backend/client/cli/manager.py @@ -223,7 +223,7 @@ def get_resources(scaling_group, group, all): with Session() as session: ret = session.Resource.get_available_resources(scaling_group, group) - print(f'Total remaining resources of group [{group}]:') + print(f'Total remaining resources of scaling group [{scaling_group}]:') print(' CPU:', ret['scaling_group_remaining']['cpu']) print(' Memory:', ret['scaling_group_remaining']['mem']) print('Each resources of scaling groups:') @@ -244,3 +244,13 @@ def get_resources(scaling_group, group, all): print(' Remaining:') print(' CPU:', ret['scaling_groups'][x]['remaining']['cpu']) print(' Memory:', ret['scaling_groups'][x]['remaining']['mem']) + print('Group limits:') + print(' CPU:', ret['group_limits']['cpu']) + print(' Memory:', ret['group_limits']['mem']) + print('Group using:') + print(' CPU:', ret['group_using']['cpu']) + print(' Memory:', ret['group_using']['mem']) + print('Group remaining:') + print(' CPU:', ret['group_remaining']['cpu']) + print(' Memory:', ret['group_remaining']['mem']) + diff --git a/src/ai/backend/client/func/resource.py b/src/ai/backend/client/func/resource.py index c4d3cbf8..fc97b94d 100644 --- a/src/ai/backend/client/func/resource.py +++ b/src/ai/backend/client/func/resource.py @@ -41,7 +41,7 @@ async def get_available_resources(cls, scaling_group: str, group: str): Lists all resource presets in the current scaling group with additiona information. """ - rqst = Request('GET', '/resource/get-resources') + rqst = Request('GET', '/resource/available-resources') async with rqst.fetch() as resp: return await resp.json() From 1a467ae98b2a22461365f2081ae17a828470be41 Mon Sep 17 00:00:00 2001 From: romanceformoon Date: Thu, 8 Jul 2021 18:34:15 +0900 Subject: [PATCH 4/9] fix: Change lint --- src/ai/backend/client/cli/manager.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ai/backend/client/cli/manager.py b/src/ai/backend/client/cli/manager.py index 3f41aeb6..c2474afc 100644 --- a/src/ai/backend/client/cli/manager.py +++ b/src/ai/backend/client/cli/manager.py @@ -253,4 +253,3 @@ def get_resources(scaling_group, group, all): print('Group remaining:') print(' CPU:', ret['group_remaining']['cpu']) print(' Memory:', ret['group_remaining']['mem']) - From 6620f047a1c9747a7d03a718d2c57a92e44b13bc Mon Sep 17 00:00:00 2001 From: romanceformoon Date: Fri, 9 Jul 2021 10:28:09 +0900 Subject: [PATCH 5/9] fix: Change comment on available-resources api --- src/ai/backend/client/func/resource.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ai/backend/client/func/resource.py b/src/ai/backend/client/func/resource.py index fc97b94d..cbd2b8c9 100644 --- a/src/ai/backend/client/func/resource.py +++ b/src/ai/backend/client/func/resource.py @@ -38,8 +38,7 @@ async def check_presets(cls): @classmethod async def get_available_resources(cls, scaling_group: str, group: str): """ - Lists all resource presets in the current scaling group with additiona - information. + Lists available resources from the scaling groups. """ rqst = Request('GET', '/resource/available-resources') async with rqst.fetch() as resp: From d346e2ee3f5f405e6f095b71fc87216b353129f2 Mon Sep 17 00:00:00 2001 From: romanceformoon Date: Mon, 12 Jul 2021 14:13:30 +0900 Subject: [PATCH 6/9] feat: Add getting resource slots to know and show exact list of resource slots. --- src/ai/backend/client/cli/manager.py | 33 ++++++++++++++-------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/ai/backend/client/cli/manager.py b/src/ai/backend/client/cli/manager.py index c2474afc..d8a8177d 100644 --- a/src/ai/backend/client/cli/manager.py +++ b/src/ai/backend/client/cli/manager.py @@ -223,33 +223,34 @@ def get_resources(scaling_group, group, all): with Session() as session: ret = session.Resource.get_available_resources(scaling_group, group) + slot_types = session.Resource.get_resource_slots() print(f'Total remaining resources of scaling group [{scaling_group}]:') - print(' CPU:', ret['scaling_group_remaining']['cpu']) - print(' Memory:', ret['scaling_group_remaining']['mem']) + for key in slot_types.keys(): + print(' ' + key, ret['scaling_group_remaining'][key]) print('Each resources of scaling groups:') if not all: print(f' [{scaling_group}]') print(' Using:') - print(' CPU:', ret['scaling_groups'][scaling_group]['using']['cpu']) - print(' Memory:', ret['scaling_groups'][scaling_group]['using']['mem']) + for key in slot_types.keys(): + print(' ' + key, ret['scaling_groups'][scaling_group]['using'][key]) print(' Remaining:') - print(' CPU:', ret['scaling_groups'][scaling_group]['remaining']['cpu']) - print(' Memory:', ret['scaling_groups'][scaling_group]['remaining']['mem']) + for key in slot_types.keys(): + print(' ' + key, ret['scaling_groups'][scaling_group]['remaining'][key]) else: for x in ret['scaling_groups'].keys(): print(f' [{x}]') print(' Using:') - print(' CPU:', ret['scaling_groups'][x]['using']['cpu']) - print(' Memory:', ret['scaling_groups'][x]['using']['mem']) + for key in slot_types.keys(): + print(' ' + key, ret['scaling_groups'][x]['using'][key]) print(' Remaining:') - print(' CPU:', ret['scaling_groups'][x]['remaining']['cpu']) - print(' Memory:', ret['scaling_groups'][x]['remaining']['mem']) + for key in slot_types.keys(): + print(' ' + key, ret['scaling_groups'][x]['remaining'][key]) print('Group limits:') - print(' CPU:', ret['group_limits']['cpu']) - print(' Memory:', ret['group_limits']['mem']) + for key in slot_types.keys(): + print(' ' + key, ret['group_limits'][key]) print('Group using:') - print(' CPU:', ret['group_using']['cpu']) - print(' Memory:', ret['group_using']['mem']) + for key in slot_types.keys(): + print(' ' + key, ret['group_using'][key]) print('Group remaining:') - print(' CPU:', ret['group_remaining']['cpu']) - print(' Memory:', ret['group_remaining']['mem']) + for key in slot_types.keys(): + print(' ' + key, ret['group_remaining'][key]) From d942a09f36c1ca1609a02c4c0e50340f71d6ce81 Mon Sep 17 00:00:00 2001 From: Joongi Kim Date: Sun, 18 Jul 2021 00:26:40 +0900 Subject: [PATCH 7/9] fix/refactor: Move command to cli.resource.check and refactor slot prints --- src/ai/backend/client/cli/__init__.py | 1 + src/ai/backend/client/cli/manager.py | 51 +--------------------- src/ai/backend/client/cli/pretty.py | 23 ++++++++++ src/ai/backend/client/cli/resource.py | 61 +++++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 50 deletions(-) create mode 100644 src/ai/backend/client/cli/resource.py diff --git a/src/ai/backend/client/cli/__init__.py b/src/ai/backend/client/cli/__init__.py index a0bdcee0..7a0d699c 100644 --- a/src/ai/backend/client/cli/__init__.py +++ b/src/ai/backend/client/cli/__init__.py @@ -39,6 +39,7 @@ def _attach_command(): from . import session_template # noqa from . import dotfile # noqa from . import server_log # noqa + from . import resource # noqa _attach_command() diff --git a/src/ai/backend/client/cli/manager.py b/src/ai/backend/client/cli/manager.py index d8a8177d..c1cf1228 100644 --- a/src/ai/backend/client/cli/manager.py +++ b/src/ai/backend/client/cli/manager.py @@ -204,53 +204,4 @@ def exclude_agents(agent_ids): print_done('The given agents will no longer start new sessions.') except Exception as e: print_error(e) - sys.exit(1) - - -@main.command() -@click.argument('scaling_group', metavar='SCALING_GROUP', default='default') -@click.argument('group', metavar='GROUP', default='default') -@click.option('-a', '--all', is_flag=True, - help='Get all resources of group.') -def get_resources(scaling_group, group, all): - ''' - Get available resources from the scaling groups. - ''' - config = get_config() - if config.endpoint_type != 'session': - print_warn('To use get-resources, your endpoint type must be "session".') - raise click.Abort() - - with Session() as session: - ret = session.Resource.get_available_resources(scaling_group, group) - slot_types = session.Resource.get_resource_slots() - print(f'Total remaining resources of scaling group [{scaling_group}]:') - for key in slot_types.keys(): - print(' ' + key, ret['scaling_group_remaining'][key]) - print('Each resources of scaling groups:') - if not all: - print(f' [{scaling_group}]') - print(' Using:') - for key in slot_types.keys(): - print(' ' + key, ret['scaling_groups'][scaling_group]['using'][key]) - print(' Remaining:') - for key in slot_types.keys(): - print(' ' + key, ret['scaling_groups'][scaling_group]['remaining'][key]) - else: - for x in ret['scaling_groups'].keys(): - print(f' [{x}]') - print(' Using:') - for key in slot_types.keys(): - print(' ' + key, ret['scaling_groups'][x]['using'][key]) - print(' Remaining:') - for key in slot_types.keys(): - print(' ' + key, ret['scaling_groups'][x]['remaining'][key]) - print('Group limits:') - for key in slot_types.keys(): - print(' ' + key, ret['group_limits'][key]) - print('Group using:') - for key in slot_types.keys(): - print(' ' + key, ret['group_using'][key]) - print('Group remaining:') - for key in slot_types.keys(): - print(' ' + key, ret['group_remaining'][key]) + sys.exit(1) \ No newline at end of file diff --git a/src/ai/backend/client/cli/pretty.py b/src/ai/backend/client/cli/pretty.py index 3074ceca..76a7bc08 100644 --- a/src/ai/backend/client/cli/pretty.py +++ b/src/ai/backend/client/cli/pretty.py @@ -1,8 +1,11 @@ +from decimal import Decimal import enum import functools +import math import sys import textwrap import traceback +from typing import TextIO from click import echo, style @@ -175,3 +178,23 @@ def show_warning(message, category, filename, lineno, file=None, line=None): style(str(category.__name__), fg='yellow', bold=True), style(str(message), fg='yellow'), ), file=file) + + +def print_resource( + slots: dict, + slot_types: dict, + *, + prefix: str = "", + file: TextIO = sys.stdout, + nan_as_infinite: bool = False +) -> None: + for key in slot_types.keys(): + value = Decimal(slots[key]) + value_expr: str + if math.isnan(value): + value_expr = "Unlimited" if nan_as_infinite else "Unavailable" + elif math.isinf(value): + value_expr = "Unlimited" + else: + value_expr = str(value) + print(f"{prefix}{key}: {value_expr}", file=file) \ No newline at end of file diff --git a/src/ai/backend/client/cli/resource.py b/src/ai/backend/client/cli/resource.py new file mode 100644 index 00000000..1502bce5 --- /dev/null +++ b/src/ai/backend/client/cli/resource.py @@ -0,0 +1,61 @@ +import sys + +import click + +from . import main +from .pretty import print_error, print_resource +from ..session import Session + + +@main.group() +def resource(): + """ + Provides resource check operations + """ + + +@resource.command() +@click.argument('scaling_group', metavar='SCALING_GROUP', default="default") +@click.argument('group', metavar='GROUP', default="default") +@click.option('-a', '--all', 'all_', is_flag=True, + help="Get all resources of group.") +def check(scaling_group: str, group: str, all_: bool) -> None: + """ + Get available resources from the scaling groups. + """ + try: + with Session() as session: + ret = session.Resource.get_available_resources(scaling_group, group) + slot_types = session.Resource.get_resource_slots() + + print(f"Total remaining resources of scaling group [{scaling_group}]:") + prefix = "- " + print_resource(ret["scaling_group_remaining"], slot_types, prefix=prefix, nan_as_infinite=True) + + print("Each resources of scaling groups:") + if not all_: + print(f" [{scaling_group}]") + print(" Using:") + prefix = " - " + print_resource(ret["scaling_groups"][scaling_group]["using"], slot_types, prefix=prefix, nan_as_infinite=True) + print(" Remaining:") + print_resource(ret["scaling_groups"][scaling_group]["remaining"], slot_types, prefix=prefix, nan_as_infinite=True) + else: + for x in ret["scaling_groups"].keys(): + print(f" [{x}]") + print(" Using:") + prefix = " - " + print_resource(ret["scaling_groups"][x]["using"], slot_types, prefix=prefix, nan_as_infinite=True) + print(" Remaining:") + print_resource(ret["scaling_groups"][x]["remaining"], slot_types, prefix=prefix, nan_as_infinite=True) + + print("Group limits:") + prefix = "- " + print_resource(ret["group_limits"], slot_types, prefix=prefix, nan_as_infinite=True) + print("Group using:") + print_resource(ret["group_using"], slot_types, prefix=prefix, nan_as_infinite=True) + print("Group remaining:") + print_resource(ret["group_remaining"], slot_types, prefix=prefix, nan_as_infinite=True) + except Exception as e: + print_error(e) + sys.exit(1) \ No newline at end of file From 65e8b4d27c02bbe2d957253747beaf4118bf8e3e Mon Sep 17 00:00:00 2001 From: Joongi Kim Date: Sun, 18 Jul 2021 00:27:40 +0900 Subject: [PATCH 8/9] docs: Update news fragment --- changes/165.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changes/165.feature b/changes/165.feature index 97633e43..4131b3f7 100644 --- a/changes/165.feature +++ b/changes/165.feature @@ -1 +1 @@ -Add command to get available resources \ No newline at end of file +Add `resource check` command to get available resources for given groups and scaling groups From fd5e6dd4c73beab224a7b5cade13fed80060968a Mon Sep 17 00:00:00 2001 From: Joongi Kim Date: Sun, 18 Jul 2021 01:35:39 +0900 Subject: [PATCH 9/9] refactor: Split check-group, check-scaling-group oommands and functions --- src/ai/backend/client/cli/resource.py | 65 +++++++++++++------------- src/ai/backend/client/func/resource.py | 14 +++++- 2 files changed, 45 insertions(+), 34 deletions(-) diff --git a/src/ai/backend/client/cli/resource.py b/src/ai/backend/client/cli/resource.py index 1502bce5..f59b7134 100644 --- a/src/ai/backend/client/cli/resource.py +++ b/src/ai/backend/client/cli/resource.py @@ -15,47 +15,48 @@ def resource(): @resource.command() -@click.argument('scaling_group', metavar='SCALING_GROUP', default="default") -@click.argument('group', metavar='GROUP', default="default") -@click.option('-a', '--all', 'all_', is_flag=True, - help="Get all resources of group.") -def check(scaling_group: str, group: str, all_: bool) -> None: +@click.argument('group', metavar='NAME', default="default") +def check_group(group: str) -> None: """ - Get available resources from the scaling groups. + Display the available resources from all allowed scaling groups of the given user group (project). """ try: with Session() as session: - ret = session.Resource.get_available_resources(scaling_group, group) + ret = session.Resource.check_group(group) slot_types = session.Resource.get_resource_slots() - - print(f"Total remaining resources of scaling group [{scaling_group}]:") + print("Limits:") prefix = "- " - print_resource(ret["scaling_group_remaining"], slot_types, prefix=prefix, nan_as_infinite=True) - - print("Each resources of scaling groups:") - if not all_: - print(f" [{scaling_group}]") - print(" Using:") + print_resource(ret["limits"], slot_types, prefix=prefix, nan_as_infinite=True) + print("Occupied:") + print_resource(ret["occupied"], slot_types, prefix=prefix) + print("Remaining:") + print_resource(ret["remaining"], slot_types, prefix=prefix) + print("Scaling groups allowed for this group:") + for sgroup_name in ret["scaling_groups"].keys(): + print(f" [{sgroup_name}]") + print(" Occupied:") prefix = " - " - print_resource(ret["scaling_groups"][scaling_group]["using"], slot_types, prefix=prefix, nan_as_infinite=True) + print_resource(ret["scaling_groups"][sgroup_name]["occupied"], slot_types, prefix=prefix) print(" Remaining:") - print_resource(ret["scaling_groups"][scaling_group]["remaining"], slot_types, prefix=prefix, nan_as_infinite=True) - else: - for x in ret["scaling_groups"].keys(): - print(f" [{x}]") - print(" Using:") - prefix = " - " - print_resource(ret["scaling_groups"][x]["using"], slot_types, prefix=prefix, nan_as_infinite=True) - print(" Remaining:") - print_resource(ret["scaling_groups"][x]["remaining"], slot_types, prefix=prefix, nan_as_infinite=True) - - print("Group limits:") + print_resource(ret["scaling_groups"][sgroup_name]["remaining"], slot_types, prefix=prefix) + except Exception as e: + print_error(e) + sys.exit(1) + + +@resource.command() +@click.argument('scaling_group', metavar='NAME', default="default") +def check_scaling_group(scaling_group: str) -> None: + """ + Display the available resource from the given scaling group. + """ + try: + with Session() as session: + ret = session.Resource.check_scaling_group(scaling_group) + slot_types = session.Resource.get_resource_slots() + print(f"Total remaining resources of scaling group [{scaling_group}]:") prefix = "- " - print_resource(ret["group_limits"], slot_types, prefix=prefix, nan_as_infinite=True) - print("Group using:") - print_resource(ret["group_using"], slot_types, prefix=prefix, nan_as_infinite=True) - print("Group remaining:") - print_resource(ret["group_remaining"], slot_types, prefix=prefix, nan_as_infinite=True) + print_resource(ret["remaining"], slot_types, prefix=prefix) except Exception as e: print_error(e) sys.exit(1) \ No newline at end of file diff --git a/src/ai/backend/client/func/resource.py b/src/ai/backend/client/func/resource.py index cbd2b8c9..e1c44408 100644 --- a/src/ai/backend/client/func/resource.py +++ b/src/ai/backend/client/func/resource.py @@ -36,11 +36,21 @@ async def check_presets(cls): @api_function @classmethod - async def get_available_resources(cls, scaling_group: str, group: str): + async def check_group(cls, group_name: str): """ Lists available resources from the scaling groups. """ - rqst = Request('GET', '/resource/available-resources') + rqst = Request('GET', '/resource/group', params={"name": group_name}) + async with rqst.fetch() as resp: + return await resp.json() + + @api_function + @classmethod + async def check_scaling_group(cls, sgroup_name: str): + """ + Lists available resources from the scaling groups. + """ + rqst = Request('GET', '/resource/scaling-group', params={"name": sgroup_name}) async with rqst.fetch() as resp: return await resp.json()