|
25 | 25 | HostServiceError, |
26 | 26 | ) |
27 | 27 | from ..utils.json import read_json_file |
| 28 | +from ..utils.systemd_journal import journal_boots_reader |
28 | 29 | from .const import PARAM_BOOT_ID, PARAM_SYSLOG_IDENTIFIER, LogFormat |
29 | 30 |
|
30 | 31 | _LOGGER: logging.Logger = logging.getLogger(__name__) |
@@ -108,12 +109,8 @@ async def get_boot_id(self, offset: int = 0) -> str: |
108 | 109 |
|
109 | 110 | return boot_ids[offset] |
110 | 111 |
|
111 | | - async def get_boot_ids(self) -> list[str]: |
112 | | - """Get boot IDs from oldest to newest.""" |
113 | | - if self._boot_ids: |
114 | | - # Doesn't change without a reboot, no reason to query again once cached |
115 | | - return self._boot_ids |
116 | | - |
| 112 | + async def _get_boot_ids_legacy(self) -> list[str]: |
| 113 | + """Get boots IDs using suboptimal method where /boots is not available.""" |
117 | 114 | try: |
118 | 115 | async with self.journald_logs( |
119 | 116 | params=BOOT_IDS_QUERY, |
@@ -142,13 +139,51 @@ async def get_boot_ids(self) -> list[str]: |
142 | 139 | _LOGGER.error, |
143 | 140 | ) from err |
144 | 141 |
|
145 | | - self._boot_ids = [] |
| 142 | + _boot_ids = [] |
146 | 143 | for entry in text.split("\n"): |
147 | | - if ( |
148 | | - entry |
149 | | - and (boot_id := json.loads(entry)[PARAM_BOOT_ID]) not in self._boot_ids |
150 | | - ): |
151 | | - self._boot_ids.append(boot_id) |
| 144 | + if entry and (boot_id := json.loads(entry)[PARAM_BOOT_ID]) not in _boot_ids: |
| 145 | + _boot_ids.append(boot_id) |
| 146 | + |
| 147 | + return _boot_ids |
| 148 | + |
| 149 | + async def _get_boot_ids_native(self): |
| 150 | + """Get boot IDs using /boots endpoint.""" |
| 151 | + try: |
| 152 | + async with self.journald_logs( |
| 153 | + path="/boots", |
| 154 | + accept=LogFormat.JSON_SEQ, |
| 155 | + timeout=ClientTimeout(total=20), |
| 156 | + ) as resp: |
| 157 | + if resp.status != 200: |
| 158 | + raise HostLogError( |
| 159 | + f"Got HTTP {resp.status} from /boots.", |
| 160 | + _LOGGER.debug, |
| 161 | + ) |
| 162 | + # Don't rely solely on the order of boots in the response, |
| 163 | + # sort the boots by index returned in the response. |
| 164 | + boot_id_tuples = [boot async for boot in journal_boots_reader(resp)] |
| 165 | + return [ |
| 166 | + boot_id for _, boot_id in sorted(boot_id_tuples, key=lambda x: x[0]) |
| 167 | + ] |
| 168 | + except (ClientError, TimeoutError) as err: |
| 169 | + raise HostLogError( |
| 170 | + "Could not get a list of boot IDs from systemd-journal-gatewayd", |
| 171 | + _LOGGER.error, |
| 172 | + ) from err |
| 173 | + |
| 174 | + async def get_boot_ids(self) -> list[str]: |
| 175 | + """Get boot IDs from oldest to newest.""" |
| 176 | + if self._boot_ids: |
| 177 | + # Doesn't change without a reboot, no reason to query again once cached |
| 178 | + return self._boot_ids |
| 179 | + |
| 180 | + try: |
| 181 | + self._boot_ids = await self._get_boot_ids_native() |
| 182 | + except HostLogError: |
| 183 | + _LOGGER.info( |
| 184 | + "Could not get /boots from systemd-journal-gatewayd, using fallback." |
| 185 | + ) |
| 186 | + self._boot_ids = await self._get_boot_ids_legacy() |
152 | 187 |
|
153 | 188 | return self._boot_ids |
154 | 189 |
|
|
0 commit comments