def _run_scheduled_calls(self): """ Run call requests that are currently scheduled to run NOTE: the scheduler no longer schedules arbitrary call request, instead it now only supports call request from the itineraries package """ coordinator = dispatch_factory.coordinator() for call_group in self._get_scheduled_call_groups(): # this is a bit of hack and presumes that the first call in the call # group is the most important, need to re-think this and implement # something more general (counter-based?) # but for right now, the presumption is correct call_group[0].add_life_cycle_callback(dispatch_constants.CALL_COMPLETE_LIFE_CYCLE_CALLBACK, scheduler_complete_callback) if len(call_group) == 1: call_report_list = [coordinator.execute_call_asynchronously(call_group[0])] else: call_report_list = coordinator.execute_multiple_calls(call_group) for call_request, call_report in zip(call_group, call_report_list): log_msg = _('Scheduled %(c)s: %(r)s [reasons: %(s)s]') % {'c': str(call_request), 'r': call_report.response, 's': pformat(call_report.reasons)} if call_report.response is dispatch_constants.CALL_REJECTED_RESPONSE: _LOG.error(log_msg) else: _LOG.info(log_msg)
def _run_scheduled_calls(self): """ Find call requests that are currently scheduled to run """ coordinator = dispatch_factory.coordinator() now = datetime.datetime.utcnow() query = {'next_run': {'$lte': now}} for scheduled_call in self.scheduled_call_collection.find(query): if not scheduled_call['enabled']: # update the next run information for disabled calls self.update_next_run(scheduled_call) continue serialized_call_request = scheduled_call['serialized_call_request'] call_request = call.CallRequest.deserialize(serialized_call_request) call_request.add_life_cycle_callback(dispatch_constants.CALL_COMPLETE_LIFE_CYCLE_CALLBACK, scheduler_complete_callback) call_report = call.CallReport.from_call_request(call_request, schedule_id=str(scheduled_call['_id'])) call_report = coordinator.execute_call_asynchronously(call_request, call_report) log_msg = _('Scheduled %(c)s: %(r)s [reasons: %(s)s]') % {'c': str(call_request), 'r': call_report.response, 's': pformat(call_report.reasons)} if call_report.response is dispatch_constants.CALL_REJECTED_RESPONSE: _LOG.error(log_msg) else: _LOG.info(log_msg)
def _get_call_report(): context = dispatch_factory.context() coordinator = dispatch_factory.coordinator() call_report_list = coordinator.find_call_reports(call_request_id=context.call_request_id) if not call_report_list: return None return call_report_list[0].serialize()
def failed(self, reply): log.info('Task RMI (failed)\n%s', reply) taskid = reply.any exception = reply.exval traceback = reply.xstate['trace'] coordinator = factory.coordinator() coordinator.complete_call_failure(taskid, exception, traceback)
def _get_scheduled_call_groups(self): """ Get call requests, by call group, that are currently scheduled to run """ coordinator = dispatch_factory.coordinator() now = datetime.datetime.utcnow() query = {'next_run': {'$lte': now}} for scheduled_call in self.scheduled_call_collection.find(query): if not scheduled_call['enabled']: # update the next run information for disabled calls self.update_next_run(scheduled_call) continue # get the itinerary call request and execute serialized_call_request = scheduled_call['serialized_call_request'] call_request = call.CallRequest.deserialize(serialized_call_request) call_report = coordinator.execute_call_synchronously(call_request) # call request group is the return of an itinerary function call_request_group = call_report.result map(lambda r: setattr(r, 'schedule_id', str(scheduled_call['_id'])), call_request_group) yield call_request_group
def _execute_itinerary(self, scheduled_call): """ Execute the scheduled itinerary call request to get the call requests that implement the scheduled call :param scheduled_call: the scheduled call :type scheduled_call: bson.BSON :return: call requests for the scheduled itinerary call :rtype: list of pulp.server.dispatch.call.CallRequest """ coordinator = dispatch_factory.coordinator() # scheduled calls are always itinerary calls itinerary_call_request = call.CallRequest.deserialize(scheduled_call['serialized_call_request']) itinerary_call_request.archive = False # don't keep a history of these calls itinerary_call_report = call.CallReport.from_call_request(itinerary_call_request) itinerary_call_report.serialize_result = False # don't try to serialize the result # use the coordinator to execute the itinerary call, it already has all # the machinery to handle the call request and report instances itinerary_call_report = coordinator.execute_call_synchronously(itinerary_call_request, itinerary_call_report) # the call request group is the result of the itinerary call call_request_group = itinerary_call_report.result self._set_call_group_scheduler_hooks(scheduled_call, call_request_group) return call_request_group
def _run_scheduled_calls(self): """ Run call requests that are currently scheduled to run NOTE: the scheduler no longer schedules arbitrary call request, instead it now only supports call request from the itineraries package """ coordinator = dispatch_factory.coordinator() for call_group in self._get_scheduled_call_groups(): for call_request in call_group: call_request.add_life_cycle_callback(dispatch_constants.CALL_COMPLETE_LIFE_CYCLE_CALLBACK, scheduler_complete_callback) if len(call_group) == 1: call_report_list = [coordinator.execute_call_asynchronously(call_group[0])] else: call_report_list = coordinator.execute_multiple_calls(call_group) for call_request, call_report in zip(call_group, call_report_list): log_msg = _('Scheduled %(c)s: %(r)s [reasons: %(s)s]') % {'c': str(call_request), 'r': call_report.response, 's': pformat(call_report.reasons)} if call_report.response is dispatch_constants.CALL_REJECTED_RESPONSE: _LOG.error(log_msg) else: _LOG.info(log_msg)
def DELETE(self, task_group_id): coordinator = dispatch_factory.coordinator() results = coordinator.cancel_multiple_calls(task_group_id) if not results: raise TaskGroupNotFound(task_group_id) if None in results.values(): raise TaskGroupCancelNotImplemented(task_group_id) return self.accepted(results)
def GET(self): valid_filters = ['tag'] filters = self.filters(valid_filters) criteria = {'tags': filters.get('tag', [])} coordinator = dispatch_factory.coordinator() call_reports = coordinator.find_call_reports(**criteria) serialized_call_reports = [c.serialize() for c in call_reports] return self.ok(serialized_call_reports)
def _get_call_report(): context = dispatch_factory.context() coordinator = dispatch_factory.coordinator() call_report_list = coordinator.find_call_reports( call_request_id=context.call_request_id) if not call_report_list: return None return call_report_list[0].serialize()
def _current_task_state(): context = dispatch_factory.context() if context.call_request_id is None: return None coordinator = dispatch_factory.coordinator() tasks = coordinator._find_tasks(call_request_id=context.call_request_id) if not tasks: return None return tasks[0].state
def execute_multiple(call_request_list): """ Execute multiple calls as a task group via the coordinator. @param call_request_list: list of call request instances @type call_request_list: list """ coordinator = dispatch_factory.coordinator() call_report_list = coordinator.execute_multiple_calls(call_request_list) raise MultipleOperationsPostponed(call_report_list)
def DELETE(self, task_id): coordinator = dispatch_factory.coordinator() result = coordinator.cancel_call(task_id) if result is None: raise MissingResource(task_id) if result is False: raise TaskCancelNotImplemented(task_id) link = serialization.link.current_link_obj() return self.accepted(link)
def DELETE(self, task_id): coordinator = dispatch_factory.coordinator() tasks = coordinator.find_tasks(task_id=task_id) if not tasks: raise QueuedCallNotFound(task_id) collection = QueuedCall.get_collection() collection.remove({'_id': tasks[0].queued_call_id}, safe=True) link = serialization.link.current_link_obj() return self.accepted(link)
def GET(self, task_group_id): coordinator = dispatch_factory.coordinator() call_reports = coordinator.find_call_reports(task_group_id=task_group_id) serialized_call_reports = [c.serialize() for c in call_reports] archived_calls = dispatch_history.find_archived_calls(task_group_id=task_group_id) serialized_call_reports.extend(c['serialized_call_report'] for c in archived_calls) if not serialized_call_reports: raise TaskGroupNotFound(task_group_id) return self.ok(serialized_call_reports)
def DELETE(self, call_request_id): coordinator = dispatch_factory.coordinator() tasks = coordinator._find_tasks(call_request_id=call_request_id) if not tasks: raise QueuedCallNotFound(call_request_id) collection = QueuedCall.get_collection() collection.remove({'_id': tasks[0].queued_call_id}, safe=True) link = serialization.link.current_link_obj() return self.accepted(link)
def GET(self): valid_filters = ['tag', 'id'] filters = self.filters(valid_filters) criteria = {'tags': filters.get('tag', [])} if 'id' in filters: criteria['call_request_id_list'] = filters['id'] coordinator = dispatch_factory.coordinator() call_reports = coordinator.find_call_reports(**criteria) serialized_call_reports = [c.serialize() for c in call_reports] return self.ok(serialized_call_reports)
def GET(self, call_request_group_id): link = serialization.link.link_obj('/pulp/api/v2/task_groups/%s/' % call_request_group_id) coordinator = dispatch_factory.coordinator() call_reports = coordinator.find_call_reports(call_request_group_id=call_request_group_id) serialized_call_reports = [c.serialize() for c in call_reports] archived_calls = dispatch_history.find_archived_calls(task_group_id=call_request_group_id) serialized_call_reports.extend(c['serialized_call_report'] for c in archived_calls) if not serialized_call_reports: raise TaskGroupNotFound(call_request_group_id) map(lambda r: r.update(link), serialized_call_reports) return self.ok(serialized_call_reports)
def progress(self, reply): """ Notification (reply) indicating an RMI has reported status. This information is relayed to the task coordinator. @param reply: A progress reply object. @type reply: L{gofer.rmi.async.Progress} """ log.info('Task RMI (progress)\n%s', reply) taskid = reply.any coordinator = factory.coordinator() coordinator.report_call_progress(taskid, reply.details)
def succeeded(self, reply): """ Notification (reply) indicating an RMI succeeded. This information is relayed to the task coordinator. @param reply: A successful reply object. @type reply: L{gofer.rmi.async.Succeeded} """ log.info('Task RMI (succeeded)\n%s', reply) taskid = reply.any result = reply.retval coordinator = factory.coordinator() coordinator.complete_call_success(taskid, result)
def DELETE(self, call_request_id): coordinator = dispatch_factory.coordinator() result = coordinator.cancel_call(call_request_id) if result is None: raise MissingResource(call_request_id) if result is False: raise TaskCancelNotImplemented(call_request_id) # if we've gotten here, the call request *should* exist call_report = coordinator.find_call_reports(call_request_id=call_request_id)[0] serialized_call_report = call_report.serialize() serialized_call_report.update(serialization.link.current_link_obj()) return self.accepted(serialized_call_report)
def DELETE(self, call_request_id): coordinator = dispatch_factory.coordinator() result = coordinator.cancel_call(call_request_id) if result is None: raise MissingResource(call_request_id) if result is False: raise TaskCancelNotImplemented(call_request_id) # if we've gotten here, the call request *should* exist call_report = coordinator.find_call_reports( call_request_id=call_request_id)[0] serialized_call_report = call_report.serialize() serialized_call_report.update(serialization.link.current_link_obj()) return self.accepted(serialized_call_report)
def execute_multiple(call_request_list): """ Execute multiple calls as a task group via the coordinator. @param call_request_list: list of call request instances @type call_request_list: list """ coordinator = dispatch_factory.coordinator() call_report_list = coordinator.execute_multiple_calls(call_request_list) if call_report_list[0].response is dispatch_constants.CALL_REJECTED_RESPONSE: # the coordinator will put the reasons on the call report that conflicted reasons = reduce(lambda p, c: c.reasons or p, call_report_list, None) raise ConflictingOperation(reasons) raise MultipleOperationsPostponed(call_report_list)
def failed(self, reply): """ Notification (reply) indicating an RMI failed. This information is relayed to the task coordinator. @param reply: A failure reply object. @type reply: L{gofer.rmi.async.Failed} """ log.info('Task RMI (failed)\n%s', reply) taskid = reply.any exception = reply.exval traceback = reply.xstate['trace'] coordinator = factory.coordinator() coordinator.complete_call_failure(taskid, exception, traceback)
def GET(self): valid_filters = ['tag'] filters = self.filters(valid_filters) criteria = {'tags': filters.get('tag', [])} # support legacy search criteria if 'task_id' in criteria: criteria['call_request_id'] = criteria.pop('task_id') if 'task_group_id' in criteria: criteria['call_request_group_id'] = criteria.pop('task_group_id') coordinator = dispatch_factory.coordinator() call_reports = coordinator.find_call_reports(**criteria) serialized_call_reports = [c.serialize() for c in call_reports] return self.ok(serialized_call_reports)
def execute_async(controller, call_request, call_report=None): """ Execute a call request asynchronously via the coordinator. @param controller: web services rest controller @type controller: pulp.server.webservices.controller.base.JSONController @param call_request: call request to execute @type call_request: pulp.server.dispatch.call.CallRequest @return: http server response """ coordinator = dispatch_factory.coordinator() call_report = coordinator.execute_call_asynchronously(call_request, call_report) if call_report.response is dispatch_constants.CALL_REJECTED_RESPONSE: raise ConflictingOperation(call_report.reasons) raise OperationPostponed(call_report)
def GET(self, task_id): link = serialization.link.link_obj('/pulp/api/v2/tasks/%s/' % task_id) coordinator = dispatch_factory.coordinator() call_reports = coordinator.find_call_reports(task_id=task_id) if call_reports: serialized_call_report = call_reports[0].serialize() serialized_call_report.update(link) return self.ok(serialized_call_report) archived_calls = dispatch_history.find_archived_calls(task_id=task_id) if archived_calls.count() > 0: serialized_call_report = archived_calls[0]['serialized_call_report'] serialized_call_report.update(link) return self.ok(serialized_call_report) raise TaskNotFound(task_id)
def execute_multiple(call_request_list): """ Execute multiple calls as a task group via the coordinator. @param call_request_list: list of call request instances @type call_request_list: list """ coordinator = dispatch_factory.coordinator() call_report_list = coordinator.execute_multiple_calls(call_request_list) if call_report_list[ 0].response is dispatch_constants.CALL_REJECTED_RESPONSE: # the coordinator will put the reasons on the call report that conflicted reasons = reduce(lambda p, c: c.reasons or p, call_report_list, None) raise ConflictingOperation(reasons) raise MultipleOperationsPostponed(call_report_list)
def setUp(self): super(PulpWebserviceTests, self).setUp() self.coordinator = dispatch_factory.coordinator() self.success_failure = None self.result = None self.exception = None self.traceback = None # The built in PulpTest clean will automatically delete users between # test runs, so we can't just create the user in the class level setup. user_manager = manager_factory.user_manager() roles = [] roles.append(manager_factory.role_manager().super_user_role) user_manager.create_user(login='******', password='******', roles=roles)
def execute_async(controller, call_request, call_report=None): """ Execute a call request asynchronously via the coordinator. @param controller: web services rest controller @type controller: pulp.server.webservices.controller.base.JSONController @param call_request: call request to execute @type call_request: pulp.server.dispatch.call.CallRequest @return: http server response """ coordinator = dispatch_factory.coordinator() call_report = coordinator.execute_call_asynchronously( call_request, call_report) if call_report.response is dispatch_constants.CALL_REJECTED_RESPONSE: raise ConflictingOperation(call_report.reasons) raise OperationPostponed(call_report)
def DELETE(self, call_request_group_id): coordinator = dispatch_factory.coordinator() results = coordinator.cancel_multiple_calls(call_request_group_id) if not results: raise TaskGroupNotFound(call_request_group_id) if reduce(lambda p, v: p and (v is None), results.values(), True): # in other words, all results values are None raise TaskGroupCancelNotImplemented(call_request_group_id) # if we've gotten this far, the call requests exist and have been cancelled call_reports = coordinator.find_call_reports(call_request_group_id=call_request_group_id) serialized_call_reports = [c.serialize() for c in call_reports] link = serialization.link.current_link_obj() for s in serialized_call_reports: s.update(link) return self.accepted(serialized_call_reports)
def _run_scheduled_calls(self): """ Run the scheduled calls that are due. """ coordinator = dispatch_factory.coordinator() for call_request_group in self._get_call_request_groups_for_scheduled_itineraries(): # dispatch the itinerary call_report_list = coordinator.execute_multiple_calls(call_request_group) # log the scheduled itinerary's dispatch for call_request, call_report in zip(call_request_group, call_report_list): _LOG.info('Scheduled %s: %s [reasons: %s]' % (str(call_request), call_report.response, pformat(call_report.reasons)))
def DELETE(self, call_request_group_id): coordinator = dispatch_factory.coordinator() results = coordinator.cancel_multiple_calls(call_request_group_id) if not results: raise TaskGroupNotFound(call_request_group_id) if reduce(lambda p, v: p and (v is None), results.values(), True): # in other words, all results values are None raise TaskGroupCancelNotImplemented(call_request_group_id) # if we've gotten this far, the call requests exist and have been cancelled call_reports = coordinator.find_call_reports( call_request_group_id=call_request_group_id) serialized_call_reports = [c.serialize() for c in call_reports] link = serialization.link.current_link_obj() for s in serialized_call_reports: s.update(link) return self.accepted(serialized_call_reports)
def execute_sync(call_request, call_report=None, timeout=timedelta(seconds=20)): """ Execute a call request synchronously via the coordinator. @param call_request: call request to execute @type call_request: pulp.server.dispatch.call.CallRequest @param timeout: time to wait for task to start before raising and exception @type timeout: datetime.timedelta @return: return value from call in call request """ coordinator = dispatch_factory.coordinator() call_report = coordinator.execute_call_synchronously(call_request, call_report, timeout) if call_report.response is dispatch_constants.CALL_REJECTED_RESPONSE: raise ConflictingOperation(call_report.reasons) if call_report.state is dispatch_constants.CALL_ERROR_STATE: raise call_report.exception, None, call_report.traceback return call_report.result
def execute(call_request, call_report=None): """ Execute a call request through the controller and return the result iff the call was executed immediately. @param call_request: call request to execute @type call_request: pulp.server.dispatch.call.CallRequest @return: return value from call in call request """ coordinator = dispatch_factory.coordinator() call_report = coordinator.execute_call(call_request, call_report) if call_report.response is dispatch_constants.CALL_REJECTED_RESPONSE: raise ConflictingOperation(call_report.reasons) if call_report.state in dispatch_constants.CALL_INCOMPLETE_STATES: raise OperationPostponed(call_report) if call_report.state is dispatch_constants.CALL_ERROR_STATE: raise call_report.exception, None, call_report.traceback return call_report.result
def POST(self, uuid): """ Agent (asynchronous) RMI call back. Update the related task by ID. """ body = self.params() _LOG.info('agent (%s) reply:\n%s', uuid, body) coordinator = dispatch_factory.coordinator() call_request_id = body['any'] if body['status'] == 200: result = body['reply'] coordinator.complete_call_success(call_request_id, result) else: raised = body['exception'] exception = raised['xmsg'] traceback = raised['xstate']['trace'] coordinator.complete_call_failure(call_request_id, exception, traceback) return self.ok({})
def GET(self, call_request_group_id): link = serialization.link.link_obj('/pulp/api/v2/task_groups/%s/' % call_request_group_id) coordinator = dispatch_factory.coordinator() call_reports = coordinator.find_call_reports( call_request_group_id=call_request_group_id) found_call_request_ids = set(c.call_request_id for c in call_reports) serialized_call_reports = [c.serialize() for c in call_reports] archived_calls = dispatch_history.find_archived_calls( call_request_group_id=call_request_group_id) serialized_call_reports.extend( c['serialized_call_report'] for c in archived_calls if c['serialized_call_report']['call_request_id'] not in found_call_request_ids) if not serialized_call_reports: raise TaskGroupNotFound(call_request_group_id) map(lambda r: r.update(link), serialized_call_reports) return self.ok(serialized_call_reports)
def GET(self, call_request_id): link = serialization.link.link_obj('/pulp/api/v2/tasks/%s/' % call_request_id) coordinator = dispatch_factory.coordinator() call_reports = coordinator.find_call_reports( call_request_id=call_request_id) if call_reports: serialized_call_report = call_reports[0].serialize() serialized_call_report.update(link) return self.ok(serialized_call_report) archived_calls = dispatch_history.find_archived_calls( call_request_id=call_request_id) if archived_calls.count() > 0: serialized_call_report = archived_calls[0][ 'serialized_call_report'] serialized_call_report.update(link) return self.ok(serialized_call_report) raise TaskNotFound(call_request_id)
def POST(self, uuid): """ Agent (asynchronous) RMI call back. Update the related task by ID. """ body = self.params() _LOG.info('agent (%s) reply:\n%s', uuid, body) coordinator = dispatch_factory.coordinator() task_id = body['any'] if body['status'] == 200: result = body['reply'] coordinator.complete_call_success(task_id, result) else: raised = body['exception'] exception = raised['xmsg'] traceback = raised['xstate']['trace'] coordinator.complete_call_failure(task_id, exception, traceback) return self.ok({})
def execute_sync_ok(controller, call_request, timeout=timedelta(seconds=20)): """ Execute a call request synchronously via the coordinator. @param controller: web services rest controller @type controller: pulp.server.webservices.controller.base.JSONController @param call_request: call request to execute @type call_request: pulp.server.dispatch.call.CallRequest @param timeout: time to wait for task to start before raising and exception @type timeout: datetime.timedelta @return: http server response """ coordinator = dispatch_factory.coordinator() call_report = coordinator.execute_call_synchronously(call_request, timeout=timeout) if call_report.response is dispatch_constants.CALL_REJECTED_RESPONSE: raise ConflictingOperation(call_report.reasons) if call_report.state is dispatch_constants.CALL_ERROR_STATE: raise call_report.exception, None, call_report.traceback return controller.ok(call_report.result)
def _get_scheduled_call_groups(self): """ Get call requests, by call group, that are currently scheduled to run """ coordinator = dispatch_factory.coordinator() now = datetime.datetime.utcnow() query = {'next_run': {'$lte': now}} for scheduled_call in self.scheduled_call_collection.find(query): # updating the next run time will keep the scheduler from finding # this call again before it completes # it's also important to update the next run time for disabled calls self.update_next_run(scheduled_call) if not scheduled_call['enabled']: continue # test to see if any tasks from this schedule are already in the queue already_queued = coordinator.find_call_reports(schedule_id=scheduled_call['id']) if already_queued: log_msg = _('Schedule %(s)s skipped: last scheduled call still running') % {'s': scheduled_call['id']} _LOG.info(log_msg) continue # scheduled calls are always itinerary calls: calls that generate # call requests # get the itinerary call request and execute serialized_call_request = scheduled_call['serialized_call_request'] itinerary_call_request = call.CallRequest.deserialize(serialized_call_request) itinerary_call_request.archive = False itinerary_call_report = call.CallReport.from_call_request(itinerary_call_request) itinerary_call_report.serialize_result = False itinerary_call_report = coordinator.execute_call_synchronously(itinerary_call_request, itinerary_call_report) # call request group is the return of an itinerary function call_request_group = itinerary_call_report.result map(lambda r: setattr(r, 'schedule_id', str(scheduled_call['_id'])), call_request_group) yield call_request_group
def execute_created(controller, call_request, location): """ Execute a call request via the coordinator. @param controller: web services rest controller @type controller: pulp.server.webservices.controller.base.JSONController @param call_request: call request to execute @type call_request: pulp.server.dispatch.call.CallRequest @param location: the location of the created resource @type location: str @return: http server response @deprecated: create should always return an _href field which requires post return processing """ coordinator = dispatch_factory.coordinator() call_report = coordinator.execute_call(call_request) if call_report.response is dispatch_constants.CALL_REJECTED_RESPONSE: raise ConflictingOperation(call_report.reasons) # covers postponed and accepted if call_report.state in dispatch_constants.CALL_INCOMPLETE_STATES: raise OperationPostponed(call_report) if call_report.state is dispatch_constants.CALL_ERROR_STATE: raise call_report.exception, None, call_report.traceback return controller.created(location, call_report.result)
def GET(self, call_request_id): coordinator = dispatch_factory.coordinator() tasks = coordinator._find_tasks(call_request_id=call_request_id) if not tasks: raise QueuedCallNotFound(call_request_id) return self.ok(tasks[0].queued_call_id)
def setUp(self): PulpAsyncServerTests.setUp(self) TaskQueue.install() self.coordinator = dispatch_factory.coordinator()