Skip to content

Commit 90a7c69

Browse files
authored
Merge pull request #6 from thobiast/dev
Dev
2 parents 2cf4ac2 + eac2731 commit 90a7c69

6 files changed

Lines changed: 134 additions & 30 deletions

File tree

.pre-commit-config.yaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
repos:
22
- repo: https://github.com/pre-commit/pre-commit-hooks
3-
rev: v4.6.0
3+
rev: v5.0.0
44
hooks:
55
- id: end-of-file-fixer
66
- id: check-yaml
77
- id: trailing-whitespace
88
- id: debug-statements
99

1010
- repo: https://github.com/psf/black
11-
rev: 24.8.0
11+
rev: 25.1.0
1212
hooks:
1313
- id: black
1414

1515
- repo: https://github.com/PyCQA/bandit
16-
rev: 1.7.9
16+
rev: 1.8.6
1717
hooks:
1818
- id: bandit
1919
exclude: ^tests/
2020

2121
- repo: https://github.com/PyCQA/flake8
22-
rev: 7.1.1
22+
rev: 7.3.0
2323
hooks:
2424
- id: flake8
2525
additional_dependencies: [flake8-bugbear, pep8-naming]
@@ -32,7 +32,7 @@ repos:
3232
args: ["--profile", "black"]
3333

3434
- repo: https://github.com/PyCQA/pylint
35-
rev: v3.2.7
35+
rev: v3.3.8
3636
hooks:
3737
- id: pylint
3838
additional_dependencies:

README.md

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,19 +40,25 @@ and optional details. If no amphoras match the filter criteria, it will indicate
4040
## Example
4141

4242
```bash
43-
$ usage: openstack-lb-info [-h] [-o {plain,rich,json}] -t {lb,amphora} [--name NAME] [--id ID]
44-
[--tags TAGS] [--flavor-id FLAVOR_ID] [--vip-address VIP_ADDRESS]
45-
[--availability-zone AVAILABILITY_ZONE] [--vip-network-id VIP_NETWORK_ID]
46-
[--vip-subnet-id VIP_SUBNET_ID] [--details] [--max-workers MAX_WORKERS]
43+
$ usage: openstack-lb-info [-h] [-d] [--os-cloud OS_CLOUD] -t {lb,amphora}
44+
[-o {plain,rich,json}] [--name NAME] [--id ID]
45+
[--tags TAGS] [--flavor-id FLAVOR_ID]
46+
[--vip-address VIP_ADDRESS]
47+
[--availability-zone AVAILABILITY_ZONE]
48+
[--vip-network-id VIP_NETWORK_ID]
49+
[--vip-subnet-id VIP_SUBNET_ID] [--details]
50+
[--no-members] [--max-workers MAX_WORKERS]
4751

4852
A script to show OpenStack load balancers information.
4953

5054
options:
5155
-h, --help show this help message and exit
52-
-o {plain,rich,json}, --output-format {plain,rich,json}
53-
Output format: 'plain', 'rich' or 'json'
56+
--os-cloud OS_CLOUD Name of the cloud to load from clouds.yaml.
57+
(Default 'envvars', which uses OS_* env vars)
5458
-t {lb,amphora}, --type {lb,amphora}
5559
Show information about load balancers or amphoras
60+
-o {plain,rich,json}, --output-format {plain,rich,json}
61+
Output format. (default: rich)
5662
--name NAME Filter load balancers name
5763
--id ID Filter load balancers id (UUID)
5864
--tags TAGS Filter load balancers tags
@@ -66,9 +72,12 @@ options:
6672
Filter load balancers network id (UUID)
6773
--vip-subnet-id VIP_SUBNET_ID
6874
Filter load balancers subnet id (UUID)
69-
--details Show all load balancers/amphora details
75+
--details Show all load balancers/amphora details. (default: False)
76+
--no-members Do not show load balancers pool members information.
77+
(default: False)
7078
--max-workers MAX_WORKERS
71-
Max number of concurrent threads to fetch members details (1-32). (default: 4)
79+
Max number of concurrent threads to fetch members details (1-32).
80+
(default: 4)
7281

7382
Example of use:
7483
openstack-lb-info

src/openstack_lb_info/formatters.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,8 @@ def add_pool_to_tree(self, parent_tree, pool):
206206
f"protocol:[magenta]{pool.protocol}[/magenta] "
207207
f"algorithm:[magenta]{pool.lb_algorithm}[/magenta] "
208208
f"prov_status:{self.format_status(pool.provisioning_status)} "
209-
f"oper_status:{self.format_status(pool.operating_status)}"
209+
f"oper_status:{self.format_status(pool.operating_status)} "
210+
f"number_members:[cyan]{len(pool.members)}[/]"
210211
)
211212
return self._add_to_tree(parent_tree, message)
212213

@@ -336,7 +337,8 @@ def add_pool_to_tree(self, parent_tree, pool):
336337
f"protocol:{pool.protocol} "
337338
f"algorithm:{pool.lb_algorithm} "
338339
f"prov_status:{self.format_status(pool.provisioning_status)} "
339-
f"oper_status:{self.format_status(pool.operating_status)}"
340+
f"oper_status:{self.format_status(pool.operating_status)} "
341+
f"number_members:{len(pool.members)}"
340342
)
341343
return self._add_to_tree(parent_tree, message)
342344

src/openstack_lb_info/loadbalancer_info.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,14 @@ class for interacting with the OpenStack environment and uses `OutputFormatter`
1717
about the amphorae associated with a Load Balancer.
1818
"""
1919
import concurrent.futures
20+
import logging
2021
from dataclasses import dataclass
2122

2223
from .formatters import OutputFormatter
2324
from .openstack_api import OpenStackAPI
2425

26+
log = logging.getLogger(__name__)
27+
2528

2629
@dataclass
2730
class ProcessingContext:
@@ -33,11 +36,13 @@ class ProcessingContext:
3336
details (bool): If True, displays detailed attributes of the Load Balancer.
3437
formatter (OutputFormatter): An instance of a formatter class for output formatting.
3538
max_workers (int): Max number of concurrent threads to fetch members details.
39+
no_members (bool): Do not show load balancer pool members information.
3640
"""
3741

3842
openstack_api: OpenStackAPI
3943
details: bool
4044
max_workers: int
45+
no_members: bool
4146
formatter: OutputFormatter
4247

4348

@@ -59,8 +64,10 @@ def __init__(self, lb, context):
5964
self.formatter = context.formatter
6065
self.openstack_api = context.openstack_api
6166
self.max_workers = context.max_workers
67+
self.no_members = context.no_members
6268
# The root of the display tree for the formatter
6369
self.lb_tree = None
70+
log.info("Processing info for Load Balancer ID: %s (Name: %s)", self.lb.id, self.lb.name)
6471

6572
def create_lb_tree(self):
6673
"""
@@ -118,10 +125,11 @@ def add_pool_info(self, listener_tree, pool_id):
118125
else:
119126
self.formatter.add_empty_node(pool_tree, "Health Monitor")
120127

121-
if pool.members:
122-
self.add_pool_members(pool_tree, pool.id, pool.members)
123-
else:
124-
self.formatter.add_empty_node(pool_tree, "Member")
128+
if not self.no_members:
129+
if pool.members:
130+
self.add_pool_members(pool_tree, pool.id, pool.members)
131+
else:
132+
self.formatter.add_empty_node(pool_tree, "Member")
125133
else:
126134
self.formatter.add_empty_node(listener_tree, "Pool")
127135

@@ -155,6 +163,12 @@ def add_pool_members(self, pool_tree, pool_id, pool_members):
155163
"""
156164
# Avoid spinning up extra idle threads
157165
max_workers = min(self.max_workers, len(pool_members))
166+
log.debug(
167+
"Using %s workers to fetch details of %s members (pool_id=%s)",
168+
max_workers,
169+
len(pool_members),
170+
pool_id,
171+
)
158172

159173
with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
160174
# Create future for each member IDs
@@ -206,6 +220,7 @@ def display_lb_info(self):
206220
f"[b]Loadbalancer ID: {self.lb.id} [bright_blue]({self.lb.name})[/]",
207221
align="center",
208222
)
223+
log.info("Displaying final tree for Load Balancer ID: %s", self.lb.id)
209224
self.formatter.print_tree(self.lb_tree)
210225
self.formatter.print("")
211226

src/openstack_lb_info/main.py

Lines changed: 67 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
import argparse
3434
import ipaddress
35+
import logging
3536
import sys
3637
import uuid
3738

@@ -44,6 +45,8 @@
4445
from .loadbalancer_info import AmphoraInfo, LoadBalancerInfo, ProcessingContext
4546
from .openstack_api import OpenStackAPI
4647

48+
log = logging.getLogger(__name__)
49+
4750
# Max allowed threads for --max-workers
4851
MAX_WORKERS_LIMIT = 32
4952

@@ -73,11 +76,20 @@ def parse_parameters():
7376
)
7477

7578
parser.add_argument(
76-
"-o",
77-
"--output-format",
78-
help="Output format: 'plain', 'rich' or 'json'",
79-
choices=("plain", "rich", "json"),
80-
default="rich",
79+
"-d",
80+
"--debug",
81+
help="Enable debug log messages. (default: %(default)s)",
82+
action="store_true",
83+
required=False,
84+
)
85+
parser.add_argument(
86+
"--os-cloud",
87+
help=(
88+
"Name of the cloud to load from clouds.yaml. "
89+
"(Default '%(default)s', which uses OS_* env vars)"
90+
),
91+
type=str,
92+
default="envvars",
8193
required=False,
8294
)
8395
parser.add_argument(
@@ -87,6 +99,14 @@ def parse_parameters():
8799
choices=("lb", "amphora"),
88100
required=True,
89101
)
102+
parser.add_argument(
103+
"-o",
104+
"--output-format",
105+
help="Output format. (default: %(default)s)",
106+
choices=("plain", "rich", "json"),
107+
default="rich",
108+
required=False,
109+
)
90110
parser.add_argument("--name", help="Filter load balancers name", type=str, required=False)
91111
parser.add_argument(
92112
"--id", help="Filter load balancers id (UUID)", type=validate_uuid, required=False
@@ -121,7 +141,13 @@ def parse_parameters():
121141
)
122142
parser.add_argument(
123143
"--details",
124-
help="Show all load balancers/amphora details",
144+
help="Show all load balancers/amphora details. (default: %(default)s)",
145+
action="store_true",
146+
required=False,
147+
)
148+
parser.add_argument(
149+
"--no-members",
150+
help="Do not show load balancers pool members information. (default: %(default)s)",
125151
action="store_true",
126152
required=False,
127153
)
@@ -211,6 +237,23 @@ def validate_ip_address(value_str):
211237
raise argparse.ArgumentTypeError(f"Invalid IP address: {value_str!r}") from exc
212238

213239

240+
def setup_logging(log_level):
241+
"""Setup logging configuration."""
242+
datefmt = "%Y-%m-%d %H:%M:%S"
243+
msg_fmt = "%(asctime)s - %(module)s.%(funcName)s - [%(levelname)s] - %(message)s"
244+
245+
formatter = logging.Formatter(
246+
fmt=msg_fmt,
247+
datefmt=datefmt,
248+
)
249+
handler = logging.StreamHandler(sys.stdout)
250+
handler.setFormatter(formatter)
251+
252+
root_logger = logging.getLogger()
253+
root_logger.setLevel(log_level)
254+
root_logger.addHandler(handler)
255+
256+
214257
def query_openstack_lbs(openstackapi, args, formatter):
215258
"""
216259
Query OpenStack Load Balancers based on user-defined filters.
@@ -238,6 +281,7 @@ def query_openstack_lbs(openstackapi, args, formatter):
238281
}.items()
239282
if v is not None
240283
}
284+
log.debug("Retrieve load balancers filter: %s", filter_criteria)
241285

242286
with formatter.status("Querying load balancers and applying filters..."):
243287
filtered_lbs_tmp = openstackapi.retrieve_load_balancers(filter_criteria)
@@ -283,6 +327,10 @@ def main():
283327

284328
args = parse_parameters()
285329

330+
log_level = logging.DEBUG if args.debug else logging.WARNING
331+
setup_logging(log_level)
332+
log.debug("CMD line args: %s", args)
333+
286334
if args.output_format == "rich" and not RICH_AVAILABLE:
287335
sys.exit(
288336
"Error: 'rich' library is not installed. "
@@ -293,9 +341,18 @@ def main():
293341
formatter = get_formatter(args.output_format)
294342

295343
# Create an instance of OpenStackAPI
296-
openstackapi = OpenStackAPI()
344+
try:
345+
openstackapi = OpenStackAPI(args.os_cloud)
346+
except RuntimeError as exc:
347+
sys.exit(f"Error: {exc}")
348+
349+
try:
350+
filtered_lbs = query_openstack_lbs(openstackapi, args, formatter)
351+
except Exception as exc: # pylint: disable=broad-exception-caught
352+
log.debug("Error to query openstack:", exc_info=True)
353+
sys.exit(f"Error: {exc}")
297354

298-
filtered_lbs = query_openstack_lbs(openstackapi, args, formatter)
355+
log.info("Found %d load balancer(s) to process.", len(filtered_lbs))
299356

300357
if not filtered_lbs:
301358
formatter.print("No load balancer(s) found.")
@@ -305,8 +362,10 @@ def main():
305362
openstack_api=openstackapi,
306363
details=args.details,
307364
max_workers=args.max_workers,
365+
no_members=args.no_members,
308366
formatter=formatter,
309367
)
368+
log.debug("Process context: %s", context)
310369

311370
for lb in filtered_lbs:
312371
if args.type == "amphora":

0 commit comments

Comments
 (0)