From 605d9daffd22823867b5ba4dd67a86e8eff22add Mon Sep 17 00:00:00 2001 From: Gabin L Date: Tue, 4 Feb 2025 21:16:40 +0100 Subject: [PATCH 1/2] feat: add admin user quota mngt Signed-off-by: Gabin L --- .gitignore | 2 + labctl/commands/admin/__init__.py | 4 +- labctl/commands/admin/quota.py | 67 +++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 labctl/commands/admin/quota.py diff --git a/.gitignore b/.gitignore index ba9db1a..f8f4793 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ *.pyc dist/ + +.DS_Store diff --git a/labctl/commands/admin/__init__.py b/labctl/commands/admin/__init__.py index 22e9932..f4b9bbf 100644 --- a/labctl/commands/admin/__init__.py +++ b/labctl/commands/admin/__init__.py @@ -1,8 +1,10 @@ import typer from .users import app as users_app from .vpn import app as vpn_app +from .quota import app as quota_app app = typer.Typer() app.add_typer(users_app, name="users") -app.add_typer(vpn_app, name="vpn") \ No newline at end of file +app.add_typer(vpn_app, name="vpn") +app.add_typer(quota_app, name="quota") diff --git a/labctl/commands/admin/quota.py b/labctl/commands/admin/quota.py new file mode 100644 index 0000000..92ec530 --- /dev/null +++ b/labctl/commands/admin/quota.py @@ -0,0 +1,67 @@ +from typing import Optional +import typer + +from rich.table import Table + +from labctl.core import Config, APIDriver, console + +app = typer.Typer() + +@app.command(name="show") +def show_user(username: str): + config = Config() + api_driver = APIDriver() + user_adjustements_list = api_driver.get(f"/quota/user/{username}/adjustements") + if user_adjustements_list.status_code == 404: + console.print(f"User {username} not found") + return + + data_total = {} + + table_adjustements = Table(title="User quota adjustements") + table_adjustements.add_column("ID", style="cyan") + table_adjustements.add_column("Type", style="magenta") + table_adjustements.add_column("Value", style="yellow") + table_adjustements.add_column("Comment", style="green") + + for quota_adjustement in user_adjustements_list.json(): + table_adjustements.add_row( + str(quota_adjustement["id"]), + quota_adjustement["type"], + str(quota_adjustement["quantity"]), + quota_adjustement["comment"], + ) + if quota_adjustement["type"] not in data_total: + data_total[quota_adjustement["type"]] = 0 + data_total[quota_adjustement["type"]] += quota_adjustement["quantity"] + + console.print(table_adjustements) + console.print("Total:") + for key, value in data_total.items(): + console.print(f" {key}: {value}") + +@app.command(name="add") +def add_quota_adjustement(username: str, quota_type: str, quantity: int, comment: Optional[str] = None): + payload = { + "username": username, + "type": quota_type, + "quantity": quantity, + "comment": comment, + } + rsp = APIDriver().post("/quota/adjust-user", json=payload) + if rsp.status_code >= 400: + console.print(f"[red]Error: {rsp.text}[/red]") + return + console.print(f"Adjustement added successfully") + +@app.command(name="del") +def delete_quota_adjustement(adjustement_id: int, confirm: bool = typer.Option(False, "--confirm", help="Confirm the deletion")): + # todo add confirmation with a get before to validate the information of this id + if not confirm: + console.print("Deletion not confirmed") + return + rsp = APIDriver().delete(f"/quota/adjust-user/{adjustement_id}") + if rsp.status_code >= 400: + console.print(f"[red]Error: {rsp.text}[/red]") + return + console.print(f"Adjustement deleted successfully") From 16483b8f962b47ffdd23d28659aac93153f2fcc6 Mon Sep 17 00:00:00 2001 From: Gabin L Date: Tue, 4 Feb 2025 21:48:10 +0100 Subject: [PATCH 2/2] feat: add quota mngt for project Signed-off-by: Gabin L --- labctl/commands/openstack.py | 72 +++++++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 9 deletions(-) diff --git a/labctl/commands/openstack.py b/labctl/commands/openstack.py index cde5dd7..758e793 100644 --- a/labctl/commands/openstack.py +++ b/labctl/commands/openstack.py @@ -1,19 +1,14 @@ +from typing import Optional from typer import Typer from labctl.core import cli_ready, Config, APIDriver, console -from rich.progress import Progress from rich.table import Table app = Typer() project = Typer() +quota = Typer() + app.add_typer(project, name="project") -## OpenStack commands -# reset-password -# project list -# project create -# project delete -# project add-user -# project del-user -# project list-users +app.add_typer(quota, name="quota") @cli_ready @app.command(name="reset-password") @@ -103,3 +98,62 @@ def del_user(project: str, user: str): console.print(f"[red]Error: {call.text}[/red]") return console.print(f"[green]User {user} deleted from project {project}[/green]") + +# quota +@cli_ready +@quota.command(name="show-project") +def show_project_quota(project: str): + """ + List OpenStack project quota + """ + call = APIDriver().get(f"/quota/project/{project}/adjustements") + if call.status_code >= 400: + console.print(f"[red]Error: {call.text}[/red]") + return + table = Table(title="Quotas for project " + project) + + table.add_column("Id") + table.add_column("Type") + table.add_column("Quantity") + table.add_column("User") + table.add_column("Comment") + + for quota in call.json(): + table.add_row(str(quota['id']), quota['type'], str(quota['quantity']), quota['username'], quota['comment']) + + console.print(table) + +# labctl openstack quota add PROJECT_NAME QUOTATYPE VALUE +@cli_ready +@quota.command(name="add") +def add_quota(project: str, quota_type: str, quantity: int, comment: Optional[str] = None): + """ + Add quota to OpenStack project + """ + config = Config() + console.print(f"[cyan]Adding {quota_type}={quantity} to OpenStack project {project}[/cyan]") + payload = { + "username": config.username, + "project_name": project, + "type": quota_type, + "quantity": quantity, + "comment": comment + } + call = APIDriver().post(f"/quota/adjust-project", json=payload) + if call.status_code >= 400: + console.print(f"[red]Error: {call.text}[/red]") + return + console.print(f"[green]Quota {quota_type}={quantity} added to project {project}[/green]") + +@cli_ready +@quota.command(name="del") +def del_quota(id: int): + """ + Delete quota from OpenStack project + """ + console.print(f"[cyan]Deleting quota {id} from OpenStack project[/cyan]") + call = APIDriver().delete(f"/quota/adjust-project/{id}/{Config().username}") + if call.status_code >= 400: + console.print(f"[red]Error: {call.text}[/red]") + return + console.print(f"[green]Quota {id} deleted from project[/green]")