Skip to content

Commit d7b523f

Browse files
committed
[loadtest] Add a test with a big subscription on N clusters
1 parent 057062b commit d7b523f

File tree

4 files changed

+216
-65
lines changed

4 files changed

+216
-65
lines changed
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
import argparse
2+
import datetime
3+
import random
4+
import uuid
5+
from collections import namedtuple
6+
7+
import client
8+
import locust
9+
from geo_utils import create_random_flight_path_volume
10+
from utils import format_time
11+
12+
from monitoring.monitorlib.testing import make_fake_url
13+
14+
Cluster = namedtuple("Cluster", ["lng", "lat", "uuid", "version"])
15+
16+
17+
@locust.events.init_command_line_parser.add_listener
18+
def init_parser(parser: argparse.ArgumentParser):
19+
"""Setup config params, populated by locust.conf."""
20+
21+
parser.add_argument(
22+
"--uss-base-url",
23+
type=str,
24+
help="Base URL of the Token Exchanger from which to request JWTs",
25+
required=True,
26+
)
27+
parser.add_argument(
28+
"--cluster-count",
29+
type=int,
30+
help="Number of clusters to create. One subscription will be created per cluster per client.",
31+
required=True,
32+
)
33+
parser.add_argument(
34+
"--base-lat",
35+
type=float,
36+
help="Latitude of the center of the first cluster",
37+
required=True,
38+
)
39+
parser.add_argument(
40+
"--base-lng",
41+
type=float,
42+
help="Longitude of the center of the first cluster",
43+
required=True,
44+
)
45+
parser.add_argument(
46+
"--area-radius",
47+
type=int,
48+
help="Radius (in meters) of clusters.",
49+
required=True,
50+
)
51+
parser.add_argument(
52+
"--max-flight-distance",
53+
type=int,
54+
help="Maximum distance to cover for an individual flight",
55+
required=True,
56+
)
57+
58+
59+
class SCD(client.USS):
60+
wait_time = locust.between(0.01, 0.1)
61+
62+
def on_start(self):
63+
self.uss_base_url = self.environment.parsed_options.uss_base_url
64+
self.radius = self.environment.parsed_options.area_radius
65+
self.max_flight_distance = self.environment.parsed_options.max_flight_distance
66+
67+
self.clusters = []
68+
69+
lat = self.environment.parsed_options.base_lat
70+
lng = self.environment.parsed_options.base_lng
71+
72+
time_start = datetime.datetime.now(datetime.UTC)
73+
time_end = time_start + datetime.timedelta(minutes=60)
74+
75+
for _ in range(self.environment.parsed_options.cluster_count):
76+
sub_uuid = str(uuid.uuid4())
77+
78+
resp = self.client.put(
79+
f"/dss/v1/subscriptions/{sub_uuid}",
80+
json={
81+
"extents": {
82+
"volume": {
83+
"outline_circle": {
84+
"center": {"lng": lng, "lat": lat},
85+
"radius": {"value": self.radius, "units": "M"},
86+
},
87+
"altitude_lower": {
88+
"value": 0,
89+
"reference": "W84",
90+
"units": "M",
91+
},
92+
"altitude_upper": {
93+
"value": 10000,
94+
"reference": "W84",
95+
"units": "M",
96+
},
97+
},
98+
"time_start": {
99+
"value": format_time(time_start),
100+
"format": "RFC3339",
101+
},
102+
"time_end": {
103+
"value": format_time(time_end),
104+
"format": "RFC3339",
105+
},
106+
},
107+
"uss_base_url": make_fake_url(),
108+
"notify_for_operational_intents": True,
109+
},
110+
name="/subscriptions/[sub_uuid]",
111+
)
112+
113+
if resp.status_code == 200:
114+
self.clusters.append(
115+
Cluster(
116+
lng=lng,
117+
lat=lat,
118+
uuid=sub_uuid,
119+
version=resp.json()["subscription"]["version"],
120+
)
121+
)
122+
123+
# Move latitude approtimatly
124+
lat += (self.radius * 2) / 111111
125+
126+
def on_stop(self):
127+
for cluster in self.clusters:
128+
self.client.delete(
129+
f"/dss/v1/subscriptions/{cluster.uuid}/{cluster.version}"
130+
)
131+
132+
@locust.task
133+
def task_put_intent(self):
134+
135+
cluster = random.choice(self.clusters)
136+
137+
entity_id = uuid.uuid4().hex
138+
139+
body = {
140+
"state": "Accepted",
141+
"uss_base_url": self.uss_base_url,
142+
"new_subscription": {
143+
"uss_base_url": self.uss_base_url,
144+
},
145+
"extents": create_random_flight_path_volume(
146+
cluster.lat, cluster.lng, self.radius, self.max_flight_distance
147+
),
148+
}
149+
self.client.put(
150+
f"/dss/v1/operational_intent_references/{entity_id}",
151+
json=body,
152+
name="/dss/v1/operational_intent_references/[id]",
153+
)

monitoring/loadtest/locust_files/SCD.py

Lines changed: 2 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,9 @@
1-
"""Loaded by default by the Locust testing framework."""
2-
31
import argparse
4-
import datetime
5-
import random
62
import uuid
73

84
import client
9-
import geo_utils
105
import locust
11-
import shapely
6+
from geo_utils import create_random_flight_path_volume
127

138

149
@locust.events.init_command_line_parser.add_listener
@@ -47,64 +42,6 @@ def init_parser(parser: argparse.ArgumentParser):
4742
)
4843

4944

50-
def _format_time(time: datetime.datetime) -> str:
51-
return time.astimezone(datetime.UTC).strftime("%Y-%m-%dT%H:%M:%SZ")
52-
53-
54-
def _create_volume(
55-
polygon: shapely.Polygon,
56-
altitude_lower: float,
57-
altitude_upper: float,
58-
time_start: datetime.datetime,
59-
time_end: datetime.datetime,
60-
):
61-
return {
62-
"volume": {
63-
"outline_polygon": {
64-
"vertices": [
65-
{"lat": v[1], "lng": v[0]} for v in polygon.exterior.coords[:-1]
66-
]
67-
},
68-
"altitude_lower": {
69-
"value": altitude_lower,
70-
"reference": "W84",
71-
"units": "M",
72-
},
73-
"altitude_upper": {
74-
"value": altitude_upper,
75-
"reference": "W84",
76-
"units": "M",
77-
},
78-
},
79-
"time_start": {
80-
"value": _format_time(time_start),
81-
"format": "RFC3339",
82-
},
83-
"time_end": {
84-
"value": _format_time(time_end),
85-
"format": "RFC3339",
86-
},
87-
}
88-
89-
90-
def _create_random_flight_path(
91-
lat: float, lng: float, radius: int, max_flight_distance_meters: int
92-
):
93-
altitude_lower = random.randint(0, 10000)
94-
altitude_upper = altitude_lower + 1
95-
96-
start_time = datetime.datetime.now()
97-
end_time = start_time + datetime.timedelta(seconds=10)
98-
99-
rects = geo_utils.create_random_flight_path(
100-
lat, lng, radius, max_flight_distance_meters
101-
)
102-
return [
103-
_create_volume(r, altitude_lower, altitude_upper, start_time, end_time)
104-
for r in rects.geoms
105-
]
106-
107-
10845
class SCD(client.USS):
10946
wait_time = locust.between(0.01, 0.1)
11047

@@ -125,7 +62,7 @@ def task_put_intent(self):
12562
"new_subscription": {
12663
"uss_base_url": self.uss_base_url,
12764
},
128-
"extents": _create_random_flight_path(
65+
"extents": create_random_flight_path_volume(
12966
self.lat, self.lng, self.radius, self.max_flight_distance
13067
),
13168
}

monitoring/loadtest/locust_files/geo_utils.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
import datetime
12
import math
23
import random
34

45
import shapely
6+
from utils import format_time
57

68

79
def _create_rectangle(
@@ -95,3 +97,55 @@ def create_random_flight_path(
9597
rect_width,
9698
rect_height,
9799
)
100+
101+
102+
def create_random_flight_path_volume(
103+
lat: float, lng: float, radius: int, max_flight_distance_meters: int
104+
):
105+
altitude_lower = random.randint(0, 10000)
106+
altitude_upper = altitude_lower + 1
107+
108+
start_time = datetime.datetime.now()
109+
end_time = start_time + datetime.timedelta(seconds=10)
110+
111+
rects = create_random_flight_path(lat, lng, radius, max_flight_distance_meters)
112+
return [
113+
create_volume(r, altitude_lower, altitude_upper, start_time, end_time)
114+
for r in rects.geoms
115+
]
116+
117+
118+
def create_volume(
119+
polygon: shapely.Polygon,
120+
altitude_lower: float,
121+
altitude_upper: float,
122+
time_start: datetime.datetime,
123+
time_end: datetime.datetime,
124+
):
125+
return {
126+
"volume": {
127+
"outline_polygon": {
128+
"vertices": [
129+
{"lat": v[1], "lng": v[0]} for v in polygon.exterior.coords[:-1]
130+
]
131+
},
132+
"altitude_lower": {
133+
"value": altitude_lower,
134+
"reference": "W84",
135+
"units": "M",
136+
},
137+
"altitude_upper": {
138+
"value": altitude_upper,
139+
"reference": "W84",
140+
"units": "M",
141+
},
142+
},
143+
"time_start": {
144+
"value": format_time(time_start),
145+
"format": "RFC3339",
146+
},
147+
"time_end": {
148+
"value": format_time(time_end),
149+
"format": "RFC3339",
150+
},
151+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import datetime
2+
3+
from monitoring.monitorlib import rid_v1
4+
5+
6+
def format_time(time: datetime.datetime) -> str:
7+
return time.astimezone(datetime.UTC).strftime(rid_v1.DATE_FORMAT)

0 commit comments

Comments
 (0)