Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(client): Fix "group" command group and client test-suite #204

Merged
merged 1 commit into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions alpenhorn/client/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import peewee as pw

from .. import db
from ..common.logger import echo as echo
from ..common.util import start_alpenhorn, version_option
from ..db import (
ArchiveAcq,
Expand Down
83 changes: 0 additions & 83 deletions alpenhorn/client/group.py

This file was deleted.

24 changes: 24 additions & 0 deletions alpenhorn/client/group/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""Alpenhorn client interface for operations on `StorageGroup`s."""

import click
import peewee as pw

from ...db import StorageGroup, StorageNode

from .create import create
from .list import list_
from .modify import modify
from .rename import rename
from .show import show


@click.group(context_settings={"help_option_names": ["-h", "--help"]})
def cli():
"""Manage Storage Groups."""


cli.add_command(create, "create")
cli.add_command(list_, "list")
cli.add_command(modify, "modify")
cli.add_command(rename, "rename")
cli.add_command(show, "show")
39 changes: 39 additions & 0 deletions alpenhorn/client/group/create.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"""alpenhorn group create command"""

import click
import json
import peewee as pw

from ...db import database_proxy, StorageGroup
from ..options import client_option, set_io_config
from ..cli import echo


@click.command()
@click.argument("group_name", metavar="NAME")
@client_option("io_class", default="Default", show_default=True)
@client_option("io_config")
@client_option("io_var")
@client_option("notes")
def create(group_name, io_class, io_config, io_var, notes):
"""Create a new storage group.

The group will be called NAME, which must not already exist.
"""

io_config = set_io_config(io_config, io_var, dict())

with database_proxy.atomic():
try:
StorageGroup.get(name=group_name)
raise click.ClickException(f'Group "{group_name}" already exists.')
except pw.DoesNotExist:
pass

StorageGroup.create(
name=group_name,
notes=notes,
io_class=io_class,
io_config=json.dumps(io_config) if io_config else None,
)
echo(f'Created storage group "{group_name}".')
24 changes: 24 additions & 0 deletions alpenhorn/client/group/list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""alpenhorn group list command"""

import click
from tabulate import tabulate
from ...db import StorageGroup
from ..cli import echo


@click.command()
def list_():
"""List all storage groups."""

data = StorageGroup.select(
StorageGroup.name, StorageGroup.io_class, StorageGroup.notes
).tuples()
if data:
# Add Default I/O class where needed
data = list(data)
for index, row in enumerate(data):
if row[1] is None:
data[index] = (row[0], "Default", row[2])
echo(tabulate(data, headers=["Name", "I/O Class", "Notes"]))
else:
echo("No storage groups found.")
56 changes: 56 additions & 0 deletions alpenhorn/client/group/modify.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"""alpenhorn group modify command"""

import json
import click
import peewee as pw

from ...db import StorageGroup, database_proxy
from ..cli import echo
from ..options import client_option, set_io_config


@click.command()
@click.argument("group_name", metavar="GROUP")
@client_option("io_class")
@client_option("io_config")
@client_option("io_var")
@client_option("notes")
def modify(group_name, io_class, io_config, io_var, notes):
"""Modify storage group metadata.

Modifies the metadata of the storage group named GROUP, which must already exist.

NOTE: to change the name of a storage group, use:

alpenhorn group rename
"""

if notes == "":
notes = None
if io_class == "":
io_class = None

with database_proxy.atomic():
try:
group = StorageGroup.get(name=group_name)
except pw.DoesNotExist:
raise click.ClickException(f'Storage group "{group_name}" does not exist.')

io_config = set_io_config(io_config, io_var, group.io_config)

# collect the updated parameters
updates = dict()
if notes != group.notes:
updates["notes"] = notes
if io_class != group.io_class:
updates["io_class"] = io_class
if io_config != group.io_config:
updates["io_config"] = json.dumps(io_config)

# Update if necessary.
if updates:
update = StorageGroup.update(**updates).where(StorageGroup.id == group.id)
update.execute()
echo("Storage group updated.")
else:
echo("Nothing to do.")
39 changes: 39 additions & 0 deletions alpenhorn/client/group/rename.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"""alpenhorn group rename command"""

import click
import peewee as pw

from ...db import StorageGroup, database_proxy
from ..cli import echo


@click.command()
@click.argument("group_name", metavar="GROUP")
@click.argument("new_name", metavar="NEW_NAME")
def rename(group_name, new_name):
"""Rename a storage group.

The existing storage group named GROUP will be renamed to NEW_NAME.
NEW_NAME must not already be the name of another group.
"""

if group_name == new_name:
# The easy case
echo("No change.")
else:
with database_proxy.atomic():
try:
StorageGroup.get(name=new_name)
raise click.ClickException(
f'Storage group "{group_name}" already exists.'
)
except pw.DoesNotExist:
pass

try:
group = StorageGroup.get(name=group_name)
group.name = new_name
group.save()
echo(f'Storage group "{group_name}" renamed to "new_name"')
except pw.DoesNotExist:
raise click.ClickException(f"No such storage group: {group_name}.")
73 changes: 73 additions & 0 deletions alpenhorn/client/group/show.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"""alpenhorn group show command"""

import json
import click
import peewee as pw
from tabulate import tabulate

from ...db import StorageGroup, StorageNode
from ..cli import echo


@click.command()
@click.argument("group_name", metavar="GROUP")
@click.option("--node-details", is_flag=True, help="Show details of listed nodes.")
@click.option("--node-stats", is_flag=True, help="Show usage stats of listed nodes.")
def show(group_name, node_details, node_stats):
"""Show details of a storage group.

Shows details of the storage group named GROUP.
"""

try:
group = StorageGroup.get(name=group_name)
except pw.DoesNotExist:
raise click.ClickException(f"no such group: {group_name}")

# Print a report
echo("Storage Group: " + group.name)
echo(" Notes: " + (group.notes if group.notes else ""))
echo(" I/O Class: " + (group.io_class if group.io_class else "Default"))

echo("\nI/O Config:\n")
if group.io_config:
try:
io_config = json.loads(group.io_config)
if io_config:
# Find length of longest key (but not too long)
keylen = min(max([len(key) for key in io_config]), 30)
for key, value in io_config.items():
echo(" " + key.rjust(keylen) + ": " + str(value))
else:
echo(" empty")
except json.JSONDecodeError:
echo("INVALID (JSON decode error)")
else:
echo(" none")

# List nodes, if any
echo("\nNodes:\n")
nodes = list(StorageNode.select().where(StorageNode.group == group))
if nodes:
if node_details or node_stats:
if node_details:
data = [
(
node.name,
node.host,
"Yes" if node.active else "No",
node.io_class if node.io_class else "Default",
)
for node in nodes
]
headers = ["Name", "Host", "Active", "I/O Class"]
if node_stats:
# TODO: add --node-stats support when "alpenhorn node stats" is implemented
raise NotImplementedError()
echo(tabulate(data, headers=headers))
else:
# simple list
for node in nodes:
echo(" " + node.name)
else:
echo(" none")
Loading