Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 20 additions & 10 deletions atlasapi/atlas.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

Core module which provides access to MongoDB Atlas Cloud Provider APIs
"""
from atlasapi.network import Network
from atlasapi.network import Network, atlas_encode_params
from atlasapi.errors import *
from pprint import pprint
from json import loads
Expand Down Expand Up @@ -605,22 +605,27 @@ def host_list_secondaries(self) -> Iterable[Host]:

def get_measurement_for_hosts(self, granularity: Optional[AtlasGranularities] = None,
period: Optional[AtlasPeriods] = None,
measurement: Optional[AtlasMeasurementTypes] = None, return_data: bool = False):
measurement: Optional[AtlasMeasurementTypes] = None, return_data: bool = False,
start: Optional[datetime] = None, end: Optional[datetime] = None):
"""Get measurement(s) for all hosts in the host_list


Populates all hosts in the host_list with the requested metric.

Multiple calls will append additional metrics to the same host object.

You must provide both `start` and `end` or neither. You cannot provide `period`
with `start` and `end`.

Please note that using the `return_data` param will also return the updated
host objects, which may unnecessarily consume memory.

Keyword Args:
granularity (AtlasGranularities): the desired granularity
period (AtlasPeriods): The desired period
measurement (AtlasMeasurementTypes) : The desired measurement or Measurement class

start (datetime): The start of the desired period
end (datetime): The end of the desired period


:param return_data:
Expand All @@ -643,6 +648,7 @@ def get_measurement_for_hosts(self, granularity: Optional[AtlasGranularities] =
logger.debug(f'The data type of measurement is is {type(measurement)}')
returned_data = self._get_measurement_for_host(each_host, granularity=granularity,
period=period,
start=start, end=end,
measurement=measurement)
each_host.measurements = list(returned_data)
except Exception as e:
Expand Down Expand Up @@ -790,7 +796,9 @@ def get_logs_for_cluster(self,
def _get_measurement_for_host(self, host_obj: Host,
granularity: Optional[AtlasGranularities] = None,
period: Optional[AtlasPeriods] = None,
measurement: Optional[AtlasMeasurementTypes] = None
measurement: Optional[AtlasMeasurementTypes] = None,
start: Optional[datetime] = None,
end: Optional[datetime] = None
) -> Iterable[AtlasMeasurement]:
"""Get measurement(s) for a host

Expand All @@ -814,6 +822,8 @@ def _get_measurement_for_host(self, host_obj: Host,
pageNum (int): Page number
itemsPerPage (int): Number of Users per Page
iterable (bool): To return an iterable high level object instead of a low level API response
start (datetime): Start of the desired period
end (datetime): End of the desired period

Returns:
Iterable[AtlasMeasurement] or dict: Iterable object representing this function OR Response payload
Expand All @@ -832,7 +842,7 @@ def _get_measurement_for_host(self, host_obj: Host,
# Set default measurement, period and granularity if none are sent
if measurement is None:
measurement = AtlasMeasurementTypes.Cache.dirty
if period is None:
if period is None and start is None and end is None:
period = AtlasPeriods.WEEKS_1
if granularity is None:
granularity = AtlasGranularities.HOUR
Expand Down Expand Up @@ -864,11 +874,11 @@ def _get_measurement_for_host(self, host_obj: Host,
uri = Settings.api_resources["Monitoring and Logs"]["Get measurement for host"].format(
group_id=self.atlas.group,
host=host_obj.hostname,
port=host_obj.port,
granularity=granularity,
period=period,
measurement=measurement
)
port=host_obj.port
) + "?" + atlas_encode_params({
"granularity": granularity, "m": measurement,
"period": period, "start": start, "end": end})

logger.debug(f'The URI used will be {uri}')
# Build the request
return_val = self.atlas.network.get(Settings.BASE_URL + uri)
Expand Down
16 changes: 16 additions & 0 deletions atlasapi/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@
Module which handles the basic network operations with the Atlas API>
"""

import datetime
from math import ceil
import requests
from requests.auth import HTTPDigestAuth, HTTPBasicAuth
from atlasapi.settings import Settings
from atlasapi.errors import *
import logging
import urllib.parse
from dateutil.tz import UTC
from json import dumps
from io import BytesIO
from typing import Union
Expand All @@ -36,6 +39,19 @@ def merge(dict1, dict2):
return dict2.update(dict1)


def atlas_encode_dt(dt: datetime.datetime) -> str:
"""Encode a datetime (must already be UTC!) for the Atlas API."""
# Atlas requires "Z" suffix, not Python's usual "+00:00" for UTC.
return dt.replace(tzinfo=None).isoformat() + 'Z'


def atlas_encode_params(params: dict) -> str:
# Use "safe" to permit ":" in ISO8601 datetimes.
return urllib.parse.urlencode(
{k: atlas_encode_dt(v) if isinstance(v, datetime.datetime) else v
for k, v in params.items() if v is not None}, safe=':')


class Network:
"""Network constructor

Expand Down
3 changes: 1 addition & 2 deletions atlasapi/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ class Settings:
"Get information for process in group": URI_STUB + "/groups/%s/processes/%s:&s?pageNum=%d"
"&itemsPerPage=%d",
"Get measurement for host": URI_STUB + "/groups/{group_id}/processes/{host}:{"
"port}/measurements?granularity={granularity}&period={period}"
"&m={measurement}",
"port}/measurements",
"Get list of databases for host": "/api/atlas/v1.0/groups/{GROUP-ID}/processes/{HOST}:{PORT}/databases",
"Get measurements of database for host.": "/api/atlas/v1.0/groups/{GROUP-ID}/processes/{HOST}:{"
"PORT}/databases/{DATABASE-NAME}/measurements",
Expand Down
12 changes: 12 additions & 0 deletions tests/test_monitoring.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,3 +371,15 @@ def test_26_issue_114_add_ten_second_granularity(self):

# This test requires a cluster of m40 or higher , so will not run this in the automated suite.
test_26_issue_114_add_ten_second_granularity.basic = False

def test_27_get_measurements_start_end(self):
self.a.Hosts.fill_host_list()
self.assertGreaterEqual(len(self.a.Hosts.host_list), 2)
end = datetime.utcnow()
start = end - timedelta(minutes=10)
self.a.Hosts.get_measurement_for_hosts(measurement=AtlasMeasurementTypes.connections,
start=start, end=end)

self.assertGreaterEqual(len(self.a.Hosts.host_list_with_measurements), 1)

test_27_get_measurements_start_end.basic = True