Skip to content

Commit be61636

Browse files
authored
Add generate_bgp_test_data management command (#269)
1 parent beb27d7 commit be61636

File tree

3 files changed

+323
-0
lines changed

3 files changed

+323
-0
lines changed

changes/269.housekeeping

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added generate_bgp_test_data management command.
Lines changed: 303 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
"""Generate test data for the BGP Models app."""
2+
3+
import random
4+
from itertools import product
5+
6+
from django.contrib.contenttypes.models import ContentType
7+
from django.core.management.base import BaseCommand
8+
from django.db import DEFAULT_DB_ALIAS
9+
from nautobot.circuits.models import Provider
10+
from nautobot.core.factory import get_random_instances
11+
from nautobot.dcim.models import Device, Interface
12+
from nautobot.extras.models import Role, Secret, Status
13+
from nautobot.ipam.models import VRF, IPAddress
14+
from nautobot.tenancy.models import Tenant
15+
16+
from nautobot_bgp_models.choices import AFISAFIChoices
17+
from nautobot_bgp_models.models import (
18+
AddressFamily,
19+
AutonomousSystem,
20+
AutonomousSystemRange,
21+
BGPRoutingInstance,
22+
PeerEndpoint,
23+
PeerEndpointAddressFamily,
24+
PeerGroup,
25+
PeerGroupAddressFamily,
26+
PeerGroupTemplate,
27+
Peering,
28+
)
29+
30+
31+
class Command(BaseCommand):
32+
"""Populate the database with various data as a baseline for testing (automated or manual)."""
33+
34+
help = __doc__
35+
36+
def add_arguments(self, parser): # noqa: D102
37+
parser.add_argument(
38+
"--database",
39+
default=DEFAULT_DB_ALIAS,
40+
help='The database to generate the test data in. Defaults to the "default" database.',
41+
)
42+
parser.add_argument(
43+
"--flush",
44+
action="store_true",
45+
help="Flush any existing bgp models data from the database before generating new data.",
46+
)
47+
48+
def _generate_static_data(self, db):
49+
providers = get_random_instances(
50+
Provider.objects.using(db).all(),
51+
minimum=2,
52+
maximum=4,
53+
)
54+
devices = get_random_instances(
55+
Device.objects.using(db).all(),
56+
minimum=2,
57+
maximum=4,
58+
)
59+
ip_addresses = get_random_instances(
60+
IPAddress.objects.using(db).all(),
61+
minimum=2,
62+
maximum=4,
63+
)
64+
tenants = get_random_instances(
65+
Tenant.objects.using(db).all(),
66+
minimum=2,
67+
maximum=4,
68+
)
69+
roles = get_random_instances(
70+
Role.objects.using(db).all(),
71+
minimum=2,
72+
maximum=4,
73+
)
74+
secrets = get_random_instances(
75+
Secret.objects.using(db).all(),
76+
minimum=2,
77+
maximum=4,
78+
)
79+
vrfs = get_random_instances(
80+
VRF.objects.using(db).all(),
81+
minimum=2,
82+
maximum=4,
83+
)
84+
interfaces = get_random_instances(
85+
Interface.objects.using(db).all(),
86+
minimum=2,
87+
maximum=4,
88+
)
89+
statuses = get_random_instances(
90+
Status.objects.using(db).all(),
91+
minimum=2,
92+
maximum=4,
93+
)
94+
for status in statuses:
95+
status.content_types.add(
96+
*ContentType.objects.filter(
97+
app_label="nautobot_bgp_models", model__in=["autonomoussystem", "bgproutinginstance", "peering"]
98+
).values_list("pk", flat=True)
99+
)
100+
for role in roles:
101+
role.content_types.add(
102+
*ContentType.objects.filter(
103+
app_label="nautobot_bgp_models", model__in=["peergrouptemplate", "peergroup", "peerendpoint"]
104+
)
105+
)
106+
107+
if len(devices) == 0:
108+
raise RuntimeError("No devices were found. At least one device is required.")
109+
110+
# Create Peerings
111+
peerings = []
112+
message = "Creating 8 Peerings..."
113+
self.stdout.write(message)
114+
for _ in range(1, 9):
115+
status = random.choice([*statuses, None]) # noqa: S311
116+
peerings.append(Peering.objects.using(db).create(status=status))
117+
118+
# Create AutonomousSystemRanges
119+
autonomous_system_ranges = []
120+
message = "Creating 8 AutonomousSystemRanges..."
121+
self.stdout.write(message)
122+
for i in range(1, 9):
123+
asn_min = random.randint(64512, 65533) # noqa: S311
124+
asn_max = random.randint(asn_min, 65534) # noqa: S311
125+
tenant = random.choice([*tenants, None]) # noqa: S311
126+
autonomous_system_ranges.append(
127+
AutonomousSystemRange.objects.using(db).create(
128+
name=f"AutonomousSystemRange{i}",
129+
asn_min=asn_min,
130+
asn_max=asn_max,
131+
description=f"Autonomous System Range {asn_min} - {asn_max}",
132+
tenant=tenant,
133+
)
134+
)
135+
136+
# Create AutonomousSystems
137+
autonomous_systems = []
138+
message = "Creating 8 AutonomousSystems..."
139+
asns = random.choices(range(64512, 65534), k=8) # noqa: S311
140+
self.stdout.write(message)
141+
for i in range(0, 8):
142+
provider = random.choice([*providers, None]) # noqa: S311
143+
status = random.choice([*statuses, None]) # noqa: S311
144+
autonomous_systems.append(
145+
AutonomousSystem.objects.using(db).create(
146+
asn=asns[i],
147+
description=f"Autonomous System {asns[i]}",
148+
provider=provider,
149+
status=status,
150+
)
151+
)
152+
153+
# Create BGPRoutingInstances
154+
bgp_routing_instances = []
155+
message = "Creating 8 BGPRoutingInstances..."
156+
self.stdout.write(message)
157+
device_autonomous_system_combinations = random.sample(list(product(autonomous_systems, devices)), k=8) # noqa: S311
158+
for i in range(0, 8):
159+
status = random.choice([*statuses, None]) # noqa: S311
160+
ip_address = random.choice([*ip_addresses, None]) # noqa: S311
161+
bgp_routing_instances.append(
162+
BGPRoutingInstance.objects.using(db).create(
163+
description=f"BGP Routing Instance {i+1}",
164+
device=device_autonomous_system_combinations[i][1],
165+
router_id=ip_address,
166+
autonomous_system=device_autonomous_system_combinations[i][0],
167+
status=status,
168+
)
169+
)
170+
171+
# Create AddressFamilies
172+
address_families = []
173+
message = "Creating 8 AddressFamilies..."
174+
afi_safi_routing_instance_vrf_combinations = random.sample(
175+
list(product(AFISAFIChoices.values(), bgp_routing_instances, [*vrfs, None])), k=8
176+
)
177+
self.stdout.write(message)
178+
for i in range(0, 8):
179+
address_families.append(
180+
AddressFamily.objects.using(db).create(
181+
afi_safi=afi_safi_routing_instance_vrf_combinations[i][0],
182+
routing_instance=afi_safi_routing_instance_vrf_combinations[i][1],
183+
vrf=afi_safi_routing_instance_vrf_combinations[i][2],
184+
)
185+
)
186+
187+
# Create PeerGroupTemplates
188+
peer_group_templates = []
189+
message = "Creating 8 PeerGroupTemplates..."
190+
self.stdout.write(message)
191+
for i in range(1, 9):
192+
role = random.choice([*roles, None]) # noqa: S311
193+
autonomous_system = random.choice([*autonomous_systems, None]) # noqa: S311
194+
secret = random.choice([*secrets, None]) # noqa: S311
195+
peer_group_templates.append(
196+
PeerGroupTemplate.objects.using(db).create(
197+
name=f"PeerGroupTemplate{i}",
198+
description=f"Peer Group Template {i}",
199+
enabled=random.choice([True, False]), # noqa: S311
200+
role=role,
201+
autonomous_system=autonomous_system,
202+
secret=secret,
203+
)
204+
)
205+
206+
# Create PeerGroups
207+
peer_groups = []
208+
message = "Creating 8 PeerGroups..."
209+
self.stdout.write(message)
210+
for i in range(1, 9):
211+
role = random.choice([*roles, None]) # noqa: S311
212+
autonomous_system = random.choice([*autonomous_systems, None]) # noqa: S311
213+
secret = random.choice([*secrets, None]) # noqa: S311
214+
routing_instance = random.choice(bgp_routing_instances) # noqa: S311
215+
peer_group_template = random.choice([*peer_group_templates, None]) # noqa: S311
216+
peer_groups.append(
217+
PeerGroup.objects.using(db).create(
218+
name=f"PeerGroup{i}",
219+
description=f"Peer Group {i}",
220+
enabled=random.choice([True, False]), # noqa: S311
221+
role=role,
222+
autonomous_system=autonomous_system,
223+
secret=secret,
224+
routing_instance=routing_instance,
225+
peergroup_template=peer_group_template,
226+
)
227+
)
228+
229+
# Create PeerGroupAddressFamilys
230+
peer_group_address_families = []
231+
message = "Creating 8 PeerGroupAddressFamilies..."
232+
self.stdout.write(message)
233+
afi_safi_peer_group_combinations = random.sample(list(product(AFISAFIChoices.values(), peer_groups)), k=8)
234+
for i in range(0, 8):
235+
peer_group_address_families.append(
236+
PeerGroupAddressFamily.objects.using(db).create(
237+
afi_safi=afi_safi_peer_group_combinations[i][0],
238+
peer_group=afi_safi_peer_group_combinations[i][1],
239+
multipath=random.choice([True, False, None]), # noqa: S311
240+
)
241+
)
242+
243+
# Create PeerEndpoints
244+
peer_endpoints = []
245+
message = "Creating 8 PeerEndpoints..."
246+
self.stdout.write(message)
247+
for i in range(1, 9):
248+
role = random.choice([*roles, None]) # noqa: S311
249+
routing_instance = random.choice([*bgp_routing_instances, None]) # noqa: S311
250+
autonomous_system = random.choice([*autonomous_systems, None]) # noqa: S311
251+
peer = random.choice([*peer_endpoints, None]) # noqa: S311
252+
peering = random.choice(peerings) # noqa: S311
253+
peer_group = random.choice([*peer_groups, None]) # noqa: S311
254+
ip_address = random.choice([*ip_addresses, None]) # noqa: S311
255+
interface = random.choice([*interfaces, None]) # noqa: S311
256+
secret = random.choice([*secrets, None]) # noqa: S311
257+
peer_endpoints.append(
258+
PeerEndpoint.objects.using(db).create(
259+
description=f"Peer Endpoint {i}",
260+
role=role,
261+
enabled=random.choice([True, False]), # noqa: S311
262+
routing_instance=routing_instance,
263+
autonomous_system=autonomous_system,
264+
peer=peer,
265+
peering=peering,
266+
peer_group=peer_group,
267+
source_ip=ip_address,
268+
source_interface=interface,
269+
secret=secret,
270+
)
271+
)
272+
273+
# Create PeerEndpointAddressFamilys
274+
peer_endpoint_address_families = []
275+
message = "Creating 8 PeerEndpointAddressFamilies..."
276+
self.stdout.write(message)
277+
afi_safi_peer_endpoint_combinations = random.sample(list(product(AFISAFIChoices.values(), peer_endpoints)), k=8)
278+
for i in range(0, 8):
279+
peer_endpoint_address_families.append(
280+
PeerEndpointAddressFamily.objects.using(db).create(
281+
afi_safi=afi_safi_peer_endpoint_combinations[i][0],
282+
peer_endpoint=afi_safi_peer_endpoint_combinations[i][1],
283+
)
284+
)
285+
286+
def handle(self, *args, **options):
287+
"""Entry point to the management command."""
288+
if options["flush"]:
289+
self.stdout.write(self.style.WARNING("Flushing bgp models objects from the database..."))
290+
PeerEndpointAddressFamily.objects.using(options["database"]).all().delete()
291+
PeerEndpoint.objects.using(options["database"]).all().delete()
292+
PeerGroupAddressFamily.objects.using(options["database"]).all().delete()
293+
PeerGroup.objects.using(options["database"]).all().delete()
294+
PeerGroupTemplate.objects.using(options["database"]).all().delete()
295+
AddressFamily.objects.using(options["database"]).all().delete()
296+
BGPRoutingInstance.objects.using(options["database"]).all().delete()
297+
AutonomousSystem.objects.using(options["database"]).all().delete()
298+
AutonomousSystemRange.objects.using(options["database"]).all().delete()
299+
Peering.objects.using(options["database"]).all().delete()
300+
301+
self._generate_static_data(db=options["database"])
302+
303+
self.stdout.write(self.style.SUCCESS(f"Database {options['database']} populated with app data successfully!"))

tasks.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,3 +1022,22 @@ def validate_app_config(context):
10221022
file="development/app_config_schema.py",
10231023
env={"APP_CONFIG_SCHEMA_COMMAND": "validate"},
10241024
)
1025+
1026+
1027+
@task
1028+
def generate_test_data(context, flush=False, database=None):
1029+
"""Generate test data in Nautobot for this app."""
1030+
# Run the core generate_test_data command first to populate the core models
1031+
command = "nautobot-server generate_test_data --seed 'nautobot'"
1032+
if database:
1033+
command += f" --database {database}"
1034+
if flush:
1035+
command += " --flush"
1036+
run_command(context, command)
1037+
1038+
command = "nautobot-server generate_bgp_test_data"
1039+
if database:
1040+
command += f" --database {database}"
1041+
if flush:
1042+
command += " --flush"
1043+
run_command(context, command)

0 commit comments

Comments
 (0)