Skip to content

Commit

Permalink
fix(client): Fix group command group
Browse files Browse the repository at this point in the history
Commands per the list in #201.

Re-engineered running the client in the test-suite.

Also, fixed common.logger.echo
  • Loading branch information
ketiltrout committed Nov 4, 2024
1 parent 0ea7a9a commit c3c993b
Show file tree
Hide file tree
Showing 19 changed files with 1,006 additions and 225 deletions.
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

0 comments on commit c3c993b

Please sign in to comment.