Skip to content

Commit

Permalink
[uss_qualifier/resources/f3548/dss/find_op_intent] Use QueryError for…
Browse files Browse the repository at this point in the history
… error handling (#496)
  • Loading branch information
mickmis authored Feb 9, 2024
1 parent 5657214 commit 87a6194
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 73 deletions.
16 changes: 11 additions & 5 deletions monitoring/uss_qualifier/resources/astm/f3548/v21/dss.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ def with_different_auth(
def find_op_intent(
self, extent: Volume4D
) -> Tuple[List[OperationalIntentReference], Query]:
"""
Find operational intents overlapping with a given volume 4D.
Raises:
* QueryError: if request failed, if HTTP status code is different than 200, or if the parsing of the response failed.
"""
self._uses_scope(Scope.StrategicCoordination)
op = OPERATIONS[OperationID.QueryOperationalIntentReferences]
req = QueryOperationalIntentReferenceParameters(area_of_interest=extent)
Expand All @@ -131,12 +136,13 @@ def find_op_intent(
json=req,
)
if query.status_code != 200:
result = None
raise QueryError(
f"Received code {query.status_code} when attempting to find operational intents in {extent}",
query,
)
else:
result = ImplicitDict.parse(
query.response.json, QueryOperationalIntentReferenceResponse
).operational_intent_references
return result, query
result = query.parse_json_result(QueryOperationalIntentReferenceResponse)
return result.operational_intent_references, query

def get_op_intent_reference(
self,
Expand Down
32 changes: 18 additions & 14 deletions monitoring/uss_qualifier/scenarios/astm/utm/dss_interoperability.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from typing import List
from urllib.parse import urlparse

from monitoring.monitorlib.fetch import QueryError
from monitoring.uss_qualifier.resources.astm.f3548.v21 import PlanningAreaResource
from monitoring.uss_qualifier.suites.suite import ExecutionContext
from uas_standards.astm.f3548.v21.api import Volume4D, Volume3D, Polygon, LatLngPoint
Expand Down Expand Up @@ -75,18 +76,21 @@ def _test_env_reqs(self):
details=f"DSS (URL: {dss.base_url}, netloc: {parsed_url.netloc}, resolved IP: {ip_addr}) is not publicly addressable",
)

# dummy search query
_, q = dss.find_op_intent(extent=self._valid_search_area)
self.record_query(q)

with self.check("DSS instance is reachable", [dss.participant_id]) as check:
# status code 999 means we could not even get a valid HTTP reply,
# either from a time-out or an un-routable address, implying the DSS is likely unavailable.
# if the code is anything else than 999, we got an actual HTTP reply, and as far as this
# scenario is concerned, the DSS is available.
if q.status_code == 999:
check.record_failed(
summary=f"Could not reach DSS instance",
details=f"{q.response.get('content', '')}",
query_timestamps=[q.request.timestamp],
)
try:
# dummy search query
_, q = dss.find_op_intent(extent=self._valid_search_area)
self.record_query(q)
except QueryError as e:
self.record_queries(e.queries)
q = e.queries[0]
# status code 999 means we could not even get a valid HTTP reply,
# either from a time-out or an un-routable address, implying the DSS is likely unavailable.
# if the code is anything else than 999, we got an actual HTTP reply, and as far as this
# scenario is concerned, the DSS is available.
if q.status_code == 999:
check.record_failed(
summary=f"Could not reach DSS instance",
details=f"{q.response.get('content', '')}; {e}",
query_timestamps=[q.request.timestamp],
)
Original file line number Diff line number Diff line change
Expand Up @@ -135,12 +135,16 @@ def run(self, context: ExecutionContext):
def _setup(self):

self.begin_test_step("Resolve USS ID of virtual USS")
_, dummy_query = self.dss.find_op_intent(self._intents_extent)
with self.check("Successful dummy query", [self.dss.participant_id]) as check:
if dummy_query.status_code != 200:
try:
_, dummy_query = self.dss.find_op_intent(self._intents_extent)
self.record_query(dummy_query)
except QueryError as e:
self.record_queries(e.queries)
dummy_query = e.queries[0]
check.record_failed(
summary="Failed to query DSS",
details=f"DSS responded code {dummy_query.status_code}; error message: {dummy_query.error_message}",
details=f"DSS responded code {dummy_query.status_code}; error message: {dummy_query.error_message}; {e}",
query_timestamps=[dummy_query.request.timestamp],
)
self.uss_qualifier_sub = self.dss.client.auth_adapter.get_sub()
Expand Down Expand Up @@ -272,16 +276,19 @@ def _plan_flight_conflict_planned(self):
self.end_test_step()

def _clear_op_intents(self):
oi_refs, find_query = self.dss.find_op_intent(self._intents_extent)
self.record_query(find_query)

with self.check(
"Successful operational intents cleanup", [self.dss.participant_id]
) as check:
if find_query.status_code != 200:
try:
oi_refs, find_query = self.dss.find_op_intent(self._intents_extent)
self.record_query(find_query)
except QueryError as e:
self.record_queries(e.queries)
find_query = e.queries[0]
check.record_failed(
summary=f"Failed to query operational intent references from DSS in {self._intents_extent} for cleanup",
details=f"DSS responded code {find_query.status_code}; error message: {find_query.error_message}",
details=f"DSS responded code {find_query.status_code}; error message: {find_query.error_message}; {e}",
query_timestamps=[find_query.request.timestamp],
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,17 +198,21 @@ def _clean_known_op_intents_ids(self):

def _attempt_to_delete_remaining_op_intents(self):
"""Search for op intents and attempt to delete them using the main credentials"""
# Also check for any potential other op_intents and delete them
(op_intents_1, q) = self._dss.find_op_intent(self._intents_extent)
self.record_query(q)

with self.check(
"Operational intent references can be searched for",
self._pid,
) as check:
if q.response.status_code != 200:
try:
# Also check for any potential other op_intents and delete them
(op_intents_1, q) = self._dss.find_op_intent(self._intents_extent)
self.record_query(q)
except QueryError as e:
self.record_queries(e.queries)
q = e.queries[0]
check.record_failed(
f"Could not search operational intent references using main credentials",
details=f"DSS responded with {q.response.status_code} to attempt to search OIs",
details=f"DSS responded with {q.response.status_code} to attempt to search OIs; {e}",
query_timestamps=[q.request.timestamp],
)

Expand All @@ -232,18 +236,21 @@ def _attempt_to_delete_remaining_op_intents(self):
query_timestamps=[dq.request.timestamp],
)

(op_intents_2, q) = self._dss_separate_creds.find_op_intent(
self._intents_extent
)
self.record_query(q)
with self.check(
"Operational intent references can be searched for",
self._pid,
) as check:
if q.response.status_code != 200:
try:
(op_intents_2, q) = self._dss_separate_creds.find_op_intent(
self._intents_extent
)
self.record_query(q)
except QueryError as e:
self.record_queries(e.queries)
q = e.queries[0]
check.record_failed(
f"Could not search operational intent references using second credentials",
details=f"DSS responded with {q.response.status_code} to attempt to search OIs",
details=f"DSS responded with {q.response.status_code} to attempt to search OIs; {e}",
query_timestamps=[q.request.timestamp],
)

Expand Down Expand Up @@ -288,17 +295,20 @@ def _ensure_clean_workspace(self) -> bool:
# Search and attempt deleting what may be found through search
self._attempt_to_delete_remaining_op_intents()

# We can't delete anything that would be left.
(stray_oir, q) = self._dss.find_op_intent(self._intents_extent)
self.record_query(q)
with self.check(
"Operational intent references can be searched for",
self._pid,
) as check:
if q.response.status_code != 200:
try:
# We can't delete anything that would be left.
(stray_oir, q) = self._dss.find_op_intent(self._intents_extent)
self.record_query(q)
except QueryError as e:
self.record_queries(e.queries)
q = e.queries[0]
check.record_failed(
f"Could not search operational intent references using main credentials",
details=f"DSS responded with {q.response.status_code} to attempt to search OIs",
details=f"DSS responded with {q.response.status_code} to attempt to search OIs; {e}",
query_timestamps=[q.request.timestamp],
)

Expand Down
14 changes: 6 additions & 8 deletions monitoring/uss_qualifier/scenarios/astm/utm/prep_planners.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import Optional, List

from monitoring.monitorlib.fetch import QueryError
from monitoring.uss_qualifier.resources.astm.f3548.v21 import DSSInstanceResource
from monitoring.uss_qualifier.resources.astm.f3548.v21.dss import DSSInstance
from monitoring.uss_qualifier.resources.flight_planning import (
Expand Down Expand Up @@ -92,16 +93,13 @@ def _validate_clear_area(
with self.check("DSS responses", [self.dss.participant_id]) as check:
try:
op_intents, query = self.dss.find_op_intent(area.to_f3548v21())
except ValueError as e:
check.record_failed(
summary="Error parsing DSS response",
details=str(e),
)
self.record_query(query)
if op_intents is None:
self.record_query(query)
except QueryError as e:
self.record_queries(e.queries)
query = e.queries[0]
check.record_failed(
summary="Error querying DSS for operational intents",
details="See query",
details=f"See query; {e}",
query_timestamps=[query.request.timestamp],
)
found_intents.extend(op_intents)
Expand Down
53 changes: 30 additions & 23 deletions monitoring/uss_qualifier/scenarios/astm/utm/test_steps.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
UasState,
AirspaceUsageState,
)
from monitoring.monitorlib.fetch import QueryError
from monitoring.uss_qualifier.common_data_definitions import Severity
from monitoring.uss_qualifier.resources.astm.f3548.v21.dss import DSSInstance
from monitoring.uss_qualifier.resources.flight_planning.flight_planner import (
Expand Down Expand Up @@ -71,9 +72,20 @@ def __init__(
self._orig_oi_ref: Optional[OperationalIntentReference] = orig_oi_ref

def __enter__(self) -> OpIntentValidator:
self._before_oi_refs, self._before_query = self._dss.find_op_intent(
self._extent
)
with self._scenario.check("DSS responses", [self._dss.participant_id]) as check:
try:
self._before_oi_refs, self._before_query = self._dss.find_op_intent(
self._extent
)
self._scenario.record_query(self._before_query)
except QueryError as e:
self._scenario.record_queries(e.queries)
self._before_query = e.queries[0]
check.record_failed(
summary="Failed to query DSS for operational intent references before planning request",
details=f"Received status code {self._before_query.status_code} from the DSS; {e}",
query_timestamps=[self._before_query.request.timestamp],
)
return self

def __exit__(self, exc_type, exc_val, exc_tb):
Expand All @@ -89,7 +101,21 @@ def _find_after_oi(self, oi_id: str) -> Optional[OperationalIntentReference]:
return found[0] if len(found) != 0 else None

def _begin_step_fragment(self):
self._after_oi_refs, self._after_query = self._dss.find_op_intent(self._extent)
with self._scenario.check("DSS responses", [self._dss.participant_id]) as check:
try:
self._after_oi_refs, self._after_query = self._dss.find_op_intent(
self._extent
)
self._scenario.record_query(self._after_query)
except QueryError as e:
self._scenario.record_queries(e.queries)
self._after_query = e.queries[0]
check.record_failed(
summary="Failed to query DSS for operational intent references after planning request",
details=f"Received status code {self._after_query.status_code} from the DSS; {e}",
query_timestamps=[self._after_query.request.timestamp],
)

oi_ids_delta = {oi_ref.id for oi_ref in self._after_oi_refs} - {
oi_ref.id for oi_ref in self._before_oi_refs
}
Expand All @@ -103,25 +129,6 @@ def _begin_step_fragment(self):
if len(oi_ids_delta) == 1:
self._new_oi_ref = self._find_after_oi(oi_ids_delta.pop())

self._scenario.record_query(self._before_query)
self._scenario.record_query(self._after_query)

with self._scenario.check("DSS responses", [self._dss.participant_id]) as check:
if self._before_query.status_code != 200:
check.record_failed(
summary="Failed to query DSS for operational intent references before planning request",
severity=Severity.High,
details=f"Received status code {self._before_query.status_code} from the DSS",
query_timestamps=[self._before_query.request.timestamp],
)
if self._after_query.status_code != 200:
check.record_failed(
summary="Failed to query DSS for operational intent references after planning request",
severity=Severity.High,
details=f"Received status code {self._after_query.status_code} from the DSS",
query_timestamps=[self._after_query.request.timestamp],
)

def expect_removed(self, oi_id: EntityID) -> None:
"""Validate that a specific operational intent reference was removed from the DSS.
Expand Down

0 comments on commit 87a6194

Please sign in to comment.