nginx-lens 0.2.2__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. {nginx_lens-0.2.2/nginx_lens.egg-info → nginx_lens-0.3.0}/PKG-INFO +1 -1
  2. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/commands/cli.py +2 -0
  3. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/commands/health.py +27 -4
  4. nginx_lens-0.3.0/commands/resolve.py +49 -0
  5. {nginx_lens-0.2.2 → nginx_lens-0.3.0/nginx_lens.egg-info}/PKG-INFO +1 -1
  6. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/nginx_lens.egg-info/SOURCES.txt +1 -0
  7. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/setup.py +1 -1
  8. nginx_lens-0.3.0/upstream_checker/checker.py +151 -0
  9. nginx_lens-0.2.2/upstream_checker/checker.py +0 -62
  10. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/LICENSE +0 -0
  11. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/README.md +0 -0
  12. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/analyzer/__init__.py +0 -0
  13. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/analyzer/base.py +0 -0
  14. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/analyzer/conflicts.py +0 -0
  15. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/analyzer/dead_locations.py +0 -0
  16. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/analyzer/diff.py +0 -0
  17. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/analyzer/duplicates.py +0 -0
  18. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/analyzer/empty_blocks.py +0 -0
  19. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/analyzer/include.py +0 -0
  20. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/analyzer/rewrite.py +0 -0
  21. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/analyzer/route.py +0 -0
  22. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/analyzer/unused.py +0 -0
  23. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/analyzer/warnings.py +0 -0
  24. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/commands/__init__.py +0 -0
  25. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/commands/analyze.py +0 -0
  26. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/commands/diff.py +0 -0
  27. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/commands/graph.py +0 -0
  28. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/commands/include.py +0 -0
  29. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/commands/logs.py +0 -0
  30. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/commands/route.py +0 -0
  31. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/commands/syntax.py +0 -0
  32. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/commands/tree.py +0 -0
  33. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/exporter/__init__.py +0 -0
  34. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/exporter/graph.py +0 -0
  35. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/exporter/html.py +0 -0
  36. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/exporter/markdown.py +0 -0
  37. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/nginx_lens.egg-info/dependency_links.txt +0 -0
  38. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/nginx_lens.egg-info/entry_points.txt +0 -0
  39. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/nginx_lens.egg-info/requires.txt +0 -0
  40. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/nginx_lens.egg-info/top_level.txt +0 -0
  41. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/parser/__init__.py +0 -0
  42. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/parser/nginx_parser.py +0 -0
  43. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/pyproject.toml +0 -0
  44. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/setup.cfg +0 -0
  45. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/tests/test_conflicts.py +0 -0
  46. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/tests/test_duplicates.py +0 -0
  47. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/tests/test_empty_blocks.py +0 -0
  48. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/tests/test_health.py +0 -0
  49. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/tests/test_parser.py +0 -0
  50. {nginx_lens-0.2.2 → nginx_lens-0.3.0}/upstream_checker/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nginx-lens
3
- Version: 0.2.2
3
+ Version: 0.3.0
4
4
  Summary: CLI-инструмент для анализа, визуализации и диагностики конфигураций Nginx
5
5
  Author: Daniil Astrouski
6
6
  Author-email: shelovesuastra@gmail.com
@@ -9,6 +9,7 @@ from commands.include import include_tree
9
9
  from commands.graph import graph
10
10
  from commands.logs import logs
11
11
  from commands.syntax import syntax
12
+ from commands.resolve import resolve
12
13
 
13
14
  app = typer.Typer(help="nginx-lens — анализ и диагностика конфигураций Nginx")
14
15
  console = Console()
@@ -22,6 +23,7 @@ app.command()(include_tree)
22
23
  app.command()(graph)
23
24
  app.command()(logs)
24
25
  app.command()(syntax)
26
+ app.command()(resolve)
25
27
 
26
28
  if __name__ == "__main__":
27
29
  app()
@@ -1,7 +1,7 @@
1
1
  import typer
2
2
  from rich.console import Console
3
3
  from rich.table import Table
4
- from upstream_checker.checker import check_upstreams
4
+ from upstream_checker.checker import check_upstreams, resolve_upstreams
5
5
  from parser.nginx_parser import parse_nginx_config
6
6
 
7
7
  app = typer.Typer()
@@ -12,6 +12,7 @@ def health(
12
12
  timeout: float = typer.Option(2.0, help="Таймаут проверки (сек)"),
13
13
  retries: int = typer.Option(1, help="Количество попыток"),
14
14
  mode: str = typer.Option("tcp", help="Режим проверки: tcp или http", case_sensitive=False),
15
+ resolve: bool = typer.Option(False, "--resolve", "-r", help="Показать резолвленные IP-адреса"),
15
16
  ):
16
17
  """
17
18
  Проверяет доступность upstream-серверов, определённых в nginx.conf. Выводит таблицу.
@@ -19,6 +20,7 @@ def health(
19
20
  Пример:
20
21
  nginx-lens health /etc/nginx/nginx.conf
21
22
  nginx-lens health /etc/nginx/nginx.conf --timeout 5 --retries 3 --mode http
23
+ nginx-lens health /etc/nginx/nginx.conf --resolve
22
24
  """
23
25
  try:
24
26
  tree = parse_nginx_config(config_path)
@@ -31,15 +33,36 @@ def health(
31
33
 
32
34
  upstreams = tree.get_upstreams()
33
35
  results = check_upstreams(upstreams, timeout=timeout, retries=retries, mode=mode.lower())
36
+
37
+ # Если нужно показать резолвленные IP-адреса
38
+ resolved_info = {}
39
+ if resolve:
40
+ resolved_info = resolve_upstreams(upstreams)
34
41
 
35
42
  table = Table(show_header=True, header_style="bold blue")
36
- table.add_column("upstream_name")
37
- table.add_column("upstream_status")
43
+ table.add_column("Address")
44
+ table.add_column("Status")
45
+ if resolve:
46
+ table.add_column("Resolved IP")
38
47
 
39
48
  for name, servers in results.items():
40
49
  for srv in servers:
41
50
  status = "Healthy" if srv["healthy"] else "Unhealthy"
42
51
  color = "green" if srv["healthy"] else "red"
43
- table.add_row(srv["address"], f"[{color}]{status}[/{color}]")
52
+
53
+ if resolve:
54
+ resolved = None
55
+ if name in resolved_info:
56
+ for resolved_srv in resolved_info[name]:
57
+ if resolved_srv["address"] == srv["address"]:
58
+ resolved = resolved_srv["resolved"]
59
+ break
60
+
61
+ if resolved:
62
+ table.add_row(srv["address"], f"[{color}]{status}[/{color}]", resolved)
63
+ else:
64
+ table.add_row(srv["address"], f"[{color}]{status}[/{color}]", "[yellow]Failed to resolve[/yellow]")
65
+ else:
66
+ table.add_row(srv["address"], f"[{color}]{status}[/{color}]")
44
67
 
45
68
  console.print(table)
@@ -0,0 +1,49 @@
1
+ import typer
2
+ from rich.console import Console
3
+ from rich.table import Table
4
+ from upstream_checker.checker import resolve_upstreams
5
+ from parser.nginx_parser import parse_nginx_config
6
+
7
+ app = typer.Typer()
8
+ console = Console()
9
+
10
+ def resolve(
11
+ config_path: str = typer.Argument(..., help="Путь к nginx.conf"),
12
+ ):
13
+ """
14
+ Резолвит DNS имена upstream-серверов в IP-адреса.
15
+
16
+ Пример:
17
+ nginx-lens resolve /etc/nginx/nginx.conf
18
+ """
19
+ try:
20
+ tree = parse_nginx_config(config_path)
21
+ except FileNotFoundError:
22
+ console.print(f"[red]Файл {config_path} не найден. Проверьте путь к конфигу.[/red]")
23
+ return
24
+ except Exception as e:
25
+ console.print(f"[red]Ошибка при разборе {config_path}: {e}[/red]")
26
+ return
27
+
28
+ upstreams = tree.get_upstreams()
29
+ if not upstreams:
30
+ console.print("[yellow]Не найдено ни одного upstream в конфигурации.[/yellow]")
31
+ return
32
+
33
+ results = resolve_upstreams(upstreams)
34
+
35
+ table = Table(show_header=True, header_style="bold blue")
36
+ table.add_column("Upstream Name")
37
+ table.add_column("Address")
38
+ table.add_column("Resolved IP")
39
+
40
+ for name, servers in results.items():
41
+ for idx, srv in enumerate(servers):
42
+ upstream_name = name if idx == 0 else ""
43
+ if srv["resolved"]:
44
+ table.add_row(upstream_name, srv["address"], f"[green]{srv['resolved']}[/green]")
45
+ else:
46
+ table.add_row(upstream_name, srv["address"], "[red]Failed to resolve[/red]")
47
+
48
+ console.print(table)
49
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nginx-lens
3
- Version: 0.2.2
3
+ Version: 0.3.0
4
4
  Summary: CLI-инструмент для анализа, визуализации и диагностики конфигураций Nginx
5
5
  Author: Daniil Astrouski
6
6
  Author-email: shelovesuastra@gmail.com
@@ -22,6 +22,7 @@ commands/graph.py
22
22
  commands/health.py
23
23
  commands/include.py
24
24
  commands/logs.py
25
+ commands/resolve.py
25
26
  commands/route.py
26
27
  commands/syntax.py
27
28
  commands/tree.py
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="nginx-lens",
5
- version="0.2.2",
5
+ version="0.3.0",
6
6
  description="CLI-инструмент для анализа, визуализации и диагностики конфигураций Nginx",
7
7
  author="Daniil Astrouski",
8
8
  author_email="shelovesuastra@gmail.com",
@@ -0,0 +1,151 @@
1
+ # upstream_checker/checker.py
2
+
3
+ import socket
4
+ import time
5
+ import http.client
6
+ from typing import Dict, List, Optional
7
+
8
+
9
+ def check_tcp(address: str, timeout: float, retries: int) -> bool:
10
+ """
11
+ Проверка доступности сервера по TCP.
12
+ Ignores extra upstream options like 'max_fails' or 'fail_timeout'.
13
+ """
14
+ # Берем только host:port, игнорируем параметры
15
+ host_port = address.split()[0]
16
+ host, port = host_port.split(":")
17
+ port = int(port)
18
+
19
+ for _ in range(retries):
20
+ try:
21
+ with socket.create_connection((host, port), timeout=timeout):
22
+ return True
23
+ except (socket.timeout, ConnectionRefusedError, OSError):
24
+ time.sleep(0.2)
25
+ return False
26
+
27
+
28
+ def check_http(address: str, timeout: float, retries: int) -> bool:
29
+ """
30
+ Проверка доступности сервера по HTTP (GET /).
31
+ Ignores extra upstream options like 'max_fails' or 'fail_timeout'.
32
+ """
33
+ host_port = address.split()[0]
34
+ host, port = host_port.split(":")
35
+ port = int(port)
36
+
37
+ for _ in range(retries):
38
+ try:
39
+ conn = http.client.HTTPConnection(host, port, timeout=timeout)
40
+ conn.request("GET", "/")
41
+ resp = conn.getresponse()
42
+ healthy = resp.status < 500
43
+ conn.close()
44
+ if healthy:
45
+ return True
46
+ except Exception:
47
+ time.sleep(0.2)
48
+ continue
49
+ return False
50
+
51
+
52
+ def resolve_address(address: str) -> Optional[str]:
53
+ """
54
+ Резолвит адрес upstream сервера в IP-адрес.
55
+
56
+ Args:
57
+ address: Адрес в формате "host:port" или "host:port параметры"
58
+
59
+ Returns:
60
+ IP-адрес в формате "ip:port" или None, если резолвинг не удался
61
+ """
62
+ try:
63
+ host_port = address.split()[0]
64
+
65
+ if ":" not in host_port:
66
+ return None
67
+
68
+ parts = host_port.rsplit(":", 1)
69
+ if len(parts) != 2:
70
+ return None
71
+ host, port = parts
72
+
73
+ try:
74
+ socket.inet_aton(host)
75
+ return host_port
76
+ except socket.error:
77
+ pass
78
+
79
+ if host.startswith("[") and host.endswith("]"):
80
+ ipv6_host = host[1:-1]
81
+ try:
82
+ socket.inet_pton(socket.AF_INET6, ipv6_host)
83
+ return host_port
84
+ except (socket.error, OSError):
85
+ pass
86
+
87
+ try:
88
+ ip = socket.gethostbyname(host)
89
+ return f"{ip}:{port}"
90
+ except (socket.gaierror, OSError):
91
+ return None
92
+ except (ValueError, IndexError, AttributeError):
93
+ return None
94
+
95
+
96
+ def resolve_upstreams(
97
+ upstreams: Dict[str, List[str]]
98
+ ) -> Dict[str, List[dict]]:
99
+ """
100
+ Резолвит DNS имена upstream-серверов в IP-адреса.
101
+
102
+ Возвращает:
103
+ {
104
+ "backend": [
105
+ {"address": "example.com:8080", "resolved": "192.168.1.1:8080"},
106
+ {"address": "127.0.0.1:8080", "resolved": "127.0.0.1:8080"},
107
+ {"address": "badhost:80", "resolved": None},
108
+ ...
109
+ ]
110
+ }
111
+ """
112
+ results = {}
113
+ for name, servers in upstreams.items():
114
+ results[name] = []
115
+ for srv in servers:
116
+ resolved = resolve_address(srv)
117
+ results[name].append({
118
+ "address": srv,
119
+ "resolved": resolved
120
+ })
121
+ return results
122
+
123
+
124
+ def check_upstreams(
125
+ upstreams: Dict[str, List[str]],
126
+ timeout: float = 2.0,
127
+ retries: int = 1,
128
+ mode: str = "tcp"
129
+ ) -> Dict[str, List[dict]]:
130
+ """
131
+ Проверяет доступность upstream-серверов.
132
+ mode: "tcp" (по умолчанию) или "http"
133
+
134
+ Возвращает:
135
+ {
136
+ "backend": [
137
+ {"address": "127.0.0.1:8080", "healthy": True},
138
+ ...
139
+ ]
140
+ }
141
+ """
142
+ results = {}
143
+ for name, servers in upstreams.items():
144
+ results[name] = []
145
+ for srv in servers:
146
+ if mode.lower() == "http":
147
+ healthy = check_http(srv, timeout, retries)
148
+ else:
149
+ healthy = check_tcp(srv, timeout, retries)
150
+ results[name].append({"address": srv, "healthy": healthy})
151
+ return results
@@ -1,62 +0,0 @@
1
- # upstream_checker/checker.py
2
-
3
- import socket
4
- import time
5
- import http.client
6
- from typing import Dict, List
7
-
8
-
9
- def check_tcp(address: str, timeout: float, retries: int) -> bool:
10
- """Проверка доступности сервера по TCP"""
11
- host, port = address.split(":")
12
- port = int(port)
13
- for _ in range(retries):
14
- try:
15
- with socket.create_connection((host, port), timeout=timeout):
16
- return True
17
- except (socket.timeout, ConnectionRefusedError, OSError):
18
- time.sleep(0.2)
19
- return False
20
-
21
-
22
- def check_http(address: str, timeout: float, retries: int) -> bool:
23
- """Проверка доступности сервера по HTTP (GET /)"""
24
- host, port = address.split(":")
25
- port = int(port)
26
- for _ in range(retries):
27
- try:
28
- conn = http.client.HTTPConnection(host, port, timeout=timeout)
29
- conn.request("GET", "/")
30
- resp = conn.getresponse()
31
- healthy = resp.status < 500
32
- conn.close()
33
- if healthy:
34
- return True
35
- except Exception:
36
- time.sleep(0.2)
37
- continue
38
- return False
39
-
40
-
41
- def check_upstreams(upstreams: Dict[str, List[str]], timeout=2.0, retries=1, mode="tcp") -> Dict[str, List[dict]]:
42
- """
43
- Проверяет доступность upstream-серверов.
44
- mode: "tcp" (по умолчанию) или "http"
45
- Возвращает:
46
- {
47
- "backend": [
48
- {"address": "127.0.0.1:8080", "healthy": True},
49
- ...
50
- ]
51
- }
52
- """
53
- results = {}
54
- for name, servers in upstreams.items():
55
- results[name] = []
56
- for srv in servers:
57
- if mode.lower() == "http":
58
- healthy = check_http(srv, timeout, retries)
59
- else:
60
- healthy = check_tcp(srv, timeout, retries)
61
- results[name].append({"address": srv, "healthy": healthy})
62
- return results
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes