def _schedule(self, context, topic, request_spec, **kwargs): """Picks a host that is up at random.""" elevated = context.elevated() hosts = self.hosts_up(elevated, topic) if not hosts: msg = _("Is the appropriate service running?") raise exception.NoValidHost(reason=msg) hosts = self._filter_hosts(request_spec, hosts, **kwargs) if not hosts: msg = _("Could not find another host") raise exception.NoValidHost(reason=msg) return hosts[int(random.random() * len(hosts))]
def schedule_create_share(self, context, request_spec, filter_properties): """Picks a host that is up and has the fewest shares.""" # TODO(rushiagr) - pick only hosts that run shares elevated = context.elevated() share_id = request_spec.get('share_id') snapshot_id = request_spec.get('snapshot_id') share_properties = request_spec.get('share_properties') share_size = share_properties.get('size') availability_zone = share_properties.get('availability_zone') zone, host = None, None if availability_zone: zone, _x, host = availability_zone.partition(':') if host and context.is_admin: service = db.service_get_by_args(elevated, host, CONF.share_topic) if not utils.service_is_up(service): raise exception.WillNotSchedule(host=host) updated_share = driver.share_update_db(context, share_id, host) self.share_rpcapi.create_share(context, updated_share, host, request_spec, None, snapshot_id=snapshot_id) return None results = db.service_get_all_share_sorted(elevated) if zone: results = [(service, gigs) for (service, gigs) in results if service['availability_zone'] == zone] for result in results: (service, share_gigabytes) = result if share_gigabytes + share_size > CONF.max_gigabytes: msg = _("Not enough allocatable share gigabytes remaining") raise exception.NoValidHost(reason=msg) if utils.service_is_up(service) and not service['disabled']: updated_share = driver.share_update_db(context, share_id, service['host']) self.share_rpcapi.create_share(context, updated_share, service['host'], request_spec, None, snapshot_id=snapshot_id) return None msg = _("Is the appropriate service running?") raise exception.NoValidHost(reason=msg)
def _populate_retry_share(self, filter_properties, properties): """Populate filter properties with retry history. Populate filter properties with history of retries for this request. If maximum retries is exceeded, raise NoValidHost. """ max_attempts = self.max_attempts retry = filter_properties.pop('retry', {}) if max_attempts == 1: # re-scheduling is disabled. return # retry is enabled, update attempt count: if retry: retry['num_attempts'] += 1 else: retry = { 'num_attempts': 1, 'hosts': [] # list of share service hosts tried } filter_properties['retry'] = retry share_id = properties.get('share_id') self._log_share_error(share_id, retry) if retry['num_attempts'] > max_attempts: msg = _("Exceeded max scheduling attempts %(max_attempts)d for " "share %(share_id)s") % { "max_attempts": max_attempts, "share_id": share_id } raise exception.NoValidHost(reason=msg)
def schedule_create_share(self, context, request_spec, filter_properties): weighed_host = self._schedule_share(context, request_spec, filter_properties) if not weighed_host: raise exception.NoValidHost(reason="") host = weighed_host.obj.host share_id = request_spec['share_id'] snapshot_id = request_spec['snapshot_id'] updated_share = driver.share_update_db(context, share_id, host) self._post_select_populate_filter_properties(filter_properties, weighed_host.obj) # context is not serializable filter_properties.pop('context', None) self.share_rpcapi.create_share_instance( context, updated_share.instance, host, request_spec=request_spec, filter_properties=filter_properties, snapshot_id=snapshot_id)
def test_create_share_exception_puts_share_in_error_state( self, _mock_message_create): """Test NoValidHost exception for create_share. Puts the share in 'error' state and eats the exception. """ fake_share_id = 1 request_spec = {'share_id': fake_share_id} ex = exception.NoValidHost(reason='') with mock.patch.object( self.manager.driver, 'schedule_create_share', mock.Mock(side_effect=ex)): self.mock_object(manager.LOG, 'error') self.manager.create_share_instance( self.context, request_spec=request_spec, filter_properties={}) db.share_update.assert_called_once_with( self.context, fake_share_id, {'status': 'error'}) (self.manager.driver.schedule_create_share. assert_called_once_with(self.context, request_spec, {})) manager.LOG.error.assert_called_once_with(mock.ANY, mock.ANY) _mock_message_create.assert_called_once_with( self.context, message_field.Action.ALLOCATE_HOST, self.context.project_id, resource_type='SHARE', exception=ex, resource_id=fake_share_id)
def schedule_create_replica(self, context, request_spec, filter_properties): share_replica_id = request_spec['share_instance_properties'].get('id') weighed_host = self._schedule_share(context, request_spec, filter_properties) if not weighed_host: msg = _('Failed to find a weighted host for scheduling share ' 'replica %s.') raise exception.NoValidHost(reason=msg % share_replica_id) host = weighed_host.obj.host updated_share_replica = base.share_replica_update_db( context, share_replica_id, host) self._post_select_populate_filter_properties(filter_properties, weighed_host.obj) # context is not serializable filter_properties.pop('context', None) self.share_rpcapi.create_share_replica( context, updated_share_replica, host, request_spec=request_spec, filter_properties=filter_properties)
def test_manage_share_exception(self): share = db_utils.create_share() self.mock_object(base.Scheduler, 'host_passes_filters', mock.Mock(side_effect=exception.NoValidHost('fake'))) self.assertRaises(exception.NoValidHost, self.manager.manage_share, self.context, share['id'], 'driver_options', {}, None)
def test_migrate_share_to_host_no_valid_host(self): share = db_utils.create_share() host = 'fake@backend#pool' self.mock_object( base.Scheduler, 'host_passes_filters', mock.Mock(side_effect=[exception.NoValidHost('fake')])) self.manager.migrate_share_to_host(self.context, share['id'], host, False, {}, None)
def schedule_create_share(self, context, request_spec, filter_properties): """Picks a host that is up and has the fewest shares.""" # TODO(rushiagr) - pick only hosts that run shares elevated = context.elevated() share_id = request_spec.get('share_id') snapshot_id = request_spec.get('snapshot_id') share_properties = request_spec.get('share_properties') share_size = share_properties.get('size') instance_properties = request_spec.get('share_instance_properties', {}) availability_zone_id = instance_properties.get('availability_zone_id') results = db.service_get_all_share_sorted(elevated) if availability_zone_id: results = [ (service_g, gigs) for (service_g, gigs) in results if (service_g['availability_zone_id'] == availability_zone_id) ] for result in results: (service, share_gigabytes) = result if share_gigabytes + share_size > CONF.max_gigabytes: msg = _("Not enough allocatable share gigabytes remaining") raise exception.NoValidHost(reason=msg) if utils.service_is_up(service) and not service['disabled']: updated_share = base.share_update_db(context, share_id, service['host']) self.share_rpcapi.create_share_instance( context, updated_share.instance, service['host'], request_spec, None, snapshot_id=snapshot_id) return None msg = _("Is the appropriate service running?") raise exception.NoValidHost(reason=msg)
def test_manage_share_exception(self): share = db_utils.create_share() db_update = self.mock_object(db, 'share_update', mock.Mock()) self.mock_object(base.Scheduler, 'host_passes_filters', mock.Mock(side_effect=exception.NoValidHost('fake'))) share_id = share['id'] self.assertRaises(exception.NoValidHost, self.manager.manage_share, self.context, share['id'], 'driver_options', {'share_id': share_id}, None) db_update.assert_called_once_with(self.context, share_id, { 'status': constants.STATUS_MANAGE_ERROR, 'size': 1 })
def schedule_create_share_group(self, context, share_group_id, request_spec, filter_properties): LOG.info("Scheduling share group %s.", share_group_id) host = self._get_best_host_for_share_group(context, request_spec) if not host: msg = _("No hosts available for share group %s.") % share_group_id raise exception.NoValidHost(reason=msg) msg = "Chose host %(host)s for create_share_group %(group)s." LOG.info(msg, {'host': host, 'group': share_group_id}) updated_share_group = base.share_group_update_db( context, share_group_id, host) self.share_rpcapi.create_share_group( context, updated_share_group, host)
def schedule_create_consistency_group(self, context, group_id, request_spec, filter_properties): LOG.info(_LI("Scheduling consistency group %s") % group_id) host = self._get_best_host_for_consistency_group(context, request_spec) if not host: msg = _("No hosts available for consistency group %s") % group_id raise exception.NoValidHost(reason=msg) msg = _LI("Chose host %(host)s for create_consistency_group %(cg_id)s") LOG.info(msg % {'host': host, 'cg_id': group_id}) updated_group = driver.cg_update_db(context, group_id, host) self.share_rpcapi.create_consistency_group(context, updated_group, host)
def host_passes_filters(self, context, host, request_spec, filter_properties): elevated = context.elevated() filter_properties, share_properties = self._format_filter_properties( context, filter_properties, request_spec) hosts = self.host_manager.get_all_host_states_share(elevated) hosts = self.host_manager.get_filtered_hosts(hosts, filter_properties) hosts = self.host_manager.get_weighed_hosts(hosts, filter_properties) for tgt_host in hosts: if tgt_host.obj.host == host: return tgt_host.obj msg = (_('Cannot place share %(id)s on %(host)s') % {'id': request_spec['share_id'], 'host': host}) raise exception.NoValidHost(reason=msg)
def _schedule_share(self, context, request_spec, filter_properties=None): """Returns a list of hosts that meet the required specs. The list is ordered by their fitness. """ elevated = context.elevated() filter_properties, share_properties = self._format_filter_properties( context, filter_properties, request_spec) # Find our local list of acceptable hosts by filtering and # weighing our options. we virtually consume resources on # it so subsequent selections can adjust accordingly. # Note: remember, we are using an iterator here. So only # traverse this list once. hosts = self.host_manager.get_all_host_states_share(elevated) # Filter local hosts based on requirements ... hosts, last_filter = self.host_manager.get_filtered_hosts( hosts, filter_properties) if not hosts: msg = _('Failed to find a weighted host, the last executed filter' ' was %s.') raise exception.NoValidHost( reason=msg % last_filter, detail_data={'last_filter': last_filter}) LOG.debug("Filtered share %(hosts)s", {"hosts": hosts}) # weighted_host = WeightedHost() ... the best # host for the job. weighed_hosts = self.host_manager.get_weighed_hosts(hosts, filter_properties) best_host = weighed_hosts[0] LOG.debug("Choosing for share: %(best_host)s", {"best_host": best_host}) # NOTE(rushiagr): updating the available space parameters at same place best_host.obj.consume_from_share(share_properties) return best_host
def raise_no_valid_host(*args, **kwargs): raise exception.NoValidHost(reason="")
class SchedulerManagerTestCase(test.TestCase): """Test case for scheduler manager.""" manager_cls = manager.SchedulerManager driver_cls = base.Scheduler driver_cls_name = 'manila.scheduler.drivers.base.Scheduler' def setUp(self): super(SchedulerManagerTestCase, self).setUp() self.flags(scheduler_driver=self.driver_cls_name) self.manager = self.manager_cls() self.context = context.RequestContext('fake_user', 'fake_project') self.topic = 'fake_topic' self.fake_args = (1, 2, 3) self.fake_kwargs = {'cat': 'meow', 'dog': 'woof'} def raise_no_valid_host(*args, **kwargs): raise exception.NoValidHost(reason="") def test_1_correct_init(self): # Correct scheduler driver manager = self.manager self.assertIsInstance(manager.driver, self.driver_cls) @ddt.data('manila.scheduler.filter_scheduler.FilterScheduler', 'manila.scheduler.drivers.filter.FilterScheduler') def test_scheduler_driver_mapper(self, driver_class): test_manager = manager.SchedulerManager(scheduler_driver=driver_class) self.assertIsInstance(test_manager.driver, filter.FilterScheduler) def test_init_host(self): self.mock_object(context, 'get_admin_context', mock.Mock(return_value='fake_admin_context')) self.mock_object(self.manager, 'request_service_capabilities') self.manager.init_host() self.manager.request_service_capabilities.assert_called_once_with( 'fake_admin_context') def test_get_host_list(self): self.mock_object(self.manager.driver, 'get_host_list') self.manager.get_host_list(context) self.manager.driver.get_host_list.assert_called_once_with() def test_get_service_capabilities(self): self.mock_object(self.manager.driver, 'get_service_capabilities') self.manager.get_service_capabilities(context) self.manager.driver.get_service_capabilities.assert_called_once_with() def test_update_service_capabilities(self): service_name = 'fake_service' host = 'fake_host' with mock.patch.object(self.manager.driver, 'update_service_capabilities', mock.Mock()): self.manager.update_service_capabilities(self.context, service_name=service_name, host=host) (self.manager.driver.update_service_capabilities. assert_called_once_with(service_name, host, {})) with mock.patch.object(self.manager.driver, 'update_service_capabilities', mock.Mock()): capabilities = {'fake_capability': 'fake_value'} self.manager.update_service_capabilities(self.context, service_name=service_name, host=host, capabilities=capabilities) (self.manager.driver.update_service_capabilities. assert_called_once_with(service_name, host, capabilities)) @mock.patch.object(db, 'share_update', mock.Mock()) def test_create_share_exception_puts_share_in_error_state(self): """Test NoValidHost exception for create_share. Puts the share in 'error' state and eats the exception. """ fake_share_id = 1 request_spec = {'share_id': fake_share_id} with mock.patch.object( self.manager.driver, 'schedule_create_share', mock.Mock(side_effect=self.raise_no_valid_host)): self.mock_object(manager.LOG, 'error') self.manager.create_share_instance(self.context, request_spec=request_spec, filter_properties={}) db.share_update.assert_called_once_with(self.context, fake_share_id, {'status': 'error'}) (self.manager.driver.schedule_create_share.assert_called_once_with( self.context, request_spec, {})) manager.LOG.error.assert_called_once_with(mock.ANY, mock.ANY) @mock.patch.object(db, 'share_update', mock.Mock()) def test_create_share_other_exception_puts_share_in_error_state(self): """Test any exception except NoValidHost for create_share. Puts the share in 'error' state and re-raises the exception. """ fake_share_id = 1 request_spec = {'share_id': fake_share_id} with mock.patch.object(self.manager.driver, 'schedule_create_share', mock.Mock(side_effect=exception.QuotaError)): self.mock_object(manager.LOG, 'error') self.assertRaises(exception.QuotaError, self.manager.create_share_instance, self.context, request_spec=request_spec, filter_properties={}) db.share_update.assert_called_once_with(self.context, fake_share_id, {'status': 'error'}) (self.manager.driver.schedule_create_share.assert_called_once_with( self.context, request_spec, {})) manager.LOG.error.assert_called_once_with(mock.ANY, mock.ANY) def test_get_pools(self): """Ensure get_pools exists and calls base_scheduler.get_pools.""" mock_get_pools = self.mock_object(self.manager.driver, 'get_pools', mock.Mock(return_value='fake_pools')) result = self.manager.get_pools(self.context, filters='fake_filters') mock_get_pools.assert_called_once_with(self.context, 'fake_filters') self.assertEqual('fake_pools', result) @mock.patch.object(db, 'share_group_update', mock.Mock()) def test_create_group_no_valid_host_puts_group_in_error_state(self): """Test that NoValidHost is raised for create_share_group. Puts the share in 'error' state and eats the exception. """ fake_group_id = 1 group_id = fake_group_id request_spec = {"share_group_id": group_id} with mock.patch.object( self.manager.driver, 'schedule_create_share_group', mock.Mock(side_effect=self.raise_no_valid_host)): self.manager.create_share_group(self.context, fake_group_id, request_spec=request_spec, filter_properties={}) db.share_group_update.assert_called_once_with( self.context, fake_group_id, {'status': 'error'}) (self.manager.driver.schedule_create_share_group. assert_called_once_with(self.context, group_id, request_spec, {})) @mock.patch.object(db, 'share_group_update', mock.Mock()) def test_create_group_exception_puts_group_in_error_state(self): """Test that exceptions for create_share_group. Puts the share in 'error' state and raises the exception. """ fake_group_id = 1 group_id = fake_group_id request_spec = {"share_group_id": group_id} with mock.patch.object(self.manager.driver, 'schedule_create_share_group', mock.Mock(side_effect=exception.NotFound)): self.assertRaises(exception.NotFound, self.manager.create_share_group, self.context, fake_group_id, request_spec=request_spec, filter_properties={}) def test_migrate_share_to_host(self): class fake_host(object): host = 'fake@backend#pool' share = db_utils.create_share() host = fake_host() self.mock_object(db, 'share_get', mock.Mock(return_value=share)) self.mock_object(share_rpcapi.ShareAPI, 'migration_start', mock.Mock(side_effect=TypeError)) self.mock_object(base.Scheduler, 'host_passes_filters', mock.Mock(return_value=host)) self.assertRaises(TypeError, self.manager.migrate_share_to_host, self.context, share['id'], 'fake@backend#pool', False, True, True, False, True, 'fake_net_id', 'fake_type_id', {}, None) db.share_get.assert_called_once_with(self.context, share['id']) base.Scheduler.host_passes_filters.assert_called_once_with( self.context, 'fake@backend#pool', {}, None) share_rpcapi.ShareAPI.migration_start.assert_called_once_with( self.context, share, host.host, False, True, True, False, True, 'fake_net_id', 'fake_type_id') @ddt.data(exception.NoValidHost(reason='fake'), TypeError) def test_migrate_share_to_host_exception(self, exc): share = db_utils.create_share(status=constants.STATUS_MIGRATING) host = 'fake@backend#pool' request_spec = {'share_id': share['id']} self.mock_object(db, 'share_get', mock.Mock(return_value=share)) self.mock_object(base.Scheduler, 'host_passes_filters', mock.Mock(side_effect=exc)) self.mock_object(db, 'share_update') self.mock_object(db, 'share_instance_update') capture = (exception.NoValidHost if isinstance(exc, exception.NoValidHost) else TypeError) self.assertRaises(capture, self.manager.migrate_share_to_host, self.context, share['id'], host, False, True, True, False, True, 'fake_net_id', 'fake_type_id', request_spec, None) base.Scheduler.host_passes_filters.assert_called_once_with( self.context, host, request_spec, None) db.share_get.assert_called_once_with(self.context, share['id']) db.share_update.assert_called_once_with( self.context, share['id'], {'task_state': constants.TASK_STATE_MIGRATION_ERROR}) db.share_instance_update.assert_called_once_with( self.context, share.instance['id'], {'status': constants.STATUS_AVAILABLE}) def test_manage_share(self): share = db_utils.create_share() self.mock_object(db, 'share_get', mock.Mock(return_value=share)) self.mock_object(share_rpcapi.ShareAPI, 'manage_share') self.mock_object(base.Scheduler, 'host_passes_filters') self.manager.manage_share(self.context, share['id'], 'driver_options', {}, None) def test_manage_share_exception(self): share = db_utils.create_share() db_update = self.mock_object(db, 'share_update', mock.Mock()) self.mock_object(base.Scheduler, 'host_passes_filters', mock.Mock(side_effect=exception.NoValidHost('fake'))) share_id = share['id'] self.assertRaises(exception.NoValidHost, self.manager.manage_share, self.context, share['id'], 'driver_options', {'share_id': share_id}, None) db_update.assert_called_once_with(self.context, share_id, { 'status': constants.STATUS_MANAGE_ERROR, 'size': 1 }) def test_create_share_replica_exception_path(self): """Test 'raisable' exceptions for create_share_replica.""" db_update = self.mock_object(db, 'share_replica_update') self.mock_object(db, 'share_snapshot_instance_get_all_with_filters', mock.Mock(return_value=[{ 'id': '123' }])) snap_update = self.mock_object(db, 'share_snapshot_instance_update') request_spec = fakes.fake_replica_request_spec() replica_id = request_spec.get('share_instance_properties').get('id') expected_updates = { 'status': constants.STATUS_ERROR, 'replica_state': constants.STATUS_ERROR, } with mock.patch.object(self.manager.driver, 'schedule_create_replica', mock.Mock(side_effect=exception.NotFound)): self.assertRaises(exception.NotFound, self.manager.create_share_replica, self.context, request_spec=request_spec, filter_properties={}) db_update.assert_called_once_with(self.context, replica_id, expected_updates) snap_update.assert_called_once_with( self.context, '123', {'status': constants.STATUS_ERROR}) def test_create_share_replica_no_valid_host(self): """Test the NoValidHost exception for create_share_replica.""" db_update = self.mock_object(db, 'share_replica_update') request_spec = fakes.fake_replica_request_spec() replica_id = request_spec.get('share_instance_properties').get('id') expected_updates = { 'status': constants.STATUS_ERROR, 'replica_state': constants.STATUS_ERROR, } with mock.patch.object( self.manager.driver, 'schedule_create_replica', mock.Mock(side_effect=self.raise_no_valid_host)): retval = self.manager.create_share_replica( self.context, request_spec=request_spec, filter_properties={}) self.assertIsNone(retval) db_update.assert_called_once_with(self.context, replica_id, expected_updates) def test_create_share_replica(self): """Test happy path for create_share_replica.""" db_update = self.mock_object(db, 'share_replica_update') mock_scheduler_driver_call = self.mock_object( self.manager.driver, 'schedule_create_replica') request_spec = fakes.fake_replica_request_spec() retval = self.manager.create_share_replica(self.context, request_spec=request_spec, filter_properties={}) mock_scheduler_driver_call.assert_called_once_with( self.context, request_spec, {}) self.assertFalse(db_update.called) self.assertIsNone(retval)
class MessageFieldTest(test.TestCase): @ddt.data(message_field.Action, message_field.Detail) def test_unique_ids(self, cls): """Assert that no action or detail id is duplicated.""" ids = [name[0] for name in cls.ALL] self.assertEqual(len(ids), len(set(ids))) @ddt.data({ 'id': '001', 'content': 'allocate host' }, { 'id': 'invalid', 'content': None }) @ddt.unpack def test_translate_action(self, id, content): result = message_field.translate_action(id) if content is None: content = 'unknown action' self.assertEqual(content, result) @ddt.data({ 'id': '001', 'content': 'An unknown error occurred.' }, { 'id': '002', 'content': 'No storage could be allocated for this share ' 'request. Trying again with a different size or ' 'share type may succeed.' }, { 'id': 'invalid', 'content': None }) @ddt.unpack def test_translate_detail(self, id, content): result = message_field.translate_detail(id) if content is None: content = 'An unknown error occurred.' self.assertEqual(content, result) @ddt.data( { 'exception': exception.NoValidHost(reason='fake reason'), 'detail': '', 'expected': '002' }, { 'exception': exception.NoValidHost( detail_data={'last_filter': 'CapacityFilter'}, reason='fake reason'), 'detail': '', 'expected': '009' }, { 'exception': exception.NoValidHost(detail_data={'last_filter': 'FakeFilter'}, reason='fake reason'), 'detail': '', 'expected': '002' }, { 'exception': None, 'detail': message_field.Detail.NO_VALID_HOST, 'expected': '002' }) @ddt.unpack def test_translate_detail_id(self, exception, detail, expected): result = message_field.translate_detail_id(exception, detail) self.assertEqual(expected, result)