async def run(self): """ Run the client in full-auto mode. """ # if not hasattr(self, 'on_event'): # raise NotImplementedError("You must implement on_event.") self.loop = asyncio.get_event_loop() await self.create_party_registration(ven_id=self.ven_id) if not self.ven_id: logger.error("No VEN ID received from the VTN, aborting.") await self.stop() return if self.reports: await self.register_reports(self.reports) self.report_queue_task = self.loop.create_task( self._report_queue_worker()) await self._poll() # Set up automatic polling if self.poll_frequency > timedelta(hours=24): logger.warning( "Polling with intervals of more than 24 hours is not supported. " "Will use 24 hours as the polling interval.") self.poll_frequency = timedelta(hours=24) cron_config = utils.cron_config(self.poll_frequency, randomize_seconds=self.allow_jitter) self.scheduler.add_job(self._poll, trigger='cron', **cron_config) self.scheduler.add_job(self._event_cleanup, trigger='interval', seconds=300) self.scheduler.start()
def _add(self, dataset): scheduler_id = dataset['id'] date_start_on = dataset['start_datetime'] date_start_off = dataset['end_datetime'] duration = dataset['report_back_duration'] scheduler_type = 'cron' config = utils.cron_config(utils.parse_duration(duration)) args_on = dict(config, start_date=date_start_on, end_date=date_start_off) cb = partial(self.callback, report_request_id=dataset['report_request_id']) self.scheduler.add_job(cb, scheduler_type, id='%son' % scheduler_id, **args_on) logger.info('add job' + str(args_on))
def test_cron_config(): assert utils.cron_config(timedelta(seconds=5)) == { 'second': '*/5', 'minute': '*', 'hour': '*' } assert utils.cron_config(timedelta(minutes=1)) == { 'second': '0', 'minute': '*/1', 'hour': '*' } assert utils.cron_config(timedelta(minutes=5)) == { 'second': '0', 'minute': '*/5', 'hour': '*' } assert utils.cron_config(timedelta(hours=1)) == { 'second': '0', 'minute': '0', 'hour': '*/1' } assert utils.cron_config(timedelta(hours=2)) == { 'second': '0', 'minute': '0', 'hour': '*/2' } assert utils.cron_config(timedelta(hours=25)) == { 'second': '0', 'minute': '0', 'hour': '0' } assert utils.cron_config(timedelta(seconds=10), randomize_seconds=True) == { 'second': '*/10', 'minute': '*', 'hour': '*', 'jitter': 1 }
async def create_report(self, report_request): """ Add the requested reports to the reporting mechanism. This is called when the VTN requests reports from us. :param report_request dict: The oadrReportRequest dict from the VTN. """ # Get the relevant variables from the report requests report_request_id = report_request['report_request_id'] report_specifier_id = report_request['report_specifier']['report_specifier_id'] report_back_duration = report_request['report_specifier'].get('report_back_duration') granularity = report_request['report_specifier']['granularity'] # Check if this report actually exists report = find_by(self.reports, 'report_specifier_id', report_specifier_id) if not report: logger.error(f"A non-existant report with report_specifier_id " f"{report_specifier_id} was requested.") return False # Check and collect the requested r_ids for this report requested_r_ids = [] for specifier_payload in report_request['report_specifier']['specifier_payloads']: r_id = specifier_payload['r_id'] # Check if the requested r_id actually exists rd = find_by(report.report_descriptions, 'r_id', r_id) if not rd: logger.error(f"A non-existant report with r_id {r_id} " f"inside report with report_specifier_id {report_specifier_id} " f"was requested.") continue # Check if the requested measurement exists and if the correct unit is requested if 'measurement' in specifier_payload: measurement = specifier_payload['measurement'] if measurement['item_description'] != rd.measurement.item_description: logger.error(f"A non-matching measurement description for report with " f"report_request_id {report_request_id} and r_id {r_id} was given " f"by the VTN. Offered: {rd.measurement.item_description}, " f"requested: {measurement['item_description']}") continue if measurement['item_units'] != rd.measurement.item_units: logger.error(f"A non-matching measurement unit for report with " f"report_request_id {report_request_id} and r_id {r_id} was given " f"by the VTN. Offered: {rd.measurement.item_units}, " f"requested: {measurement['item_units']}") continue if granularity is not None: if not rd.sampling_rate.min_period <= granularity <= rd.sampling_rate.max_period: logger.error(f"An invalid sampling rate {granularity} was requested for report " f"with report_specifier_id {report_specifier_id} and r_id {r_id}. " f"The offered sampling rate was between " f"{rd.sampling_rate.min_period} and " f"{rd.sampling_rate.max_period}") continue else: # If no granularity is specified, set it to the lowest sampling rate. granularity = rd.sampling_rate.max_period requested_r_ids.append(r_id) callback = partial(self.update_report, report_request_id=report_request_id) reporting_interval = report_back_duration or granularity job = self.scheduler.add_job(func=callback, trigger='cron', **cron_config(reporting_interval)) self.report_requests.append({'report_request_id': report_request_id, 'report_specifier_id': report_specifier_id, 'report_back_duration': report_back_duration, 'r_ids': requested_r_ids, 'granularity': granularity, 'job': job})