From 87a3fb3ac34d2c0e85b9f376ed2e38388e0f8c99 Mon Sep 17 00:00:00 2001 From: Thobias Salazar Trevisan Date: Wed, 5 Feb 2025 15:20:49 -0300 Subject: [PATCH 1/2] Simplify filter criteria: exclude None values --- src/openstack_lb_info/main.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/openstack_lb_info/main.py b/src/openstack_lb_info/main.py index 24c2825..6fa3976 100644 --- a/src/openstack_lb_info/main.py +++ b/src/openstack_lb_info/main.py @@ -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) From 23baeb2d764a10ff70c7524d0043cace83ac2ed8 Mon Sep 17 00:00:00 2001 From: Thobias Salazar Trevisan Date: Wed, 5 Feb 2025 15:27:40 -0300 Subject: [PATCH 2/2] Refactor lb resources retrieval and tree population logic --- src/openstack_lb_info/loadbalancer_info.py | 258 ++++++++++++--------- 1 file changed, 148 insertions(+), 110 deletions(-) diff --git a/src/openstack_lb_info/loadbalancer_info.py b/src/openstack_lb_info/loadbalancer_info.py index b5c152c..807e7fb 100644 --- a/src/openstack_lb_info/loadbalancer_info.py +++ b/src/openstack_lb_info/loadbalancer_info.py @@ -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. @@ -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): """ @@ -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): """ @@ -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): """