Skip to content
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
258 changes: 148 additions & 110 deletions src/openstack_lb_info/loadbalancer_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,56 @@ def __init__(self, openstack_api, lb, details, formatter):
self.lb_tree = None
self.openstack_api = openstack_api

def _add_all_attr_to_tree(self, obj, tree):
"""
Add all attributes of an object to a tree.

This function iterates through all the attributes of a given Python object and
adds them to a Rich tree. Each attribute is displayed in the
format "attribute_name: value".

Args:
obj (object): The object whose attributes are to be added.
tree: The tree to which the attributes will be added.
"""
obj_dict = obj.to_dict()
for attr in sorted(obj_dict):
value = obj_dict[attr]
content = f"{attr}: {value}"
self.formatter.add_to_tree(tree, content, highlight=True)

# pylint: disable=too-many-arguments
def _retrieve_and_add_to_tree(self, label, resource_id, retrieve_method, tree, format_fn):
"""
Generic helper to retrieve a resource, add its formatted information to a tree.

This method displays a status message while retrieving a resource via the provided API call.
If the resource is found, its formatted information is added to the specified tree node. In
detailed mode, all resource attributes are appended to the tree node as well.

Args:
label (str): The resource label (e.g., "Listener, "Health Monitor", ...).
resource_id (str): The ID of the resource to retrieve.
retrieve_method (Callable): The API method used to retrieve the resource.
tree: The tree node to which the resource's info will be added.
format_fn (Callable): A function that takes the resource and returns a formatted string.

Returns:
The retrieved resource object if found; otherwise, returns None.
"""
with self.formatter.status(f"Getting {label} details id [b]{resource_id}[/b]"):
resource = retrieve_method(resource_id)

if resource:
resource_tree = self.formatter.add_to_tree(tree, format_fn(resource))
if self.details:
self._add_all_attr_to_tree(resource, resource_tree)
return resource

self.formatter.add_to_tree(tree, f"[b green]{label}:[/] None")

return None

def create_lb_tree(self):
"""
Create a tree representing Load Balancer information.
Expand All @@ -55,71 +105,43 @@ def create_lb_tree(self):
f"tags:[magenta]{self.lb.tags}[/]"
)
if self.details:
self.add_all_attr_to_tree(self.lb, self.lb_tree)
self._add_all_attr_to_tree(self.lb, self.lb_tree)

return self.lb_tree

def add_health_monitor_info(self, pool_tree, health_monitor_id):
"""
Add information about a Health Monitor to a Pool tree.

Args:
pool_tree: The tree representing the Pool.
health_monitor_id (str): The ID of the Health Monitor.

Returns:
None
"""
with self.formatter.status(f"Getting health monitor details id [b]{health_monitor_id}[/b]"):
health_monitor = self.openstack_api.retrieve_health_monitor(health_monitor_id)

if health_monitor:
health_monitor_tree = self.formatter.add_to_tree(
pool_tree,
f"[b green]Health Monitor:[/] [b white]{health_monitor.id}[/] "
f"type:[magenta]{health_monitor.type}[/magenta] "
f"http_method:[magenta]{health_monitor.http_method}[/magenta] "
f"http_codes:[magenta]{health_monitor.expected_codes}[/magenta] "
f"url_path:[magenta]{health_monitor.url_path}[/magenta] "
f"prov_status:{self.formatter.format_status(health_monitor.provisioning_status)} "
f"oper_status:{self.formatter.format_status(health_monitor.operating_status)}",
)

if self.details:
self.add_all_attr_to_tree(health_monitor, health_monitor_tree)
else:
self.formatter.add_to_tree(pool_tree, "[b green]Health Monitor:[/] None")

def add_pool_members(self, pool_tree, pool_id, pool_members):
def add_listener_info(self, listener_id):
"""
Add information about Members of a Pool to the Pool tree.
Add information about a Listener to the Load Balancer tree.

Args:
pool_tree: The tree representing the Pool.
pool_id (str): The ID of the Pool for which to retrieve Member information.
pool_members (list): A list of dictionaries containing Member information,
where each dictionary includes the Member's ID and additional details.
listener_id (str): The ID of the Listener for which to retrieve and display
information.

Returns:
None
"""
for member in pool_members:
with self.formatter.status(f"Getting member details id [b]{member['id']}[/b]"):
os_m = self.openstack_api.retrieve_member(member["id"], pool_id)

member_tree = self.formatter.add_to_tree(
pool_tree,
f"[b green]Member:[/] [b white]{os_m.id}[/] "
f"IP:[magenta]{os_m.address}[/magenta] "
f"port:[magenta]{os_m.protocol_port}[/magenta] "
f"weight:[magenta]{os_m.weight}[/magenta] "
f"backup:[magenta]{os_m.backup}[/magenta] "
f"prov_status:{self.formatter.format_status(os_m.provisioning_status)} "
f"oper_status:{self.formatter.format_status(os_m.operating_status)}",
def format_listener(listener):
return (
f"[b green]Listener:[/] [b white]{listener.id}[/] "
f"([blue b]{listener.name}[/]) "
f"port:[cyan]{listener.protocol}/{listener.protocol_port}[/] "
f"prov_status:{self.formatter.format_status(listener.provisioning_status)} "
f"oper_status:{self.formatter.format_status(listener.operating_status)}"
)

if self.details:
self.add_all_attr_to_tree(os_m, member_tree)
listener = self._retrieve_and_add_to_tree(
"Listener",
listener_id,
self.openstack_api.retrieve_listener,
self.lb_tree,
format_listener,
)
if listener:
if listener.default_pool_id:
self.add_pool_info(self.lb_tree, listener.default_pool_id)
else:
self.formatter.add_to_tree(self.lb_tree, "[b green]Pool:[/] None")

def add_pool_info(self, tree, pool_id):
"""
Expand All @@ -132,80 +154,96 @@ def add_pool_info(self, tree, pool_id):
Returns:
None
"""
with self.formatter.status(f"Getting pool details id [b]{pool_id}[/b]"):
pool = self.openstack_api.retrieve_pool(pool_id)

pool_tree = self.formatter.add_to_tree(
tree,
f"[b green]Pool:[/] [b white]{pool.id}[/] "
f"protocol:[magenta]{pool.protocol}[/magenta] "
f"algorithm:[magenta]{pool.lb_algorithm}[/magenta] "
f"prov_status:{self.formatter.format_status(pool.provisioning_status)} "
f"oper_status:{self.formatter.format_status(pool.operating_status)} ",
)
if self.details:
self.add_all_attr_to_tree(pool, pool_tree)

if pool.health_monitor_id:
self.add_health_monitor_info(pool_tree, pool.health_monitor_id)
else:
self.formatter.add_to_tree(pool_tree, "[b green]Health Monitor:[/] None")
def format_pool(pool):
return (
f"[b green]Pool:[/] [b white]{pool.id}[/] "
f"protocol:[magenta]{pool.protocol}[/magenta] "
f"algorithm:[magenta]{pool.lb_algorithm}[/magenta] "
f"prov_status:{self.formatter.format_status(pool.provisioning_status)} "
f"oper_status:{self.formatter.format_status(pool.operating_status)}"
)

if pool.members:
self.add_pool_members(pool_tree, pool.id, pool.members)
else:
self.formatter.add_to_tree(pool_tree, "[b green]Member:[/] None")
pool = self._retrieve_and_add_to_tree(
"Pool", pool_id, self.openstack_api.retrieve_pool, tree, format_pool
)
if pool:
if pool.health_monitor_id:
self.add_health_monitor_info(tree, pool.health_monitor_id)
else:
self.formatter.add_to_tree(tree, "[b green]Health Monitor:[/] None")

def add_listener_info(self, listener_id):
if pool.members:
self.add_pool_members(tree, pool.id, pool.members)
else:
self.formatter.add_to_tree(tree, "[b green]Member:[/] None")

def add_health_monitor_info(self, pool_tree, health_monitor_id):
"""
Add information about a Listener to the Load Balancer tree.
Add information about a Health Monitor to a Pool tree.

Args:
listener_id (str): The ID of the Listener for which to retrieve and display
information.
pool_tree: The tree representing the Pool.
health_monitor_id (str): The ID of the Health Monitor.

Returns:
None
"""
with self.formatter.status(
f"Getting listener details for loadbalancers "
f"[b]{self.lb.id}[/b] listener: [b]{listener_id}[/b]"
):
listener = self.openstack_api.retrieve_listener(listener_id)

listener_tree = self.formatter.add_to_tree(
self.lb_tree,
f"[b green]Listener:[/] [b white]{listener.id}[/] "
f"([blue b]{listener.name}[/]) "
f"port:[cyan]{listener.protocol}/{listener.protocol_port}[/] "
f"prov_status:{self.formatter.format_status(listener.provisioning_status)} "
f"oper_status:{self.formatter.format_status(listener.operating_status)} ",
)
if self.details:
self.add_all_attr_to_tree(listener, listener_tree)
def format_health_monitor(hm):
return (
f"[b green]Health Monitor:[/] [b white]{hm.id}[/] "
f"type:[magenta]{hm.type}[/magenta] "
f"http_method:[magenta]{hm.http_method}[/magenta] "
f"http_codes:[magenta]{hm.expected_codes}[/magenta] "
f"url_path:[magenta]{hm.url_path}[/magenta] "
f"prov_status:{self.formatter.format_status(hm.provisioning_status)} "
f"oper_status:{self.formatter.format_status(hm.operating_status)}"
)

if listener.default_pool_id:
self.add_pool_info(listener_tree, listener.default_pool_id)
else:
self.formatter.add_to_tree(listener_tree, "[b green]Pool:[/] None")
self._retrieve_and_add_to_tree(
"Health Monitor",
health_monitor_id,
self.openstack_api.retrieve_health_monitor,
pool_tree,
format_health_monitor,
)

def add_all_attr_to_tree(self, obj, tree):
def add_pool_members(self, pool_tree, pool_id, pool_members):
"""
Add all attributes of an object to a tree.

This function iterates through all the attributes of a given Python object and
adds them to a Rich tree. Each attribute is displayed in the
format "attribute_name: value".
Add information about Members of a Pool to the Pool tree.

Args:
obj (object): The object whose attributes are to be added.
tree: The tree to which the attributes will be added.
pool_tree: The tree representing the Pool.
pool_id (str): The ID of the Pool for which to retrieve Member information.
pool_members (list): A list of dictionaries containing Member information,
where each dictionary includes the Member's ID and additional details.

Returns:
None
"""
obj_dict = obj.to_dict()
for attr in sorted(obj_dict):
value = obj_dict[attr]
content = f"{attr}: {value}"
self.formatter.add_to_tree(tree, content, highlight=True)
for member in pool_members:
with self.formatter.status(f"Getting member details id [b]{member['id']}[/b]"):
os_m = self.openstack_api.retrieve_member(member["id"], pool_id)

def format_member(m):
return (
f"[b green]Member:[/] [b white]{m.id}[/] "
f"IP:[magenta]{m.address}[/magenta] "
f"port:[magenta]{m.protocol_port}[/magenta] "
f"weight:[magenta]{m.weight}[/magenta] "
f"backup:[magenta]{m.backup}[/magenta] "
f"prov_status:{self.formatter.format_status(m.provisioning_status)} "
f"oper_status:{self.formatter.format_status(m.operating_status)}"
)

def return_member(_, os_m=os_m):
# Simply return the already retrieved member.
return os_m

self._retrieve_and_add_to_tree(
"Member", member["id"], return_member, pool_tree, format_member
)

def display_lb_info(self):
"""
Expand Down Expand Up @@ -320,7 +358,7 @@ def add_amphora_to_tree(self, amphora):
f"compute host:([magenta]{server_compute_host}[/])",
)
if self.details:
self.add_all_attr_to_tree(amphora, amphora_tree)
self._add_all_attr_to_tree(amphora, amphora_tree)

def display_amp_info(self):
"""
Expand Down
22 changes: 12 additions & 10 deletions src/openstack_lb_info/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,18 +198,20 @@ def query_openstack_lbs(openstackapi, args, formatter):
list: A list of OpenStack Load Balancer objects that match the specified
filters, or an empty list if no load balancers match the criteria.
"""
# Define filter criteria
# Define filter criteria. It includes only keys with non-None values.
filter_criteria = {
"tags": args.tags,
"availability_zone": args.availability_zone,
"vip_network_id": args.vip_network_id,
"vip_subnet_id": args.vip_subnet_id,
"flavor_id": args.flavor_id,
"vip_address": args.vip_address,
k: v
for k, v in {
"tags": args.tags,
"availability_zone": args.availability_zone,
"vip_network_id": args.vip_network_id,
"vip_subnet_id": args.vip_subnet_id,
"flavor_id": args.flavor_id,
"vip_address": args.vip_address,
"id": args.id if args.id else None,
}.items()
if v is not None
}
if args.id:
# Add the "id" key to the filter criteria only if specified
filter_criteria["id"] = args.id

with formatter.status("Quering load balancers..."):
filtered_lbs_tmp = openstackapi.retrieve_load_balancers(filter_criteria)
Expand Down