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

Dev #3

Merged
merged 6 commits into from
Sep 19, 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
3 changes: 2 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ repos:
rev: 5.13.2
hooks:
- id: isort
args: ["--profile", "black"]

- repo: https://github.com/PyCQA/pylint
rev: v3.2.6
rev: v3.2.7
hooks:
- id: pylint
additional_dependencies:
Expand Down
38 changes: 27 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
openstack-lb-info - A script to display OpenStack Load Balancer resource details.
openstack-lb-info - A command-line tool for displaying OpenStack Load Balancer resource details.


[![Build and Test](https://github.com/thobiast/openstack-loadbalancer-info/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/thobiast/openstack-loadbalancer-info/actions/workflows/build.yml)
Expand All @@ -21,35 +21,38 @@ Below are the key features and components:
- Display detailed attributes and information about listeners, pools, health monitors, members, and amphorae.
- Filter results based on various criteria, such as load balancer name, ID, tags, availability zone, VIP network, and VIP subnet.
- Present information in a structured and colorful format using the Rich library.
- Support output in different formats, including Rich text, plain text, and JSON.

## Information Display

The program provides two main modes for displaying information:

1. **Load Balancer Information:** When the resource type is specified as "lb," it retrieves and displays
1. **Load Balancer Information:** When the resource type is specified as "*lb*", it retrieves and displays
details about OpenStack load balancers. The displayed information includes load balancer IDs, VIP addresses, provisioning status,
operating status, and other optional details. If no load balancers match the filter criteria, it will indicate that
no load balancers were found.

2. **Amphora Information:** When the resource type is specified as "amphora," it retrieves and displays information
2. **Amphora Information:** When the resource type is specified as "*amphora*", it retrieves and displays information
about amphoras associated with load balancers. Amphoras are responsible for handling load balancing operations. The displayed
information includes amphora IDs, roles, status, load balancer network IP addresses, associated images, server information,
and optional details. If no amphoras match the filter criteria, it will indicate that no amphoras were found.

## Example

```bash
$ usage: openstack-lb-info [-h] --type {lb,amphora} [--name NAME] [--id ID] [--tags TAGS]
[--flavor-id FLAVOR_ID] [--vip-address VIP_ADDRESS]
[--availability-zone AVAILABILITY_ZONE]
[--vip-network-id VIP_NETWORK_ID]
$ usage: openstack-lb-info [-h] [-o {plain,rich,json}] -t {lb,amphora} [--name NAME] [--id ID]
[--tags TAGS] [--flavor-id FLAVOR_ID] [--vip-address VIP_ADDRESS]
[--availability-zone AVAILABILITY_ZONE] [--vip-network-id VIP_NETWORK_ID]
[--vip-subnet-id VIP_SUBNET_ID] [--details]

A script to show OpenStack load balancers information.

options:
-h, --help show this help message and exit
--type {lb,amphora} Show information about load balancers or amphoras
-o {plain,rich,json}, --output-format {plain,rich,json}
Output format: 'plain', 'rich' or 'json'
-t {lb,amphora}, --type {lb,amphora}
Show information about load balancers or amphoras
--name NAME Filter load balancers name
--id ID Filter load balancers id
--tags TAGS Filter load balancers tags
Expand All @@ -71,20 +74,33 @@ options:
openstack-lb-info --type lb --id load_balancer_id
openstack-lb-info --type amphora --id load_balancer_id
openstack-lb-info --type amphora --id load_balancer_id --details

```
![example](img/example.png)

## Authentication Methods

##### Environment Variables
You can manually set the required environment variables or use an OpenStack RC file to simplify the process.

##### clouds.yaml Configuration
Alternatively, you can use a *clouds.yaml* and export "*OS_CLOUD*" variable to pass the cloud name.

## Usage
For more information: https://docs.openstack.org/python-openstackclient/latest/cli/man/openstack.html

## Installation

Clone or download the repository to your local machine.

### Install in development mode using pip
#### Development mode using pip
```bash
$ pip install -e .
```

### Install in development mode using pipx
#### Development mode using pipx
```bash
$ pipx install -e .
```
## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.
8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ description = "A script to display OpenStack Load Balancer resource details."
readme = "README.md"
license = {file = "LICENSE"}
dynamic = ["version", "dependencies"]
requires-python = ">=3.6"
requires-python = ">=3.7"
authors = [
{name = "Thobias Salazar Trevisan"},
]

[project.scripts]
openstack-lb-info = "openstack_lb_info.openstack_lb_info:main"
openstack-lb-info = "openstack_lb_info.main:main"

[project.urls]
"Homepage" = "https://github.com/thobiast/openstack-loadbalancer-info"
Expand All @@ -25,10 +25,10 @@ version = {attr = "openstack_lb_info.__version__"}
dependencies = {file = ["requirements.txt"]}

[tool.black]
line-length = 88
line-length = 100

[tool.pylint]
max-line-length = 88
max-line-length = 100
disable = [
"C0103", # (invalid-name)
]
2 changes: 1 addition & 1 deletion src/openstack_lb_info/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-
"""openstack-lb-info module."""

__version__ = "0.0.2"
__version__ = "0.1.0"
212 changes: 212 additions & 0 deletions src/openstack_lb_info/formatters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
# -*- coding: utf-8 -*-
"""
Output Formatters Module
------------------------

This module provides classes for formatting and displaying output in different formats,
including Rich text, plain text, and JSON. It defines an abstract base class `OutputFormatter`
and concrete implementations for each output format. These formatters are used to present
information about OpenStack resources in a user-friendly and structured manner.
"""

import contextlib
import json
import re
from abc import ABC, abstractmethod

try:
from rich.console import Console
from rich.highlighter import ReprHighlighter
from rich.tree import Tree

RICH_AVAILABLE = True
except ImportError:
RICH_AVAILABLE = False


class OutputFormatter(ABC):
"""Abstract base class for output formatters."""

@abstractmethod
def create_tree(self, name):
"""Create a tree structure for the output."""

@abstractmethod
def add_to_tree(self, tree, content):
"""Add content to the tree structure."""

@abstractmethod
def print_tree(self, tree):
"""Print the tree structure."""

@abstractmethod
def print(self, message):
"""Print a message."""

@abstractmethod
def status(self, message):
"""Display a status message."""

@abstractmethod
def line(self):
"""Print a line separator."""

@abstractmethod
def rule(self, title, align="center"):
"""Print a rule with a title."""

@abstractmethod
def format_status(self, status):
"""Format status text."""


class RichOutputFormatter(OutputFormatter):
"""Formatter using the Rich library."""

def __init__(self):
self.console = Console()
self.highlighter = ReprHighlighter()

def create_tree(self, name):
return Tree(name)

def add_to_tree(self, tree, content, highlight=False):
if highlight:
content = self.highlighter(content)
return tree.add(content)

def print_tree(self, tree):
self.console.print(tree)

def print(self, message):
self.console.print(message)

def status(self, message):
return self.console.status(message)

def line(self):
self.console.line()

def rule(self, title, align="center"):
self.console.rule(title, align=align)

def format_message(self, message):
"""Return the message as-is, preserving Rich formatting."""
return message

def format_status(self, status):
status_colors = {
"ACTIVE": "green",
"ONLINE": "green",
"PENDING": "yellow",
}
color = status_colors.get(status, "red")
return f"[{color}]{status}[/{color}]"


class PlainOutputFormatter(OutputFormatter):
"""Formatter for plain text output."""

def create_tree(self, name):
return {"name": name, "children": []}

def add_to_tree(self, tree, content, highlight=False):
_ = highlight
child_tree = {"name": self.format_message(content), "children": []}
tree["children"].append(child_tree)
return child_tree

def print_tree(self, tree, level=0):
indent = " " * level
print(f"{indent}{self.format_message(tree['name'])}")
for child in tree.get("children", []):
self.print_tree(child, level + 1)

def print(self, message):
print(self.format_message(message))

def status(self, message):
@contextlib.contextmanager
def plain_status():
# Remove Rich formatting codes from the message
clean_message = self.format_message(message)
print(f"[STATUS] {clean_message}")
try:
yield
finally:
print(f"[STATUS] Completed: {clean_message}")

return plain_status()

def line(self):
print()

def rule(self, title, align="center"):
title = self.format_message(title)
print(f"{title}")
print("-" * len(title))

def format_message(self, message):
"""Remove Rich text formatting tags from a message."""
clean_message = re.sub(r"\[\/?[^\]]+\]", "", message)
return clean_message

def format_status(self, status):
return status


class JSONOutputFormatter(OutputFormatter):
"""Formatter for JSON output."""

def __init__(self):
self.data = None

def create_tree(self, name):
# Remove Rich codes
clean_name = self.format_message(name)
self.data = {"name": clean_name, "children": []}
return self.data

def add_to_tree(self, tree, content, highlight=False):
_ = highlight
# Remove Rich codes
clean_content = self.format_message(content)
# Create a new node and add it to the tree's children
child = {"name": clean_content, "children": []}
tree["children"].append(child)
return child

def print_tree(self, tree):
print(json.dumps(tree, indent=4))

def print(self, message):
# Remove Rich codes
clean_message = self.format_message(message)
# Not show empty prints
if not clean_message:
return
# For consistency, wrap messages in a dict
output = {"message": clean_message}
print(json.dumps(output, indent=4))

def status(self, message):
return contextlib.nullcontext()

def line(self):
pass

def rule(self, title, align="center"):
pass

def format_status(self, status):
return status

def format_message(self, message):
"""Remove Rich text formatting tags from a message."""
if isinstance(message, str):
clean_message = re.sub(r"\[\/?[^\]]+\]", "", message)
return clean_message
return message


# vim: ts=4
Loading
Loading