def mock_the_extension_manager(driver="fake", namespace="ironic.drivers"): """Get a fake stevedore NameDispatchExtensionManager instance. :param namespace: A string representing the namespace over which to search for entrypoints. :returns mock_ext_mgr: A DriverFactory instance that has been faked. :returns mock_ext: A real plugin loaded by mock_ext_mgr in the specified namespace. """ entry_point = None for ep in list(pkg_resources.iter_entry_points(namespace)): s = "%s" % ep if driver == s[:s.index(' =')]: entry_point = ep break # NOTE(lucasagomes): Initialize the _extension_manager before # instantiaing a DriverFactory class to avoid # a real NameDispatchExtensionManager to be created # with the real namespace. driver_factory.DriverFactory._extension_manager = ( dispatch.NameDispatchExtensionManager('ironic.no-such-namespace', lambda x: True)) mock_ext_mgr = driver_factory.DriverFactory() mock_ext = mock_ext_mgr._extension_manager._load_one_plugin( entry_point, True, [], {}, False) mock_ext_mgr._extension_manager.extensions = [mock_ext] mock_ext_mgr._extension_manager.by_name = dict( (e.name, e) for e in [mock_ext]) return (mock_ext_mgr, mock_ext)
def test_start_registers_driver_specific_tasks(self, get_mock): init_names = ['fake1'] self.config(enabled_drivers=init_names) class TestInterface(object): @periodics.periodic(spacing=100500) def iface(self): pass class Driver(object): core_interfaces = [] standard_interfaces = ['iface'] all_interfaces = core_interfaces + standard_interfaces iface = TestInterface() @periodics.periodic(spacing=42) def task(self, context): pass obj = Driver() get_mock.return_value = mock.Mock(obj=obj) with mock.patch.object( driver_factory.DriverFactory()._extension_manager, 'names') as mock_names: mock_names.return_value = init_names self._start_service(start_periodic_tasks=True) tasks = {c[0] for c in self.service._periodic_task_callables} for t in (obj.task, obj.iface.iface): self.assertTrue(periodics.is_periodic(t)) self.assertIn(t, tasks)
def test_start_registers_driver_names(self, net_factory, storage_factory): init_names = ['fake1', 'fake2'] restart_names = ['fake3', 'fake4'] df = driver_factory.DriverFactory() with mock.patch.object(df._extension_manager, 'names') as mock_names: # verify driver names are registered self.config(enabled_drivers=init_names) mock_names.return_value = init_names self._start_service() res = objects.Conductor.get_by_hostname(self.context, self.hostname) self.assertEqual(init_names, res['drivers']) self._stop_service() # verify that restart registers new driver names self.config(enabled_drivers=restart_names) mock_names.return_value = restart_names self._start_service() res = objects.Conductor.get_by_hostname(self.context, self.hostname) self.assertEqual(restart_names, res['drivers']) self.assertEqual(2, net_factory.call_count) self.assertEqual(2, storage_factory.call_count)
def start(self): super(ConductorManager, self).start() self.dbapi = dbapi.get_instance() # create a DriverFactory instance, which initializes the stevedore # extension manager, when the service starts. # TODO(deva): Enable re-loading of the DriverFactory to load new # extensions without restarting the whole service. df = driver_factory.DriverFactory() self.drivers = df.names """List of driver names which this conductor supports.""" try: self.dbapi.register_conductor({ 'hostname': self.host, 'drivers': self.drivers }) except exception.ConductorAlreadyRegistered: LOG.warn( _("A conductor with hostname %(hostname)s " "was previously registered. Updating registration") % {'hostname': self.host}) self.dbapi.unregister_conductor(self.host) self.dbapi.register_conductor({ 'hostname': self.host, 'drivers': self.drivers }) self.driver_rings = self._get_current_driver_rings() """Consistent hash ring which maps drivers to conductors.""" self._worker_pool = greenpool.GreenPool(size=CONF.rpc_thread_pool_size) """GreenPool of background workers for performing tasks async."""
def init_host(self): self.dbapi = dbapi.get_instance() self.driver_factory = driver_factory.DriverFactory() self.drivers = self.driver_factory.names """List of driver names which this conductor supports.""" try: self.dbapi.register_conductor({'hostname': self.host, 'drivers': self.drivers}) except exception.ConductorAlreadyRegistered: LOG.warn(_("A conductor with hostname %(hostname)s " "was previously registered. Updating registration") % {'hostname': self.host}) self.dbapi.unregister_conductor(self.host) self.dbapi.register_conductor({'hostname': self.host, 'drivers': self.drivers}) self.ring_manager = hash.HashRingManager() """Consistent hash ring which maps drivers to conductors.""" self._worker_pool = greenpool.GreenPool( size=CONF.conductor.workers_pool_size) """GreenPool of background workers for performing tasks async.""" # Spawn a dedicated greenthread for the keepalive try: self._keepalive_evt = threading.Event() self._spawn_worker(self._conductor_service_record_keepalive) except exception.NoFreeConductorWorker: with excutils.save_and_reraise_exception(): LOG.critical(_('Failed to start keepalive')) self.del_host()
def test_start_registers_driver_names(self): init_names = ['fake1', 'fake2'] restart_names = ['fake3', 'fake4'] df = driver_factory.DriverFactory() with mock.patch.object(df._extension_manager, 'names') as mock_names: # verify driver names are registered mock_names.return_value = init_names self.service.start() res = self.dbapi.get_conductor('test-host') self.assertEqual(res['drivers'], init_names) # verify that restart registers new driver names mock_names.return_value = restart_names self.service.start() res = self.dbapi.get_conductor('test-host') self.assertEqual(res['drivers'], restart_names)
def test_start_registers_driver_specific_tasks(self, get_mock): init_names = ['fake1'] expected_name = 'ironic.tests.unit.conductor.test_base_manager.task' expected_name2 = 'ironic.tests.unit.conductor.test_base_manager.iface' self.config(enabled_drivers=init_names) class TestInterface(object): @drivers_base.driver_periodic_task(spacing=100500) def iface(self): pass class Driver(object): core_interfaces = [] standard_interfaces = ['iface'] iface = TestInterface() @drivers_base.driver_periodic_task(spacing=42) def task(self, context): pass obj = Driver() self.assertTrue(obj.task._periodic_enabled) get_mock.return_value = mock.Mock(obj=obj) with mock.patch.object( driver_factory.DriverFactory()._extension_manager, 'names') as mock_names: mock_names.return_value = init_names self._start_service() tasks = dict(self.service._periodic_tasks) self.assertEqual(obj.task, tasks[expected_name]) self.assertEqual(obj.iface.iface, tasks[expected_name2]) self.assertEqual(42, self.service._periodic_spacing[expected_name]) self.assertEqual(100500, self.service._periodic_spacing[expected_name2]) self.assertIn(expected_name, self.service._periodic_last_run) self.assertIn(expected_name2, self.service._periodic_last_run)
def init_host(self): self.dbapi = dbapi.get_instance() self.driver_factory = driver_factory.DriverFactory() self.drivers = self.driver_factory.names """List of driver names which this conductor supports.""" try: self.dbapi.register_conductor({'hostname': self.host, 'drivers': self.drivers}) except exception.ConductorAlreadyRegistered: LOG.warn(_("A conductor with hostname %(hostname)s " "was previously registered. Updating registration") % {'hostname': self.host}) self.dbapi.unregister_conductor(self.host) self.dbapi.register_conductor({'hostname': self.host, 'drivers': self.drivers}) self.ring_manager = hash.HashRingManager() """Consistent hash ring which maps drivers to conductors.""" self._worker_pool = greenpool.GreenPool( size=CONF.conductor.workers_pool_size) """GreenPool of background workers for performing tasks async."""
def init_host(self): self.dbapi = dbapi.get_instance() self._keepalive_evt = threading.Event() """Event for the keepalive thread.""" self._worker_pool = greenpool.GreenPool( size=CONF.conductor.workers_pool_size) """GreenPool of background workers for performing tasks async.""" self.ring_manager = hash.HashRingManager() """Consistent hash ring which maps drivers to conductors.""" # NOTE(deva): instantiating DriverFactory may raise DriverLoadError # or DriverNotFound self._driver_factory = driver_factory.DriverFactory() """Driver factory loads all enabled drivers.""" self.drivers = self._driver_factory.names """List of driver names which this conductor supports.""" if not self.drivers: msg = _LE("Conductor %s cannot be started because no drivers " "were loaded. This could be because no drivers were " "specified in 'enabled_drivers' config option.") LOG.error(msg, self.host) raise exception.NoDriversLoaded(conductor=self.host) # Collect driver-specific periodic tasks for driver_obj in driver_factory.drivers().values(): self._collect_periodic_tasks(driver_obj) for iface_name in (driver_obj.core_interfaces + driver_obj.standard_interfaces + ['vendor']): iface = getattr(driver_obj, iface_name, None) if iface: self._collect_periodic_tasks(iface) # clear all locks held by this conductor before registering self.dbapi.clear_node_reservations_for_conductor(self.host) try: # Register this conductor with the cluster cdr = self.dbapi.register_conductor({ 'hostname': self.host, 'drivers': self.drivers }) except exception.ConductorAlreadyRegistered: # This conductor was already registered and did not shut down # properly, so log a warning and update the record. LOG.warning( _LW("A conductor with hostname %(hostname)s " "was previously registered. Updating registration"), {'hostname': self.host}) cdr = self.dbapi.register_conductor( { 'hostname': self.host, 'drivers': self.drivers }, update_existing=True) self.conductor = cdr # NOTE(lucasagomes): If the conductor server dies abruptly # mid deployment (OMM Killer, power outage, etc...) we # can not resume the deployment even if the conductor # comes back online. Cleaning the reservation of the nodes # (dbapi.clear_node_reservations_for_conductor) is not enough to # unstick it, so let's gracefully fail the deployment so the node # can go through the steps (deleting & cleaning) to make itself # available again. filters = {'reserved': False, 'provision_state': states.DEPLOYING} last_error = (_("The deployment can't be resumed by conductor " "%s. Moving to fail state.") % self.host) self._fail_if_in_state(ironic_context.get_admin_context(), filters, states.DEPLOYING, 'provision_updated_at', last_error=last_error) # Spawn a dedicated greenthread for the keepalive try: self._spawn_worker(self._conductor_service_record_keepalive) LOG.info( _LI('Successfully started conductor with hostname ' '%(hostname)s.'), {'hostname': self.host}) except exception.NoFreeConductorWorker: with excutils.save_and_reraise_exception(): LOG.critical(_LC('Failed to start keepalive')) self.del_host()