|
1 | 1 | import re |
| 2 | +from datetime import datetime |
2 | 3 |
|
3 | 4 | from kubernetes.dynamic.exceptions import ConflictError |
4 | 5 |
|
5 | | -from ocp_resources.utils.constants import TIMEOUT_4MINUTES |
| 6 | +from ocp_resources.utils.constants import TIMEOUT_1MINUTE, TIMEOUT_4MINUTES, TIMEOUT_5SEC |
6 | 7 | from ocp_resources.exceptions import NNCPConfigurationFailed |
7 | 8 | from ocp_resources.node import Node |
8 | 9 | from ocp_resources.node_network_configuration_enactment import ( |
9 | 10 | NodeNetworkConfigurationEnactment, |
10 | 11 | ) |
11 | 12 | from ocp_resources.node_network_state import NodeNetworkState |
12 | 13 | from ocp_resources.resource import Resource, ResourceEditor |
13 | | -from timeout_sampler import TimeoutExpiredError, TimeoutSampler, TimeoutWatch |
| 14 | +from timeout_sampler import TimeoutExpiredError, TimeoutSampler, TimeoutWatch, retry |
14 | 15 |
|
15 | 16 | IPV4_STR = "ipv4" |
16 | 17 | IPV6_STR = "ipv6" |
@@ -322,10 +323,43 @@ def _absent_interface(self): |
322 | 323 | if self.ports: |
323 | 324 | self.add_ports() |
324 | 325 |
|
| 326 | + # The current time-stamp of the NNCP's available status will change after the NNCP is updated, therefore |
| 327 | + # it must be fetched and stored before the update, and compared with the new time-stamp after. |
| 328 | + initial_success_status_time = self._get_last_successful_transition_time() |
325 | 329 | ResourceEditor( |
326 | 330 | patches={self: {"spec": {"desiredState": {"interfaces": self.desired_state["interfaces"]}}}} |
327 | 331 | ).update() |
328 | 332 |
|
| 333 | + # If the NNCP failed on setup, then its tear-down AVAIALBLE status will necessarily be the first. |
| 334 | + if initial_success_status_time: |
| 335 | + self._wait_for_nncp_status_update(initial_transition_time=initial_success_status_time) |
| 336 | + |
| 337 | + def _get_last_successful_transition_time(self) -> str | None: |
| 338 | + for condition in self.instance.status.conditions: |
| 339 | + if ( |
| 340 | + condition["type"] == self.Conditions.Type.AVAILABLE |
| 341 | + and condition["status"] == Resource.Condition.Status.TRUE |
| 342 | + and condition["reason"] == self.Conditions.Reason.SUCCESSFULLY_CONFIGURED |
| 343 | + ): |
| 344 | + return condition["lastTransitionTime"] |
| 345 | + return None |
| 346 | + |
| 347 | + @retry( |
| 348 | + wait_timeout=TIMEOUT_1MINUTE, |
| 349 | + sleep=TIMEOUT_5SEC, |
| 350 | + ) |
| 351 | + def _wait_for_nncp_status_update(self, initial_transition_time: str) -> bool: |
| 352 | + date_format = "%Y-%m-%dT%H:%M:%SZ" |
| 353 | + formatted_initial_transition_time = datetime.strptime(initial_transition_time, date_format) |
| 354 | + for condition in self.instance.get("status", {}).get("conditions", []): |
| 355 | + if ( |
| 356 | + condition["type"] == self.Conditions.Type.AVAILABLE |
| 357 | + and condition["status"] == Resource.Condition.Status.TRUE |
| 358 | + and datetime.strptime(condition["lastTransitionTime"], date_format) > formatted_initial_transition_time |
| 359 | + ): |
| 360 | + return True |
| 361 | + return False |
| 362 | + |
329 | 363 | @property |
330 | 364 | def status(self): |
331 | 365 | for condition in self.instance.status.conditions: |
|
0 commit comments