|
1 | | -import subprocess |
| 1 | +import requests |
| 2 | +import json |
2 | 3 | import re |
3 | 4 |
|
4 | 5 | from wanda.logger import Logger |
|
8 | 9 |
|
9 | 10 | class IRRDClient: |
10 | 11 |
|
11 | | - POSSIBLE_RETRIES = 3 |
12 | | - |
13 | 12 | def __init__(self, irrd_url): |
14 | | - self.data = [] |
15 | | - self.irrdURL = irrd_url |
16 | | - self.host_params = ['-h', irrd_url] |
17 | | - |
18 | | - def call_subprocess(self, command_array): |
19 | | - |
20 | | - current_try = 1 |
21 | | - |
22 | | - while current_try <= self.POSSIBLE_RETRIES: |
23 | | - result = subprocess.run(command_array, capture_output=True) |
24 | | - if result.returncode == 0: |
25 | | - result_str = result.stdout.decode("utf-8") |
26 | | - return result_str |
| 13 | + self.irrdURL = f"https://{irrd_url}/graphql/" |
27 | 14 |
|
28 | | - current_try += 1 |
| 15 | + def fetch_graphql_data(self, query): |
| 16 | + response = requests.post(url=self.irrdURL, json={"query": query}) |
| 17 | + if response.status_code == 200: |
| 18 | + return json.loads(response.content)["data"] |
29 | 19 |
|
30 | | - l.error(f"Failed to execute command: {' '.join(command_array)}") |
31 | | - raise Exception("bgpq4 could not be called successfully, this may be an programming error or a bad internet connection.") |
32 | | - |
33 | | - def call_bgpq4_aspath_access_list(self, asn, irr_name): |
34 | | - command_array = ["bgpq4", *self.host_params, "-H", str(asn), "-W 100", "-J", "-l", f"AS{asn}_ORIGINS", irr_name] |
35 | | - return self.call_subprocess(command_array) |
| 20 | + return None |
36 | 21 |
|
37 | 22 | def generate_input_aspath_access_list(self, asn, irr_name): |
38 | | - # bgpq4 AS-TELIANET-V6 -H 1299 -W 100 -J -l AS1299_ORIGINS |
39 | | - result_str = self.call_bgpq4_aspath_access_list(asn, irr_name) |
40 | | - m = re.search(r'.*as-list-group.*{(.|\n)*?}', result_str) |
41 | | - |
42 | | - if m: |
43 | | - # Technically, only adding the AS..._NEIGHBOR list would work, but we do some cleaning for better quality of the generated configuration |
44 | | - |
45 | | - lines = m[0].split("\n") |
46 | | - new_lines = list() |
47 | | - new_lines.append(f"as-list AS{asn}_NEIGHBOR members {asn};") |
48 | | - indent_count = 0 |
| 23 | + if re.match(r"^AS\d+$", irr_name): |
| 24 | + return [ irr_name[2:] ] |
49 | 25 |
|
50 | | - for line in lines: |
51 | | - line_without_prefixed_spaces = line.lstrip() |
| 26 | + body = f""" |
| 27 | + {{ |
| 28 | + recursiveSetMembers(setNames: ["{irr_name}"], depth: 5) {{ members }} |
| 29 | + }} |
| 30 | + """ |
| 31 | + result = self.fetch_graphql_data(body) |
52 | 32 |
|
53 | | - if '}' in line_without_prefixed_spaces: |
54 | | - indent_count -= 1 |
| 33 | + # return unique members that are ASNs |
| 34 | + members = set(result["recursiveSetMembers"][0]["members"]) |
| 35 | + return [int(i[2:]) for i in members if re.match(r"^AS\d+$", i)] |
55 | 36 |
|
56 | | - spaces = [" " for _ in range(indent_count * 4)] |
57 | | - new_lines.append("".join(spaces) + line_without_prefixed_spaces) |
58 | | - |
59 | | - if '{' in line_without_prefixed_spaces: |
60 | | - indent_count += 1 |
61 | | - |
62 | | - return "\n".join(new_lines) |
63 | | - |
64 | | - return None |
65 | | - |
66 | | - def call_bgpq4_prefix_lists(self, irr_name, ip_version): |
67 | | - command_array = ["bgpq4", *self.host_params, f"-{ip_version}", "-F", "%n/%l\n", irr_name] |
68 | | - return self.call_subprocess(command_array) |
69 | 37 |
|
70 | 38 | def generate_prefix_lists(self, irr_name): |
71 | | - result_v4 = self.call_bgpq4_prefix_lists(irr_name, 4) |
72 | | - result_v6 = self.call_bgpq4_prefix_lists(irr_name, 6) |
73 | | - |
74 | | - result_entries_v4 = result_v4.splitlines() |
75 | | - result_entries_v6 = result_v6.splitlines() |
76 | | - |
77 | | - # Stripping empty lines |
78 | | - result_entries_v4_cleaned = [x for x in result_entries_v4 if x] |
79 | | - result_entries_v6_cleaned = [x for x in result_entries_v6 if x] |
80 | | - |
81 | | - return result_entries_v4_cleaned, result_entries_v6_cleaned |
| 39 | + if re.match(r"^AS\d+$", irr_name): |
| 40 | + # ASNs |
| 41 | + body = f""" |
| 42 | + {{ |
| 43 | + v4: asnPrefixes(asns: ["{irr_name[2:]}"], ipVersion: 4) {{ prefixes }} |
| 44 | + v6: asnPrefixes(asns: ["{irr_name[2:]}"], ipVersion: 6) {{ prefixes }} |
| 45 | + }} |
| 46 | + """ |
| 47 | + else: |
| 48 | + # AS-Set |
| 49 | + body = f""" |
| 50 | + {{ |
| 51 | + v4: asSetPrefixes(setNames: ["{irr_name}"], ipVersion: 4) {{ prefixes }} |
| 52 | + v6: asSetPrefixes(setNames: ["{irr_name}"], ipVersion: 6) {{ prefixes }} |
| 53 | + }} |
| 54 | + """ |
| 55 | + result = self.fetch_graphql_data(body) |
| 56 | + |
| 57 | + return result["v4"][0]["prefixes"], result["v6"][0]["prefixes"] |
0 commit comments