|
4 | 4 | import warnings
|
5 | 5 | from distutils.version import LooseVersion
|
6 | 6 | from typing import Dict, Optional, Tuple, Union
|
| 7 | +from urllib.parse import urlencode |
7 | 8 |
|
8 | 9 | from requests import ReadTimeout
|
9 | 10 |
|
@@ -306,7 +307,7 @@ def series(self, datasource_id, match, start, end, access="proxy"):
|
306 | 307 | r = self.client.POST(post_series_path, data={"match[]": match, "start": start, "end": end})
|
307 | 308 | return r
|
308 | 309 |
|
309 |
| - def smartquery(self, datasource: Union[DatasourceIdentifier, Dict], expression: str, store: Optional[str] = None): |
| 310 | + def smartquery_old(self, datasource: Union[DatasourceIdentifier, Dict], expression: str, store: Optional[str] = None): |
310 | 311 | """
|
311 | 312 | Send a query to the designated data source and return its response.
|
312 | 313 |
|
@@ -378,6 +379,85 @@ def smartquery(self, datasource: Union[DatasourceIdentifier, Dict], expression:
|
378 | 379 | f"Reason: {ex}. Response: {ex.response or '<empty>'}"
|
379 | 380 | )
|
380 | 381 | raise
|
| 382 | +#********************************************************************************** |
| 383 | + def smartquery(self, datasource: Union[DatasourceIdentifier, Dict], expression: str, attrs: Optional[dict] = None, request: Optional[dict] = None): |
| 384 | + """ |
| 385 | + Send a query to the designated data source and return its response. |
| 386 | +
|
| 387 | + TODO: This is by far not complete. The `query_factory` function has to |
| 388 | + be made more elaborate in order to query different data source |
| 389 | + types. |
| 390 | + """ |
| 391 | + |
| 392 | + if isinstance(datasource, DatasourceIdentifier): |
| 393 | + datasource = self.get(datasource) |
| 394 | + |
| 395 | + datasource_id = datasource["id"] |
| 396 | + datasource_type = datasource["type"] |
| 397 | + datasource_dialect = datasource.get("jsonData", {}).get("version", "InfluxQL") |
| 398 | + access_type = datasource["access"] |
| 399 | + |
| 400 | + # Sanity checks. |
| 401 | + if not request and not expression: |
| 402 | + raise ValueError("request or expression must be given") |
| 403 | + elif not request: |
| 404 | + model = { |
| 405 | + "refId": "test", |
| 406 | + } |
| 407 | + if expression is not None and 'query' not in attrs: |
| 408 | + model['query'] = expression |
| 409 | + model.update(attrs) |
| 410 | + request = query_factory(datasource, model) |
| 411 | + |
| 412 | + # Compute request method, body, and endpoint. |
| 413 | + if "method" in request and isinstance(request["method"], str): |
| 414 | + if request['method'] == 'POST': |
| 415 | + send_request = self.client.POST |
| 416 | + else: |
| 417 | + send_request = self.client.GET |
| 418 | + |
| 419 | + logger.info(f"Submitting request: {request}") |
| 420 | + |
| 421 | + # Certain data sources like InfluxDB 1.x, still use the `/datasources/proxy` route. |
| 422 | + if datasource_type == "influxdb" and datasource_dialect == "InfluxQL": |
| 423 | + url = f"/datasources/proxy/{datasource_id}/query" |
| 424 | + url += '?' + urlencode(request['params']) |
| 425 | + request_kwargs = {"data": request["data"]} |
| 426 | + |
| 427 | + elif datasource_type == "graphite": |
| 428 | + url = f"/datasources/proxy/{datasource_id}/render" |
| 429 | + request_kwargs = {"data": request["data"]} |
| 430 | + |
| 431 | + # This case is very special. It is used for Elasticsearch and Testdata. |
| 432 | + # elif expression.startswith("url://"): |
| 433 | + # url = expression.replace("url://", "") |
| 434 | + # url = url.format( |
| 435 | + # datasource_id=datasource.get("id"), |
| 436 | + # datasource_uid=datasource.get("uid"), |
| 437 | + # database_name=datasource.get("database"), |
| 438 | + # ) |
| 439 | + # request_kwargs = {} |
| 440 | + # send_request = self.client.GET |
| 441 | + |
| 442 | + # For all others, use the generic data source communication endpoint. |
| 443 | + elif access_type in ["server", "proxy"]: |
| 444 | + url = "/ds/query" |
| 445 | + request_kwargs = {"json": request["data"]} |
| 446 | + |
| 447 | + else: |
| 448 | + raise NotImplementedError(f"Unable to submit query to data source with access type '{access_type}'") |
| 449 | + |
| 450 | + # Submit query. |
| 451 | + try: |
| 452 | + r = send_request(url, **request_kwargs) |
| 453 | + # logger.debug(f"Response from generic data source query: {r}") |
| 454 | + return r |
| 455 | + except (GrafanaClientError, GrafanaServerError) as ex: |
| 456 | + logger.error( |
| 457 | + f"Querying data source failed. id={datasource_id}, type={datasource_type}. " |
| 458 | + f"Reason: {ex}. Response: {ex.response or '<empty>'}" |
| 459 | + ) |
| 460 | + raise |
381 | 461 |
|
382 | 462 | def health_check(self, datasource: Union[DatasourceIdentifier, Dict]) -> DatasourceHealthResponse:
|
383 | 463 | """
|
|
0 commit comments