def create_unit_install_schedule(self, consumer_id, units, install_options, schedule_data ): """ Create a schedule for installing content units on a consumer. @param consumer_id: unique id for the consumer @param units: list of unit type and unit key dicts @param install_options: options to pass to the install manager @param schedule_data: scheduling data @return: schedule id """ self._validate_consumer(consumer_id) self._validate_keys(install_options, _UNIT_INSTALL_OPTION_KEYS) if 'schedule' not in schedule_data: raise pulp_exceptions.MissingValue(['schedule']) manager = managers_factory.consumer_agent_manager() args = [consumer_id] kwargs = {'units': units, 'options': install_options.get('options', {})} weight = pulp_config.config.getint('tasks', 'consumer_content_weight') tags = [resource_tag(dispatch_constants.RESOURCE_CONSUMER_TYPE, consumer_id), action_tag('unit_install'), action_tag('scheduled_unit_install')] call_request = CallRequest(manager.install_content, args, kwargs, weight=weight, tags=tags, archive=True) call_request.reads_resource(dispatch_constants.RESOURCE_CONSUMER_TYPE, consumer_id) scheduler = dispatch_factory.scheduler() schedule_id = scheduler.add(call_request, **schedule_data) return schedule_id
def unregister(self, id): """ Unregisters given consumer. @param id: identifies the consumer being unregistered @type id: str @raises MissingResource: if the given consumer does not exist @raises OperationFailed: if any part of the unregister process fails; the exception will contain information on which sections failed @raises PulpExecutionException: if error during updating database collection """ self.get_consumer(id) # Remove associate bind manager = factory.consumer_bind_manager() manager.consumer_deleted(id) # Remove associated profiles manager = factory.consumer_profile_manager() manager.consumer_deleted(id) # Notify agent agent_consumer = factory.consumer_agent_manager() agent_consumer.unregistered(id) # Database Updates try: Consumer.get_collection().remove({'id' : id}, safe=True) except Exception: _LOG.exception('Error updating database collection while removing consumer [%s]' % id) raise PulpExecutionException("database-error"), None, sys.exc_info()[2] factory.consumer_history_manager().record_event(id, 'consumer_unregistered')
def uninstall_content(self, consumer_group_id, units, options): group_collection = validate_existing_consumer_group(consumer_group_id) consumer_group = group_collection.find_one({'id': consumer_group_id}) agent_manager = manager_factory.consumer_agent_manager() for consumer_id in consumer_group['consumer_ids']: agent_manager.uninstall_content(consumer_id, units, options)
def bind(self, consumer_id, repo_id, distributor_id): """ Bind consumer to a specific distributor associated with a repository. This call is idempotent. @param consumer_id: uniquely identifies the consumer. @type consumer_id: str @param repo_id: uniquely identifies the repository. @type repo_id: str @param distributor_id: uniquely identifies a distributor. @type distributor_id: str @return: The Bind object @rtype: SON @raise MissingResource: when given consumer does not exist. """ manager = factory.consumer_manager() manager.get_consumer(consumer_id) manager = factory.repo_distributor_manager() distributor = manager.get_distributor(repo_id, distributor_id) bind = Bind(consumer_id, repo_id, distributor_id) collection = Bind.get_collection() try: collection.save(bind, safe=True) bind = self.get_bind(consumer_id, repo_id, distributor_id) except DuplicateKeyError: # idempotent pass manager = factory.consumer_agent_manager() manager.bind(consumer_id, repo_id) consumer_event_details = {"repo_id": repo_id, "distributor_id": distributor_id} factory.consumer_history_manager().record_event(consumer_id, "repo_bound", consumer_event_details) return bind
def consumer_content_update_itinerary(consumer_id, units, options): """ Create an itinerary for consumer content update. @param consumer_id: unique id of the consumer @type consumer_id: str @param units: units to update @type units: list or tuple @param options: options to pass to the update manager @type options: dict or None @return: list of call requests @rtype: list """ manager = managers_factory.consumer_agent_manager() args = [consumer_id] kwargs = {'units': units, 'options': options} weight = pulp_config.config.getint('tasks', 'consumer_content_weight') tags = [resource_tag(dispatch_constants.RESOURCE_CONSUMER_TYPE, consumer_id), action_tag('unit_update')] call_request = CallRequest( manager.update_content, args, kwargs, weight=weight, tags=tags, archive=True, asynchronous=True, kwarg_blacklist=['options']) call_request.add_control_hook(dispatch_constants.CALL_CANCEL_CONTROL_HOOK, cancel_agent_request) call_request.reads_resource(dispatch_constants.RESOURCE_CONSUMER_TYPE, consumer_id) return [call_request]
def unbind(self, consumer_id, repo_id, distributor_id): """ Unbind consumer to a specifiec distirbutor associated with a repository. This call is idempotent. @param consumer_id: uniquely identifies the consumer. @type consumer_id: str @param repo_id: uniquely identifies the repository. @type repo_id: str @param distributor_id: uniquely identifies a distributor. @type distributor_id: str @return: The Bind object @rtype: SON """ query = dict(consumer_id=consumer_id, repo_id=repo_id, distributor_id=distributor_id) collection = Bind.get_collection() bind = collection.find_one(query) if bind is None: # idempotent return collection.remove(bind, safe=True) manager = factory.consumer_agent_manager() manager.unbind(consumer_id, repo_id) consumer_event_details = {"repo_id": repo_id, "distributor_id": distributor_id} factory.consumer_history_manager().record_event(consumer_id, "repo_unbound", consumer_event_details) return bind
def test_bind(self, unused): # Setup self.populate() manager = factory.consumer_bind_manager() manager.bind(self.CONSUMER_ID, self.REPO_ID, self.DISTRIBUTOR_ID, self.NOTIFY_AGENT, self.BINDING_CONFIG) # Test manager = factory.consumer_agent_manager() manager.bind(self.CONSUMER_ID, self.REPO_ID, self.DISTRIBUTOR_ID, self.OPTIONS) # verify bindings = [ dict(type_id=self.DISTRIBUTOR_ID, repo_id=self.REPO_ID, details=CONSUMER_PAYLOAD) ] args = mock_agent.Consumer.bind.call_args[0] self.assertEquals(args[0], bindings) self.assertEquals(args[1], self.OPTIONS) manager = factory.consumer_bind_manager() bind = manager.get_bind(self.CONSUMER_ID, self.REPO_ID, self.DISTRIBUTOR_ID) actions = bind['consumer_actions'] self.assertEqual(len(actions), 1) self.assertEqual(actions[0]['id'], None) self.assertEqual(actions[0]['status'], 'pending')
def uninstall(self, id): """ Uninstall content (units) on a consumer. Expected body: {units:[], options:<dict>} where unit is: {type_id:<str>, unit_key={}} and the options is a dict of uninstall options. @param id: A consumer ID. @type id: str @return: TBD @rtype: dict """ body = self.params() units = body.get('units') options = body.get('options') resources = { dispatch_constants.RESOURCE_CONSUMER_TYPE: {id:dispatch_constants.RESOURCE_READ_OPERATION}, } args = [ id, units, options, ] manager = managers.consumer_agent_manager() call_request = CallRequest( manager.uninstall_content, args, resources=resources, weight=pulp_config.config.getint('tasks', 'consumer_content_weight'), asynchronous=True, archive=True,) result = execution.execute_async(self, call_request) return result
def test_content_install(self): # Setup self.populate() # Test units = [ {'type_id':'rpm', 'unit_key':{'name':'zsh', 'version':'1.0'}}, {'type_id':'rpm', 'unit_key':{'name':'bar', 'version':'1.0'}}, {'type_id':'rpm', 'unit_key':{'name':'abc', 'version':'1.0'}}, {'type_id':'mock-type', 'unit_key':{'name':'monster', 'version':'5.0'}}, {'type_id':'unsupported', 'unit_key':{'name':'xxx', 'version':'1.0'}}, ] options = dict(importkeys=True) manager = factory.consumer_agent_manager() manager.install_content(self.CONSUMER_ID, units, options) # Verify profiler = plugin_api.get_profiler_by_type('rpm')[0] pargs = profiler.install_units.call_args[0] self.assertEquals(pargs[0].id, self.CONSUMER_ID) self.assertEquals(pargs[0].profiles, {}) self.assertEquals(pargs[1], units[:3]) self.assertEquals(pargs[2], options) profiler = plugin_api.get_profiler_by_type('mock-type')[0] pargs = profiler.install_units.call_args[0] self.assertEquals(pargs[0].id, self.CONSUMER_ID) self.assertEquals(pargs[0].profiles, {}) self.assertEquals(pargs[1], units[3:4]) self.assertEquals(pargs[2], options)
def test_content_install(self): # Setup self.populate() # Test units = [ {"type_id": "rpm", "unit_key": {"name": "zsh", "version": "1.0"}}, {"type_id": "rpm", "unit_key": {"name": "bar", "version": "1.0"}}, {"type_id": "rpm", "unit_key": {"name": "abc", "version": "1.0"}}, {"type_id": "mock-type", "unit_key": {"name": "monster", "version": "5.0"}}, {"type_id": "unsupported", "unit_key": {"name": "xxx", "version": "1.0"}}, ] options = dict(importkeys=True) manager = factory.consumer_agent_manager() manager.install_content(self.CONSUMER_ID, units, options) # Verify # agent call params = mock_agent.Content.install.call_args[0] self.assertEqual(sorted(params[0]), sorted(units)) self.assertEqual(params[1], options) # profiler call profiler = plugin_api.get_profiler_by_type("rpm")[0] pargs = profiler.install_units.call_args[0] self.assertEquals(pargs[0].id, self.CONSUMER_ID) self.assertEquals(pargs[0].profiles, {}) self.assertEquals(pargs[1], units[:3]) self.assertEquals(pargs[2], options) profiler = plugin_api.get_profiler_by_type("mock-type")[0] pargs = profiler.install_units.call_args[0] self.assertEquals(pargs[0].id, self.CONSUMER_ID) self.assertEquals(pargs[0].profiles, {}) self.assertEquals(pargs[1], units[3:4]) self.assertEquals(pargs[2], options)
def force_unbind(consumer_id, repo_id, distributor_id, options): """ Get the unbind itinerary. A forced unbind immediately deletes the binding instead of marking it deleted and going through that lifecycle. It is intended to be used to clean up orphaned bindings caused by failed/unconfirmed unbind actions on the consumer. The itinerary is: 1. Delete the binding on the server. 2. Request that the consumer (agent) perform the unbind. :param consumer_id: A consumer ID. :type consumer_id: str :param repo_id: A repository ID. :type repo_id: str :param distributor_id: A distributor ID. :type distributor_id: str :param options: Unbind options passed to the agent handler. :type options: dict :returns TaskResult containing the result of the unbind & any spawned tasks or a dictionary of the unbind result if no tasks were spawned. :rtype: TaskResult """ bind_manager = managers.consumer_bind_manager() binding = bind_manager.get_bind(consumer_id, repo_id, distributor_id) bind_manager.delete(consumer_id, repo_id, distributor_id, True) response = None if binding['notify_agent']: agent_manager = managers.consumer_agent_manager() task = agent_manager.unbind(consumer_id, repo_id, distributor_id, options) response = TaskResult(spawned_tasks=[task]) return response
def consumer_content_update_itinerary(consumer_id, units, options): """ Create an itinerary for consumer content update. @param consumer_id: unique id of the consumer @type consumer_id: str @param units: units to update @type units: list or tuple @param options: options to pass to the update manager @type options: dict or None @return: list of call requests @rtype: list """ manager = managers_factory.consumer_agent_manager() args = [consumer_id] kwargs = {'units': units, 'options': options} weight = pulp_config.config.getint('tasks', 'consumer_content_weight') tags = [ resource_tag(dispatch_constants.RESOURCE_CONSUMER_TYPE, consumer_id), action_tag('unit_update') ] call_request = CallRequest(manager.update_content, args, kwargs, weight=weight, tags=tags, archive=True, asynchronous=True) call_request.add_control_hook(dispatch_constants.CALL_CANCEL_CONTROL_HOOK, cancel_agent_request) call_request.reads_resource(dispatch_constants.RESOURCE_CONSUMER_TYPE, consumer_id) return [call_request]
def test_unregistered(self): # Setup self.populate() # Test manager = factory.consumer_agent_manager() manager.unregistered(self.CONSUMER_ID) # verify mock_agent.Consumer.unregistered.assert_called_once_with()
def create_unit_uninstall_schedule(self, consumer_id, units, uninstall_options, schedule_data): """ Create a schedule for uninstalling content units on a consumer. @param consumer_id: unique id for the consumer @param units: list of unit type and unit key dicts @param uninstall_options: options to pass to the uninstall manager @param schedule_data: scheduling data @return: schedule id """ manager = managers_factory.consumer_agent_manager() return self._create_schedule(manager.uninstall_content, UNIT_UNINSTALL_ACTION, consumer_id, units, uninstall_options, schedule_data)
def test_unbind(self): # Setup self.populate() manager = factory.consumer_bind_manager() bind = manager.bind( self.CONSUMER_ID, self.REPO_ID, self.DISTRIBUTOR_ID) # Test manager = factory.consumer_agent_manager() manager.unbind(self.CONSUMER_ID, self.REPO_ID)
def cancel_agent_request(call_request, call_report): """ Cancel the agent request associated with the task. :param call_request: The call request that has been cancelled. :type call_request: pulp.server.dispatch.call.CallRequest :param call_report: The report associated with the call request to be cancelled. :type call_report: pulp.server.dispatch.call.CallReport """ task_id = call_report.call_request_id consumer_id = call_request.args[0] agent_manager = managers_factory.consumer_agent_manager() agent_manager.cancel_request(consumer_id, task_id)
def unregister(consumer_id): """ Unregisters given consumer. :param consumer_id: identifies the consumer being unregistered :type consumer_id: str :raises MissingResource: if the given consumer does not exist :raises OperationFailed: if any part of the unregister process fails; the exception will contain information on which sections failed :raises PulpExecutionException: if error during updating database collection """ ConsumerManager.get_consumer(consumer_id) # Remove associate bind manager = factory.consumer_bind_manager() manager.consumer_deleted(consumer_id) # Remove associated profiles manager = factory.consumer_profile_manager() manager.consumer_deleted(consumer_id) # Notify agent agent_consumer = factory.consumer_agent_manager() agent_consumer.unregistered(consumer_id) # remove from consumer groups group_manager = factory.consumer_group_manager() group_manager.remove_consumer_from_groups(consumer_id) # delete any scheduled unit installs schedule_manager = factory.consumer_schedule_manager() for schedule in schedule_manager.get(consumer_id): # using "delete" on utils skips validation that the consumer exists. schedule_utils.delete(schedule.id) # Database Updates try: Consumer.get_collection().remove({'id': consumer_id}, safe=True) except Exception: _logger.exception( 'Error updating database collection while removing consumer [%s]' % consumer_id) raise PulpExecutionException( "database-error"), None, sys.exc_info()[2] # remove the consumer from any groups it was a member of group_manager = factory.consumer_group_manager() group_manager.remove_consumer_from_groups(consumer_id) factory.consumer_history_manager().record_event( consumer_id, 'consumer_unregistered')
def repo_deleted(self, repo_id): """ Notification that a repository has been deleted. Associated binds are removed. @param repo_id: A repo ID. @type repo_id: str """ collection = Bind.get_collection() agent_manager = factory.consumer_agent_manager() for bind in self.find_by_repo(repo_id): collection.remove(bind, safe=True) consumer_id = bind["consumer_id"] agent_manager.unbind(consumer_id, repo_id)
def consumer_deleted(self, id): """ Notification that a consumer has been deleted. Associated binds are removed. @param id: A consumer ID. @type id: str """ collection = Bind.get_collection() agent_manager = factory.consumer_agent_manager() for bind in self.find_by_consumer(id): collection.remove(bind, safe=True) repo_id = bind["repo_id"] agent_manager.unbind(id, repo_id)
def unregister(self, consumer_id): """ Unregisters given consumer. @param consumer_id: identifies the consumer being unregistered @type consumer_id: str @raises MissingResource: if the given consumer does not exist @raises OperationFailed: if any part of the unregister process fails; the exception will contain information on which sections failed @raises PulpExecutionException: if error during updating database collection """ self.get_consumer(consumer_id) # Remove associate bind manager = factory.consumer_bind_manager() manager.consumer_deleted(consumer_id) # Remove associated profiles manager = factory.consumer_profile_manager() manager.consumer_deleted(consumer_id) # Notify agent agent_consumer = factory.consumer_agent_manager() agent_consumer.unregistered(consumer_id) # remove from consumer groups group_manager = factory.consumer_group_manager() group_manager.remove_consumer_from_groups(consumer_id) # delete any scheduled unit installs schedule_manager = factory.schedule_manager() schedule_manager.delete_all_unit_install_schedules(consumer_id) schedule_manager.delete_all_unit_update_schedules(consumer_id) schedule_manager.delete_all_unit_uninstall_schedules(consumer_id) # Database Updates try: Consumer.get_collection().remove({'id' : consumer_id}, safe=True) except Exception: _LOG.exception('Error updating database collection while removing ' 'consumer [%s]' % consumer_id) raise PulpExecutionException("database-error"), None, sys.exc_info()[2] # remove the consumer from any groups it was a member of group_manager = factory.consumer_group_manager() group_manager.remove_consumer_from_groups(consumer_id) factory.consumer_history_manager().record_event(consumer_id, 'consumer_unregistered')
def unregister(consumer_id): """ Unregisters given consumer. :param consumer_id: identifies the consumer being unregistered :type consumer_id: str :raises MissingResource: if the given consumer does not exist :raises OperationFailed: if any part of the unregister process fails; the exception will contain information on which sections failed :raises PulpExecutionException: if error during updating database collection """ ConsumerManager.get_consumer(consumer_id) # Remove associate bind manager = factory.consumer_bind_manager() manager.consumer_deleted(consumer_id) # Remove associated profiles manager = factory.consumer_profile_manager() manager.consumer_deleted(consumer_id) # Notify agent agent_consumer = factory.consumer_agent_manager() agent_consumer.unregister(consumer_id) # remove from consumer groups group_manager = factory.consumer_group_manager() group_manager.remove_consumer_from_groups(consumer_id) # delete any scheduled unit installs schedule_manager = factory.consumer_schedule_manager() for schedule in schedule_manager.get(consumer_id): # using "delete" on utils skips validation that the consumer exists. schedule_utils.delete(schedule.id) # Database Updates try: Consumer.get_collection().remove({'id': consumer_id}) except Exception: _logger.exception( 'Error updating database collection while removing consumer [%s]' % consumer_id) raise PulpExecutionException("database-error"), None, sys.exc_info()[2] # remove the consumer from any groups it was a member of group_manager = factory.consumer_group_manager() group_manager.remove_consumer_from_groups(consumer_id) factory.consumer_history_manager().record_event(consumer_id, 'consumer_unregistered')
def uninstall_content(consumer_id, units, options): """ Uninstall content from a consumer. :param consumer_id: unique id of the consumer :type consumer_id: str :param units: units to install :type units: list or tuple :param options: options to pass to the install manager :type options: dict or None :returns Dictionary representation of a task status :rtype: dictionary """ agent_manager = managers.consumer_agent_manager() return agent_manager.uninstall_content(consumer_id, units, options)
def test_uninstall_invalid_units(self): # Setup self.populate() invalid_units = [{'type_id' : 'mock-type', 'unit_key' : 'key'}] message = 'cannot install this' mock_plugins.MOCK_PROFILER.uninstall_units.side_effect = \ InvalidUnitsRequested(invalid_units, message) # Test try: manager = factory.consumer_agent_manager() manager.uninstall_content(self.CONSUMER_ID, invalid_units, {}) self.fail() except PulpDataException, e: self.assertEqual(e.message, message)
def uninstall(self, consumer_id): """ Uninstall content (units) on a consumer. Expected body: {units:[], options:<dict>} where unit is: {type_id:<str>, unit_key={}} and the options is a dict of uninstall options. @param consumer_id: A consumer ID. @type consumer_id: str """ body = self.params() units = body.get('units') options = body.get('options') agent_manager = managers.consumer_agent_manager() task = agent_manager.uninstall_content(consumer_id, units, options) raise OperationPostponed(TaskResult.from_task_status_dict(task))
def distributor_deleted(self, repo_id, distributor_id): """ Notification that a distributor has been deleted. Associated binds are removed. @param repo_id: A Repo ID. @type repo_id: str @param distributor_id: A Distributor ID. @type distributor_id: str """ collection = Bind.get_collection() agent_manager = factory.consumer_agent_manager() for bind in self.find_by_distributor(repo_id, distributor_id): collection.remove(bind, safe=True) consumer_id = bind['consumer_id'] agent_manager.unbind(consumer_id, repo_id)
def test_content_uninstall(self): # Setup self.populate() # Test manager = factory.consumer_agent_manager() unit = dict(type_id='rpm', unit_key=dict(name='zsh')) units = [unit,] options = {} manager.uninstall_content(self.CONSUMER_ID, units, options) # verify profiler = plugin_api.get_profiler_by_type('rpm')[0] pargs = profiler.uninstall_units.call_args[0] self.assertEquals(pargs[0].id, self.CONSUMER_ID) self.assertEquals(pargs[0].profiles, {}) self.assertEquals(pargs[1], units[:3]) self.assertEquals(pargs[2], options)
def test_uninstall_invalid_units(self): # Setup self.populate() invalid_units = [{"type_id": "mock-type", "unit_key": "key"}] message = "cannot install this" mock_plugins.MOCK_PROFILER.uninstall_units.side_effect = InvalidUnitsRequested(invalid_units, message) # Test try: manager = factory.consumer_agent_manager() manager.uninstall_content(self.CONSUMER_ID, invalid_units, {}) self.fail() except PulpDataException, e: args = e.data_dict()["args"] self.assertEqual(args[0], invalid_units) self.assertEqual(args[1], message)
def uninstall_content(consumer_group_id, units, options): """ Create an itinerary for consumer group content uninstallation. :param consumer_group_id: unique id of the consumer group :type consumer_group_id: str :param units: units to uninstall :type units: list or tuple :param options: options to pass to the uninstall manager :type options: dict or None :return: Details of the subtasks that were executed :rtype: TaskResult """ consumer_group = managers.consumer_group_query_manager().get_group(consumer_group_id) agent_manager = managers.consumer_agent_manager() return _process_group(consumer_group, PLP0022, {'group_id': consumer_group_id}, agent_manager.uninstall_content, units, options)
def update_content(consumer_id, units, options, scheduled_call_id=None): """ Update units on a consumer. :param consumer_id: unique id of the consumer :type consumer_id: str :param units: units to install :type units: list or tuple :param options: options to pass to the install manager :type options: dict or None :param scheduled_call_id: id of scheduled call that dispatched this task :type scheduled_call_id: str :returns Dictionary representation of a task status :rtype: dictionary """ agent_manager = managers.consumer_agent_manager() return agent_manager.update_content(consumer_id, units, options)
def test_uninstall_invalid_units(self): # Setup self.populate() invalid_units = [{'type_id': 'mock-type', 'unit_key': 'key'}] message = 'cannot install this' mock_plugins.MOCK_PROFILER.uninstall_units.side_effect = \ InvalidUnitsRequested(invalid_units, message) # Test try: manager = factory.consumer_agent_manager() manager.uninstall_content(self.CONSUMER_ID, invalid_units, {}) self.fail() except PulpDataException, e: args = e.data_dict()['args'] self.assertEqual(args[0], invalid_units) self.assertEqual(args[1], message)
def cancel(task_id, revoke_task=True): """ Cancel the task that is represented by the given task_id. This method cancels only the task with given task_id, not the spawned tasks. This also updates task's state to 'canceled'. :param task_id: The ID of the task you wish to cancel :type task_id: basestring :param revoke_task: Whether to perform a celery revoke() on the task in edition to cancelling Works around issue #2835 (https://pulp.plan.io/issues/2835) :type revoke_task: bool :raises MissingResource: if a task with given task_id does not exist :raises PulpCodedException: if given task is already in a complete state """ try: task_status = TaskStatus.objects.get(task_id=task_id) except DoesNotExist: raise MissingResource(task_id) if task_status['state'] in constants.CALL_COMPLETE_STATES: # If the task is already done, just stop msg = _('Task [%(task_id)s] already in a completed state: %(state)s') _logger.info(msg % {'task_id': task_id, 'state': task_status['state']}) return if task_status['worker_name'] == 'agent': tag_dict = dict([ tags.parse_resource_tag(t) for t in task_status['tags'] if tags.is_resource_tag(t) ]) agent_manager = managers.consumer_agent_manager() consumer_id = tag_dict.get(tags.RESOURCE_CONSUMER_TYPE) agent_manager.cancel_request(consumer_id, task_id) else: if revoke_task: controller.revoke(task_id, terminate=True) qs = TaskStatus.objects(task_id=task_id, state__nin=constants.CALL_COMPLETE_STATES) qs.update_one(set__state=constants.CALL_CANCELED_STATE) msg = _('Task canceled: %(task_id)s.') msg = msg % {'task_id': task_id} _logger.info(msg)
def update_content(consumer_group_id, units, options): """ Create an itinerary for consumer group content update. :param consumer_group_id: unique id of the consumer group :type consumer_group_id: str :param units: units to update :type units: list or tuple :param options: options to pass to the update manager :type options: dict or None :return: Details of the subtasks that were executed :rtype: TaskResult """ consumer_group = manager_factory.consumer_group_query_manager().get_group(consumer_group_id) agent_manager = manager_factory.consumer_agent_manager() return ConsumerGroupManager.process_group(consumer_group, error_codes.PLP0021, {'group_id': consumer_group_id}, agent_manager.update_content, units, options)
def uninstall_content(consumer_group_id, units, options): """ Create an itinerary for consumer group content uninstallation. :param consumer_group_id: unique id of the consumer group :type consumer_group_id: str :param units: units to uninstall :type units: list or tuple :param options: options to pass to the uninstall manager :type options: dict or None :return: Details of the subtasks that were executed :rtype: TaskResult """ consumer_group = manager_factory.consumer_group_query_manager().get_group(consumer_group_id) agent_manager = manager_factory.consumer_agent_manager() return ConsumerGroupManager.process_group(consumer_group, error_codes.PLP0022, {'group_id': consumer_group_id}, agent_manager.uninstall_content, units, options)
def cancel(task_id, revoke_task=True): """ Cancel the task that is represented by the given task_id. This method cancels only the task with given task_id, not the spawned tasks. This also updates task's state to 'canceled'. :param task_id: The ID of the task you wish to cancel :type task_id: basestring :param revoke_task: Whether to perform a celery revoke() on the task in edition to cancelling Works around issue #2835 (https://pulp.plan.io/issues/2835) :type revoke_task: bool :raises MissingResource: if a task with given task_id does not exist :raises PulpCodedException: if given task is already in a complete state """ try: task_status = TaskStatus.objects.get(task_id=task_id) except DoesNotExist: raise MissingResource(task_id) if task_status['state'] in constants.CALL_COMPLETE_STATES: # If the task is already done, just stop msg = _('Task [%(task_id)s] already in a completed state: %(state)s') _logger.info(msg % {'task_id': task_id, 'state': task_status['state']}) return if task_status['worker_name'] == 'agent': tag_dict = dict( [ tags.parse_resource_tag(t) for t in task_status['tags'] if tags.is_resource_tag(t) ]) agent_manager = managers.consumer_agent_manager() consumer_id = tag_dict.get(tags.RESOURCE_CONSUMER_TYPE) agent_manager.cancel_request(consumer_id, task_id) else: if revoke_task: controller.revoke(task_id, terminate=True) qs = TaskStatus.objects(task_id=task_id, state__nin=constants.CALL_COMPLETE_STATES) qs.update_one(set__state=constants.CALL_CANCELED_STATE) msg = _('Task canceled: %(task_id)s.') msg = msg % {'task_id': task_id} _logger.info(msg)
def test_bind(self, unused): # Setup self.populate() manager = factory.consumer_bind_manager() manager.bind(self.CONSUMER_ID, self.REPO_ID, self.DISTRIBUTOR_ID) # Test manager = factory.consumer_agent_manager() manager.bind(self.CONSUMER_ID, self.REPO_ID, self.DISTRIBUTOR_ID, self.OPTIONS) # verify bindings = [dict(type_id=self.DISTRIBUTOR_ID, repo_id=self.REPO_ID, details=CONSUMER_PAYLOAD)] args = mock_agent.Consumer.bind.call_args[0] self.assertEquals(args[0], bindings) self.assertEquals(args[1], self.OPTIONS) manager = factory.consumer_bind_manager() bind = manager.get_bind(self.CONSUMER_ID, self.REPO_ID, self.DISTRIBUTOR_ID) actions = bind["consumer_actions"] self.assertEqual(len(actions), 1) self.assertEqual(actions[0]["id"], None) self.assertEqual(actions[0]["status"], "pending")
def update_content(consumer_group_id, units, options): """ Create an itinerary for consumer group content update. :param consumer_group_id: unique id of the consumer group :type consumer_group_id: str :param units: units to update :type units: list or tuple :param options: options to pass to the update manager :type options: dict or None :return: Details of the subtasks that were executed :rtype: TaskResult """ consumer_group = managers.consumer_group_query_manager().get_group( consumer_group_id) agent_manager = managers.consumer_agent_manager() return _process_group(consumer_group, PLP0021, {'group_id': consumer_group_id}, agent_manager.update_content, units, options)
def unbind(consumer_id, repo_id, distributor_id, options): """ Unbind a consumer. The itinerary is: 1. Unbind the consumer from the repo on the server (mark the binding on the server as deleted.) 2. Request that the consumer (agent) perform the unbind. 3. Delete the binding on the server. :param consumer_id: A consumer ID. :type consumer_id: str :param repo_id: A repository ID. :type repo_id: str :param distributor_id: A distributor ID. :type distributor_id: str :param options: Unbind options passed to the agent handler. :type options: dict :returns TaskResult containing the result of the unbind & any spawned tasks or a dictionary of the unbind result if no tasks were spawned. :rtype: TaskResult """ bind_manager = managers.consumer_bind_manager() binding = bind_manager.get_bind(consumer_id, repo_id, distributor_id) response = TaskResult(result=binding) if binding['notify_agent']: # Unbind the consumer from the repo on the server bind_manager.unbind(consumer_id, repo_id, distributor_id) # Notify the agent to remove the binding. # The agent notification handler will delete the binding from the server agent_manager = managers.consumer_agent_manager() task = agent_manager.unbind(consumer_id, repo_id, distributor_id, options) # we only want the task's ID, not the full task response.spawned_tasks.append({'task_id': task['task_id']}) else: # Since there was no agent notification, perform the delete immediately bind_manager.delete(consumer_id, repo_id, distributor_id, True) return response
def unbind(consumer_id, repo_id, distributor_id, options): """ Unbind a consumer. The itinerary is: 1. Unbind the consumer from the repo on the server (mark the binding on the server as deleted.) 2. Request that the consumer (agent) perform the unbind. 3. Delete the binding on the server. :param consumer_id: A consumer ID. :type consumer_id: str :param repo_id: A repository ID. :type repo_id: str :param distributor_id: A distributor ID. :type distributor_id: str :param options: Unbind options passed to the agent handler. :type options: dict :returns TaskResult containing the result of the unbind & any spawned tasks or a dictionary of the unbind result if no tasks were spawned. :rtype: TaskResult """ bind_manager = managers.consumer_bind_manager() binding = bind_manager.get_bind(consumer_id, repo_id, distributor_id) response = TaskResult(result=binding) if binding["notify_agent"]: # Unbind the consumer from the repo on the server bind_manager.unbind(consumer_id, repo_id, distributor_id) # Notify the agent to remove the binding. # The agent notification handler will delete the binding from the server agent_manager = managers.consumer_agent_manager() task = agent_manager.unbind(consumer_id, repo_id, distributor_id, options) # we only want the task's ID, not the full task response.spawned_tasks.append({"task_id": task["task_id"]}) else: # Since there was no agent notification, perform the delete immediately bind_manager.delete(consumer_id, repo_id, distributor_id, True) return response
def bind(consumer_id, repo_id, distributor_id, notify_agent, binding_config, agent_options): """ Bind a repo to a consumer: 1. Create the binding on the server. 2. Request that the consumer (agent) perform the bind. :param consumer_id: A consumer ID. :type consumer_id: str :param repo_id: A repository ID. :type repo_id: str :param distributor_id: A distributor ID. :type distributor_id: str :param agent_options: Bind options passed to the agent handler. :type agent_options: dict :param notify_agent: indicates if the agent should be sent a message about the new binding :type notify_agent: bool :param binding_config: configuration options to use when generating the payload for this binding :returns TaskResult containing the result of the bind & any spawned tasks or a dictionary of the bind result if no tasks were spawned. :rtype: TaskResult :raises pulp.server.exceptions.MissingResource: when given consumer does not exist """ # Create the binding on the server bind_manager = managers.consumer_bind_manager() binding = bind_manager.bind(consumer_id, repo_id, distributor_id, notify_agent, binding_config) response = TaskResult(result=binding) # Notify the agents of the binding if notify_agent: agent_manager = managers.consumer_agent_manager() task = agent_manager.bind(consumer_id, repo_id, distributor_id, agent_options) # we only want the task's ID, not the full task response.spawned_tasks.append({'task_id': task['task_id']}) return response
def test_content_uninstall(self): # Setup self.populate() # Test manager = factory.consumer_agent_manager() unit = dict(type_id='rpm', unit_key=dict(name='zsh')) units = [ unit, ] options = {} manager.uninstall_content(self.CONSUMER_ID, units, options) # Verify # agent call params = mock_agent.Content.uninstall.call_args[0] self.assertEqual(params[0], units) self.assertEqual(params[1], options) # profiler call profiler = plugin_api.get_profiler_by_type('rpm')[0] pargs = profiler.uninstall_units.call_args[0] self.assertEquals(pargs[0].id, self.CONSUMER_ID) self.assertEquals(pargs[0].profiles, {}) self.assertEquals(pargs[1], units[:3]) self.assertEquals(pargs[2], options)
def uninstall(self, request, consumer_id, units, options): """ Uninstall content (units) on a consumer. Expected body: {units:[], options:<dict>} where unit is: {type_id:<str>, unit_key={}} and the options is a dict of uninstall options. :param request: WSGI request object :type request: django.core.handlers.wsgi.WSGIRequest :param consumer_id: A consumer ID. :type consumer_id: str :param units: units to install :type units: list :param options: install options :type options: dict :raises OperationPostponed: when an async operation is performed. """ agent_manager = factory.consumer_agent_manager() task = agent_manager.uninstall_content(consumer_id, units, options) raise OperationPostponed(TaskResult.from_task_status_dict(task))
def force_unbind(consumer_id, repo_id, distributor_id, options): """ Get the unbind itinerary. A forced unbind immediately deletes the binding instead of marking it deleted and going through that lifecycle. It is intended to be used to clean up orphaned bindings caused by failed/unconfirmed unbind actions on the consumer. The itinerary is: 1. Delete the binding on the server. 2. Request that the consumer (agent) perform the unbind. :param consumer_id: A consumer ID. :type consumer_id: str :param repo_id: A repository ID. :type repo_id: str :param distributor_id: A distributor ID. :type distributor_id: str :param options: Unbind options passed to the agent handler. :type options: dict :returns TaskResult containing the result of the unbind & any spawned tasks or a dictionary of the unbind result if no tasks were spawned. :rtype: TaskResult """ bind_manager = managers.consumer_bind_manager() binding = bind_manager.get_bind(consumer_id, repo_id, distributor_id) bind_manager.delete(consumer_id, repo_id, distributor_id, True) response = TaskResult() if binding['notify_agent']: agent_manager = managers.consumer_agent_manager() task = agent_manager.unbind(consumer_id, repo_id, distributor_id, options) # we only want the task's ID, not the full task response.spawned_tasks.append({'task_id': task['task_id']}) return response
def update(self, consumer_id): """ Update content (units) on a consumer. Expected body: {units:[], options:<dict>} where unit is: {type_id:<str>, unit_key={}} and the options is a dict of update options. @param consumer_id: A consumer ID. @type consumer_id: str """ body = self.params() missing_params = [] units = body.get('units') if units is None: missing_params.append('units') options = body.get('options') if options is None: missing_params.append('options') if len(missing_params) > 0: raise MissingValue(missing_params) agent_manager = managers.consumer_agent_manager() task = agent_manager.update_content(consumer_id, units, options) raise OperationPostponed(TaskResult.from_task_status_dict(task))
def unbind_itinerary(consumer_id, repo_id, distributor_id, options): """ Get the unbind itinerary. The tasks in the itinerary are as follows: 1. Mark the binding as (deleted) on the server. 2. Request that the consumer (agent) perform the unbind. 3. Delete the binding on the server. @param consumer_id: A consumer ID. @type consumer_id: str @param repo_id: A repository ID. @type repo_id: str @param distributor_id: A distributor ID. @type distributor_id: str @param options: Unbind options passed to the agent handler. @type options: dict @return: A list of call_requests. @rtype list """ call_requests = [] bind_manager = managers.consumer_bind_manager() agent_manager = managers.consumer_agent_manager() # unbind tags = [ resource_tag(dispatch_constants.RESOURCE_CONSUMER_TYPE, consumer_id), resource_tag(dispatch_constants.RESOURCE_REPOSITORY_TYPE, repo_id), resource_tag(dispatch_constants.RESOURCE_REPOSITORY_DISTRIBUTOR_TYPE, distributor_id), action_tag(ACTION_UNBIND) ] args = [ consumer_id, repo_id, distributor_id, ] unbind_request = CallRequest(bind_manager.unbind, args=args, tags=tags) unbind_request.reads_resource(dispatch_constants.RESOURCE_CONSUMER_TYPE, consumer_id) call_requests.append(unbind_request) # notify agent tags = [ resource_tag(dispatch_constants.RESOURCE_CONSUMER_TYPE, consumer_id), resource_tag(dispatch_constants.RESOURCE_REPOSITORY_TYPE, repo_id), resource_tag(dispatch_constants.RESOURCE_REPOSITORY_DISTRIBUTOR_TYPE, distributor_id), action_tag(ACTION_AGENT_UNBIND) ] args = [ consumer_id, repo_id, distributor_id, options, ] agent_request = CallRequest(agent_manager.unbind, args, weight=0, asynchronous=True, archive=True, tags=tags) agent_request.add_life_cycle_callback( dispatch_constants.CALL_SUCCESS_LIFE_CYCLE_CALLBACK, unbind_succeeded) agent_request.add_life_cycle_callback( dispatch_constants.CALL_FAILURE_LIFE_CYCLE_CALLBACK, unbind_failed) call_requests.append(agent_request) agent_request.depends_on(unbind_request.id) # delete the binding tags = [ resource_tag(dispatch_constants.RESOURCE_CONSUMER_TYPE, consumer_id), resource_tag(dispatch_constants.RESOURCE_REPOSITORY_TYPE, repo_id), resource_tag(dispatch_constants.RESOURCE_REPOSITORY_DISTRIBUTOR_TYPE, distributor_id), action_tag(ACTION_DELETE_BINDING) ] args = [consumer_id, repo_id, distributor_id] delete_request = CallRequest(bind_manager.delete, args=args, tags=tags) unbind_request.reads_resource(dispatch_constants.RESOURCE_CONSUMER_TYPE, consumer_id) call_requests.append(delete_request) delete_request.depends_on(agent_request.id) return call_requests
def forced_unbind_itinerary(consumer_id, repo_id, distributor_id, options): """ Get the unbind itinerary. A forced unbind immediately deletes the binding instead of marking it deleted and going through that lifecycle. It is intended to be used to clean up orphaned bindings caused by failed/unconfirmed unbind actions on the consumer. The itinerary is: 1. Delete the binding on the server. 2. Request that the consumer (agent) perform the unbind. @param consumer_id: A consumer ID. @type consumer_id: str @param repo_id: A repository ID. @type repo_id: str @param distributor_id: A distributor ID. @type distributor_id: str @param options: Unbind options passed to the agent handler. @type options: dict @return: A list of call_requests @rtype list """ call_requests = [] bind_manager = managers.consumer_bind_manager() agent_manager = managers.consumer_agent_manager() # unbind tags = [ resource_tag(dispatch_constants.RESOURCE_CONSUMER_TYPE, consumer_id), resource_tag(dispatch_constants.RESOURCE_REPOSITORY_TYPE, repo_id), resource_tag(dispatch_constants.RESOURCE_REPOSITORY_DISTRIBUTOR_TYPE, distributor_id), action_tag(ACTION_UNBIND) ] args = [ consumer_id, repo_id, distributor_id, True, ] delete_request = CallRequest(bind_manager.delete, args=args, tags=tags) delete_request.reads_resource(dispatch_constants.RESOURCE_CONSUMER_TYPE, consumer_id) call_requests.append(delete_request) # notify agent tags = [ resource_tag(dispatch_constants.RESOURCE_CONSUMER_TYPE, consumer_id), resource_tag(dispatch_constants.RESOURCE_REPOSITORY_TYPE, repo_id), resource_tag(dispatch_constants.RESOURCE_REPOSITORY_DISTRIBUTOR_TYPE, distributor_id), action_tag(ACTION_AGENT_UNBIND) ] args = [ consumer_id, repo_id, distributor_id, options, ] agent_request = CallRequest(agent_manager.unbind, args, weight=0, asynchronous=True, archive=True, tags=tags) call_requests.append(agent_request) agent_request.depends_on(delete_request.id) return call_requests
def bind_itinerary(consumer_id, repo_id, distributor_id, notify_agent, binding_config, agent_options): """ Get the bind itinerary: 1. Create the binding on the server. 2. Request that the consumer (agent) perform the bind. @param consumer_id: A consumer ID. @type consumer_id: str @param repo_id: A repository ID. @type repo_id: str @param distributor_id: A distributor ID. @type distributor_id: str @param agent_options: Bind options passed to the agent handler. @type agent_options: dict @param notify_agent: indicates if the agent should be sent a message about the new binding @type notify_agent: bool @param binding_config: configuration options to use when generating the payload for this binding @return: A list of call_requests. @rtype list """ call_requests = [] bind_manager = managers.consumer_bind_manager() agent_manager = managers.consumer_agent_manager() # bind tags = [ resource_tag(dispatch_constants.RESOURCE_CONSUMER_TYPE, consumer_id), resource_tag(dispatch_constants.RESOURCE_REPOSITORY_TYPE, repo_id), resource_tag(dispatch_constants.RESOURCE_REPOSITORY_DISTRIBUTOR_TYPE, distributor_id), action_tag(ACTION_BIND) ] args = [ consumer_id, repo_id, distributor_id, notify_agent, binding_config, ] bind_request = CallRequest(bind_manager.bind, args, weight=0, tags=tags) bind_request.reads_resource(dispatch_constants.RESOURCE_CONSUMER_TYPE, consumer_id) bind_request.reads_resource(dispatch_constants.RESOURCE_REPOSITORY_TYPE, repo_id) bind_request.reads_resource( dispatch_constants.RESOURCE_REPOSITORY_DISTRIBUTOR_TYPE, distributor_id) call_requests.append(bind_request) # notify agent if notify_agent: tags = [ resource_tag(dispatch_constants.RESOURCE_CONSUMER_TYPE, consumer_id), resource_tag(dispatch_constants.RESOURCE_REPOSITORY_TYPE, repo_id), resource_tag( dispatch_constants.RESOURCE_REPOSITORY_DISTRIBUTOR_TYPE, distributor_id), action_tag(ACTION_AGENT_BIND) ] args = [consumer_id, repo_id, distributor_id, agent_options] agent_request = CallRequest(agent_manager.bind, args, weight=0, asynchronous=True, archive=True, tags=tags) agent_request.add_life_cycle_callback( dispatch_constants.CALL_SUCCESS_LIFE_CYCLE_CALLBACK, bind_succeeded) agent_request.add_life_cycle_callback( dispatch_constants.CALL_FAILURE_LIFE_CYCLE_CALLBACK, bind_failed) call_requests.append(agent_request) agent_request.depends_on(bind_request.id) return call_requests
def test_content_install(self): # Setup self.populate() # Test units = [ { 'type_id': 'rpm', 'unit_key': { 'name': 'zsh', 'version': '1.0' } }, { 'type_id': 'rpm', 'unit_key': { 'name': 'bar', 'version': '1.0' } }, { 'type_id': 'rpm', 'unit_key': { 'name': 'abc', 'version': '1.0' } }, { 'type_id': 'mock-type', 'unit_key': { 'name': 'monster', 'version': '5.0' } }, { 'type_id': 'unsupported', 'unit_key': { 'name': 'xxx', 'version': '1.0' } }, ] options = dict(importkeys=True) manager = factory.consumer_agent_manager() manager.install_content(self.CONSUMER_ID, units, options) # Verify # agent call params = mock_agent.Content.install.call_args[0] self.assertEqual(sorted(params[0]), sorted(units)) self.assertEqual(params[1], options) # profiler call profiler = plugin_api.get_profiler_by_type('rpm')[0] pargs = profiler.install_units.call_args[0] self.assertEquals(pargs[0].id, self.CONSUMER_ID) self.assertEquals(pargs[0].profiles, {}) self.assertEquals(pargs[1], units[:3]) self.assertEquals(pargs[2], options) profiler = plugin_api.get_profiler_by_type('mock-type')[0] pargs = profiler.install_units.call_args[0] self.assertEquals(pargs[0].id, self.CONSUMER_ID) self.assertEquals(pargs[0].profiles, {}) self.assertEquals(pargs[1], units[3:4]) self.assertEquals(pargs[2], options)