def test_namespec(self): """ Test of the process namspec. """ from supvisors.process import ProcessStatus # test namespec when group and name are different info = process_info_by_name('segv') process = ProcessStatus(info['group'], info['name'], self.supvisors) self.assertEqual('crash:segv', process.namespec()) # test namespec when group and name are identical info = process_info_by_name('firefox') process = ProcessStatus(info['group'], info['name'], self.supvisors) self.assertEqual('firefox', process.namespec())
def test_update_sequences(self): """ Test the sequencing of the deployment method. """ from supvisors.application import ApplicationStatus from supvisors.process import ProcessStatus application = ApplicationStatus('ApplicationTest', self.supvisors.logger) # add processes to the application for info in ProcessInfoDatabase: process = ProcessStatus(info['group'], info['name'], self.supvisors) process.add_info('10.0.0.1', info.copy()) # set random sequence to process process.rules.start_sequence = random.randint(0, 2) process.rules.stop_sequence = random.randint(0, 2) application.add_process(process) # call the sequencer application.update_sequences() # check the sequencing of the starting sequences = sorted({process.rules.start_sequence for process in application.processes.values()}) # as key is an integer, the sequence dictionary should be sorted but pypy doesn't... for sequence, processes in sorted(application.start_sequence.items()): self.assertEqual(sequence, sequences.pop(0)) self.assertListEqual(sorted(processes, key=lambda x: x.process_name), sorted([proc for proc in application.processes.values() if sequence == proc.rules.start_sequence], key=lambda x: x.process_name)) # check the sequencing of the stopping sequences = sorted({process.rules.stop_sequence for process in application.processes.values()}) # as key is an integer, the sequence dictionary should be sorted but pypy doesn't... for sequence, processes in sorted(application.stop_sequence.items()): self.assertEqual(sequence, sequences.pop(0)) self.assertListEqual(sorted(processes, key=lambda x: x.process_name), sorted([proc for proc in application.processes.values() if sequence == proc.rules.stop_sequence], key=lambda x: x.process_name))
def test_update_times(self): """ Test the update of the time entries for a process info belonging to a ProcessStatus. """ from supervisor.states import ProcessStates from supvisors.process import ProcessStatus # add 2 process infos into a process status info = any_process_info_by_state(ProcessStates.STOPPING) process = ProcessStatus(info['group'], info['name'], self.supvisors) process.add_info('10.0.0.1', info) process.add_info('10.0.0.2', any_process_info_by_state(ProcessStates.STOPPED)) # get their time values now_1 = process.infos['10.0.0.1']['now'] uptime_1 = process.infos['10.0.0.1']['uptime'] now_2 = process.infos['10.0.0.2']['now'] # update times on address 2 process.update_times('10.0.0.2', now_2 + 10) # check that nothing changed for address 1 self.assertEqual(now_1, process.infos['10.0.0.1']['now']) self.assertEqual(uptime_1, process.infos['10.0.0.1']['uptime']) # check that times changed for address 2 (uptime excepted) self.assertEqual(now_2 + 10, process.infos['10.0.0.2']['now']) self.assertEqual(0, process.infos['10.0.0.2']['uptime']) # update times on address 1 process.update_times('10.0.0.1', now_1 + 20) # check that times changed for address 1 (including uptime) self.assertEqual(now_1 + 20, process.infos['10.0.0.1']['now']) self.assertEqual(uptime_1 + 20, process.infos['10.0.0.1']['uptime']) # check that nothing changed for address 2 self.assertEqual(now_2 + 10, process.infos['10.0.0.2']['now']) self.assertEqual(0, process.infos['10.0.0.2']['uptime'])
def test_evaluate_conflict(self): """ Test the determination of a synthetic state in case of conflict. """ from supervisor.states import ProcessStates from supvisors.process import ProcessStatus # when there is only one STOPPED process info, there is no conflict info = any_process_info_by_state(ProcessStates.STOPPED) process = ProcessStatus(info['group'], info['name'], self.supvisors) process.add_info('10.0.0.1', info) self.assertFalse(process.evaluate_conflict()) self.assertEqual(ProcessStates.STOPPED, process.state) # the addition of one RUNNING process info does not raise any conflict process.infos['10.0.0.2'] = any_process_info_by_state(ProcessStates.RUNNING) process.addresses = {'10.0.0.2'} self.assertFalse(process.evaluate_conflict()) # the addition of one STARTING process raises a conflict process.infos['10.0.0.3'] = any_process_info_by_state(ProcessStates.STARTING) process.addresses.add('10.0.0.3') self.assertTrue(process.evaluate_conflict()) self.assertEqual(ProcessStates.RUNNING, process.state) # replace the RUNNING process info with a BACKOFF process info process.infos['10.0.0.2'] = any_process_info_by_state(ProcessStates.BACKOFF) self.assertTrue(process.evaluate_conflict()) self.assertEqual(ProcessStates.BACKOFF, process.state) # replace the BACKOFF process info with a STARTING process info process.infos['10.0.0.2'] = any_process_info_by_state(ProcessStates.STARTING) self.assertTrue(process.evaluate_conflict()) self.assertEqual(ProcessStates.STARTING, process.state) # replace the STARTING process info with an EXITED process info process.infos['10.0.0.2'] = any_process_info_by_state(ProcessStates.EXITED) process.addresses.remove('10.0.0.2') self.assertFalse(process.evaluate_conflict()) self.assertEqual(ProcessStates.STARTING, process.state)
def test_times(self): """ Test the update_times method. """ from supervisor.states import ProcessStates from supvisors.address import AddressStatus from supvisors.process import ProcessStatus status = AddressStatus('10.0.0.1', self.supvisors.logger) # add processes for info in ProcessInfoDatabase: process = ProcessStatus(info['group'], info['name'], self.supvisors) process.add_info('10.0.0.1', info.copy()) status.add_process(process) # get current process times ref_data = {process.namespec(): (process.state, info['now'], info['local_time'], info.get('uptime', None)) for process in status.processes.values() for info in [process.infos['10.0.0.1']]} # update times and check now = time.time() status.update_times(now + 10, now) self.assertEqual(now + 10, status.remote_time) self.assertEqual(now, status.local_time) # test process times: only RUNNING has an uptime new_data = {process.namespec(): (process.state, info['now'], info['local_time'], info.get('uptime', None)) for process in status.processes.values() for info in [process.infos['10.0.0.1']]} for namespec, new_info in new_data.items(): ref_info = ref_data[namespec] self.assertEqual(new_info[0], ref_info[0]) self.assertGreater(new_info[1], ref_info[1]) self.assertGreater(new_info[2], ref_info[2]) if new_info[0] in [ProcessStates.RUNNING, ProcessStates.STOPPING]: self.assertGreater(new_info[3], ref_info[3]) else: self.assertEqual(new_info[3], ref_info[3])
def test_invalidate_address(self): """ Test the invalidation of addresses. """ from supervisor.states import ProcessStates from supvisors.process import ProcessStatus # create conflict directly with 3 process info info = any_process_info_by_state(ProcessStates.BACKOFF) process = ProcessStatus(info['group'], info['name'], self.supvisors) process.add_info('10.0.0.1', info) process.add_info('10.0.0.2', any_process_info_by_state(ProcessStates.RUNNING)) process.add_info('10.0.0.3', any_process_info_by_state(ProcessStates.STARTING)) # check the conflict self.assertTrue(process.conflicting()) self.assertEqual(ProcessStates.RUNNING, process.state) # invalidate RUNNING one process.invalidate_address('10.0.0.2', False) # check state became UNKNOWN on invalidated address self.assertEqual(ProcessStates.UNKNOWN, process.infos['10.0.0.2']['state']) # check the conflict self.assertTrue(process.conflicting()) # check new synthetic state self.assertEqual(ProcessStates.BACKOFF, process.state) # invalidate BACKOFF one process.invalidate_address('10.0.0.1', False) # check state became UNKNOWN on invalidated address self.assertEqual(ProcessStates.UNKNOWN, process.infos['10.0.0.1']['state']) # check 1 address: no conflict self.assertFalse(process.conflicting()) # check synthetic state (running process) self.assertEqual(ProcessStates.STARTING, process.state) # invalidate STARTING one process.invalidate_address('10.0.0.3', True) # check state became UNKNOWN on invalidated address self.assertEqual(ProcessStates.UNKNOWN, process.infos['10.0.0.3']['state']) # check 0 address: no conflict self.assertFalse(process.conflicting()) # check that synthetic state became FATAL self.assertEqual(ProcessStates.FATAL, process.state) # check that failure_handler is notified self.assertEqual( [call(process)], self.supvisors.failure_handler.add_default_job.call_args_list) # add one STOPPING process.add_info('10.0.0.4', any_process_info_by_state(ProcessStates.STOPPING)) # check state STOPPING self.assertEqual(ProcessStates.STOPPING, process.state) # invalidate STOPPING one process.invalidate_address('10.0.0.4', False) # check state became UNKNOWN on invalidated address self.assertEqual(ProcessStates.UNKNOWN, process.infos['10.0.0.4']['state']) # check that synthetic state became STOPPED self.assertEqual(ProcessStates.STOPPED, process.state)
def test_accept_extra_arguments(self): """ Test the possibility to add extra arguments when starting the process, iaw the process rules. """ from supvisors.process import ProcessStatus info = any_process_info() process = ProcessStatus(info['group'], info['name'], self.supvisors) self.supvisors.info_source.autorestart.return_value = False self.assertTrue(process.accept_extra_arguments()) self.supvisors.info_source.autorestart.return_value = True self.assertFalse(process.accept_extra_arguments())
def test_stopped_running(self): """ Test the stopped / running status. """ from supervisor.states import ProcessStates from supvisors.process import ProcessStatus # test with STOPPED process info = any_process_info_by_state(ProcessStates.STOPPED) process = ProcessStatus(info['group'], info['name'], self.supvisors) process.add_info('10.0.0.1', info) self.assertTrue(process.stopped()) self.assertFalse(process.running()) self.assertFalse(process.running_on('10.0.0.1')) self.assertFalse(process.pid_running_on('10.0.0.1')) # test with BACKOFF process info = any_process_info_by_state(ProcessStates.BACKOFF) process = ProcessStatus(info['group'], info['name'], self.supvisors) process.add_info('10.0.0.1', info) self.assertFalse(process.stopped()) self.assertTrue(process.running()) self.assertTrue(process.running_on('10.0.0.1')) self.assertFalse(process.running_on('10.0.0.2')) self.assertFalse(process.pid_running_on('10.0.0.1')) self.assertFalse(process.pid_running_on('10.0.0.2')) # test with RUNNING process info = any_process_info_by_state(ProcessStates.RUNNING) process = ProcessStatus(info['group'], info['name'], self.supvisors) process.add_info('10.0.0.1', info) self.assertFalse(process.stopped()) self.assertTrue(process.running()) self.assertTrue(process.running_on('10.0.0.1')) self.assertFalse(process.running_on('10.0.0.2')) self.assertTrue(process.pid_running_on('10.0.0.1')) self.assertFalse(process.pid_running_on('10.0.0.2')) # test with STOPPING process info = any_process_info_by_state(ProcessStates.STOPPING) process = ProcessStatus(info['group'], info['name'], self.supvisors) process.add_info('10.0.0.1', info) self.assertFalse(process.stopped()) self.assertFalse(process.running()) self.assertFalse(process.running_on('10.0.0.1')) self.assertFalse(process.running_on('10.0.0.2')) self.assertFalse(process.pid_running_on('10.0.0.1')) self.assertFalse(process.pid_running_on('10.0.0.2'))
def test_add_process(self): """ Test the add_process method. """ from supvisors.address import AddressStatus from supvisors.process import ProcessStatus status = AddressStatus('10.0.0.1', self.supvisors.logger) info = any_process_info() process = ProcessStatus(info['group'], info['name'], self.supvisors) status.add_process(process) # check that process is stored self.assertIn(process.namespec(), status.processes.keys()) self.assertIs(process, status.processes[process.namespec()])
def test_pid_process(self): """ Test the pid_process method. """ from supvisors.address import AddressStatus from supvisors.process import ProcessStatus status = AddressStatus('10.0.0.1', self.supvisors.logger) for info in ProcessInfoDatabase: process = ProcessStatus(info['group'], info['name'], self.supvisors) process.add_info('10.0.0.1', info.copy()) status.add_process(process) # check the namespec and pid of the running processes self.assertItemsEqual([('sample_test_1:xfontsel', 80879), ('sample_test_2:yeux_01', 80882)], status.pid_processes())
def test_stopped(self): """ Test the stopped method. """ from supvisors.application import ApplicationStatus from supvisors.process import ProcessStatus application = ApplicationStatus('ApplicationTest', self.supvisors.logger) self.assertTrue(application.stopped()) # add a stopped process info = any_stopped_process_info() process = ProcessStatus(info['group'], info['name'], self.supvisors) process.add_info('10.0.0.1', info) application.add_process(process) application.update_status() self.assertTrue(application.stopped()) # add a running process info = any_running_process_info() process = ProcessStatus(info['group'], info['name'], self.supvisors) process.add_info('10.0.0.1', info) application.add_process(process) application.update_status() self.assertFalse(application.stopped())
def test_update_status(self): """ Test the update of state and running addresses. """ from supervisor.states import ProcessStates from supvisors.process import ProcessStatus # update_status is called in the construction info = any_process_info_by_state(ProcessStates.STOPPED) process = ProcessStatus(info['group'], info['name'], self.supvisors) process.add_info('10.0.0.1', info) self.assertFalse(process.addresses) self.assertEqual(ProcessStates.STOPPED, process.state) self.assertTrue(process.expected_exit) # replace with an EXITED process info process.infos['10.0.0.1'] = any_process_info_by_state( ProcessStates.EXITED) process.update_status('10.0.0.1', ProcessStates.EXITED, False) self.assertFalse(process.addresses) self.assertEqual(ProcessStates.EXITED, process.state) self.assertFalse(process.expected_exit) # add a STARTING process info process.infos['10.0.0.2'] = any_process_info_by_state( ProcessStates.STARTING) process.update_status('10.0.0.2', ProcessStates.STARTING, True) self.assertSetEqual({'10.0.0.2'}, process.addresses) self.assertEqual(ProcessStates.STARTING, process.state) self.assertTrue(process.expected_exit) # add a BACKOFF process info process.infos['10.0.0.3'] = any_process_info_by_state( ProcessStates.BACKOFF) process.update_status('10.0.0.3', ProcessStates.STARTING, True) self.assertSetEqual({'10.0.0.3', '10.0.0.2'}, process.addresses) self.assertEqual(ProcessStates.BACKOFF, process.state) self.assertTrue(process.expected_exit) # replace STARTING process info with RUNNING process.infos['10.0.0.2'] = any_process_info_by_state( ProcessStates.RUNNING) process.update_status('10.0.0.2', ProcessStates.RUNNING, True) self.assertSetEqual({'10.0.0.3', '10.0.0.2'}, process.addresses) self.assertEqual(ProcessStates.RUNNING, process.state) self.assertTrue(process.expected_exit) # replace BACKOFF process info with FATAL process.infos['10.0.0.3'] = any_process_info_by_state( ProcessStates.FATAL) process.update_status('10.0.0.3', ProcessStates.FATAL, False) self.assertSetEqual({'10.0.0.2'}, process.addresses) self.assertEqual(ProcessStates.RUNNING, process.state) self.assertTrue(process.expected_exit) # replace RUNNING process info with STOPPED process.infos['10.0.0.2'] = any_process_info_by_state( ProcessStates.STOPPED) process.update_status('10.0.0.2', ProcessStates.STOPPED, False) self.assertFalse(process.addresses) self.assertEqual(ProcessStates.STOPPED, process.state) self.assertFalse(process.expected_exit)
def test_running_process(self): """ Test the running_process method. """ from supvisors.address import AddressStatus from supvisors.process import ProcessStatus status = AddressStatus('10.0.0.1', self.supvisors.logger) for info in ProcessInfoDatabase: process = ProcessStatus(info['group'], info['name'], self.supvisors) process.add_info('10.0.0.1', info.copy()) status.add_process(process) # check the name of the running processes self.assertItemsEqual(['late_segv','segv', 'xfontsel', 'yeux_01'], [proc.process_name for proc in status.running_processes()])
def test_add_process(self): """ Test the add_process method. """ from supvisors.application import ApplicationStatus from supvisors.process import ProcessStatus application = ApplicationStatus('ApplicationTest', self.supvisors.logger) # add a process to the application info = any_process_info() process = ProcessStatus(info['group'], info['name'], self.supvisors) process.add_info('10.0.0.1', info) application.add_process(process) # check that process is stored self.assertIn(process.process_name, application.processes.keys()) self.assertIs(process, application.processes[process.process_name])
def test_add_info(self): """ Test the addition of a process info into the ProcessStatus. """ from supervisor.states import ProcessStates from supvisors.process import ProcessStatus # ProcessStatus constructor uses add_info info = process_info_by_name('xclock') self.assertNotIn('uptime', info) process = ProcessStatus(info['group'], info['name'], self.supvisors) process.add_info('10.0.0.1', info) # check contents self.assertEqual(1, len(process.infos)) self.assertIs(info, process.infos['10.0.0.1']) self.assertGreater(process.last_event_time, 0) last_event_time = process.last_event_time self.assertIn('uptime', info) self.assertEqual(info['now'] - info['start'], info['uptime']) self.assertFalse(process.addresses) self.assertEqual(ProcessStates.STOPPING, process.state) self.assertTrue(process.expected_exit) # test that address is unchanged self.assertListEqual(['*'], process.rules.addresses) # update rules to test '#' process.rules.addresses = ['#'] # replace with an EXITED process info info = any_process_info_by_state(ProcessStates.EXITED) process.add_info('10.0.0.1', info) # check contents self.assertEqual(1, len(process.infos)) self.assertIs(info, process.infos['10.0.0.1']) self.assertGreaterEqual(process.last_event_time, last_event_time) last_event_time = process.last_event_time self.assertIn('uptime', info) self.assertFalse(process.addresses) self.assertEqual(ProcessStates.EXITED, process.state) self.assertTrue(process.expected_exit) # the firefox process has a procnumber of 0 and address '10.0.0.1' has an index of 1 # address rule remains unchanged self.assertListEqual(['#'], process.rules.addresses) # add a RUNNING process info info = any_process_info_by_state(ProcessStates.RUNNING) process.add_info('10.0.0.2', info) # check contents self.assertEqual(2, len(process.infos)) self.assertIs(info, process.infos['10.0.0.2']) self.assertGreaterEqual(process.last_event_time, last_event_time) self.assertEqual({'10.0.0.2'}, process.addresses) self.assertEqual(ProcessStates.RUNNING, process.state) self.assertTrue(process.expected_exit) # the xfontsel process has a procnumber of 2 and address '10.0.0.2' has an index of 2 # address rule changes to '10.0.0.2' self.assertListEqual(['10.0.0.2'], process.rules.addresses)
def test_invalidate_address(self): """ Test the invalidation of addresses. """ from supervisor.states import ProcessStates from supvisors.process import ProcessStatus # create conflict directly with 3 process info info = any_process_info_by_state(ProcessStates.BACKOFF) process = ProcessStatus(info['group'], info['name'], self.supvisors) process.add_info('10.0.0.1', info) process.add_info('10.0.0.2', any_process_info_by_state(ProcessStates.RUNNING)) process.add_info('10.0.0.3', any_process_info_by_state(ProcessStates.STARTING)) # check the conflict self.assertTrue(process.conflicting()) self.assertEqual(ProcessStates.RUNNING, process.state) # invalidate RUNNING one process.invalidate_address('10.0.0.2') # check UNKNOWN self.assertEqual(ProcessStates.UNKNOWN, process.infos['10.0.0.2']['state']) # check the conflict self.assertTrue(process.conflicting()) # check new synthetic state self.assertEqual(ProcessStates.BACKOFF, process.state) # invalidate BACKOFF one process.invalidate_address('10.0.0.1') # check UNKNOWN self.assertEqual(ProcessStates.UNKNOWN, process.infos['10.0.0.1']['state']) # check 1 address: no conflict self.assertFalse(process.conflicting()) # check state (running process) self.assertEqual(ProcessStates.STARTING, process.state) # invalidate STARTING one process.invalidate_address('10.0.0.3') # check UNKNOWN self.assertEqual(ProcessStates.UNKNOWN, process.infos['10.0.0.3']['state']) # check 0 address: no conflict self.assertFalse(process.conflicting()) # check state FATAL self.assertEqual(ProcessStates.FATAL, process.state) # check mark_for_restart self.assertFalse(process.mark_for_restart) # add one STOPPING process.add_info('10.0.0.4', any_process_info_by_state(ProcessStates.STOPPING)) # check state STOPPING self.assertEqual(ProcessStates.STOPPING, process.state) # invalidate STOPPING one process.invalidate_address('10.0.0.4') # check UNKNOWN self.assertEqual(ProcessStates.UNKNOWN, process.infos['10.0.0.4']['state']) # check state STOPPED self.assertEqual(ProcessStates.STOPPED, process.state)
def test_serialization(self): """ Test the serialization of the ProcessStatus. """ import pickle from supervisor.states import ProcessStates from supvisors.process import ProcessStatus # test with a STOPPED process info = any_process_info_by_state(ProcessStates.STOPPED) process = ProcessStatus(info['group'], info['name'], self.supvisors) process.add_info('10.0.0.1', info) serialized = process.serial() self.assertDictEqual(serialized, {'application_name': info['group'], 'process_name': info['name'], 'statecode': 0, 'statename': 'STOPPED', 'expected_exit': not info['spawnerr'], 'last_event_time': process.last_event_time, 'addresses': []}) # test that returned structure is serializable using pickle dumped = pickle.dumps(serialized) loaded = pickle.loads(dumped) self.assertDictEqual(serialized, loaded)
def test_conflicting(self): """ Test the process conflicting rules. """ from supervisor.states import ProcessStates from supvisors.process import ProcessStatus # when there is only one STOPPED process info, there is no conflict info = any_process_info_by_state(ProcessStates.STOPPED) process = ProcessStatus(info['group'], info['name'], self.supvisors) process.add_info('10.0.0.1', info) self.assertFalse(process.conflicting()) # the addition of a running address, still no conflict process.addresses.add('10.0.0.2') self.assertFalse(process.conflicting()) # the addition of a new running address raises a conflict process.addresses.add('10.0.0.4') self.assertTrue(process.conflicting()) # remove the first running address to solve the conflict process.addresses.remove('10.0.0.2') self.assertFalse(process.conflicting())
def test_add_info(self): """ Test the addition of a process info into the ProcessStatus. """ from supervisor.states import ProcessStates from supvisors.process import ProcessStatus # ProcessStatus constructor uses add_info info = any_process_info_by_state(ProcessStates.STOPPING) self.assertNotIn('event_time', info) self.assertNotIn('local_time', info) self.assertNotIn('uptime', info) process = ProcessStatus(info['group'], info['name'], self.supvisors) process.add_info('10.0.0.1', info) # check contents self.assertEqual(1, len(process.infos)) self.assertIs(info, process.infos['10.0.0.1']) self.assertIsNotNone(process.last_event_time) last_event_time = process.last_event_time self.assertIn('event_time', info) self.assertIn('local_time', info) self.assertIn('uptime', info) self.assertEqual(last_event_time, info['event_time']) self.assertEqual(last_event_time, info['now']) self.assertEqual(info['now'] - info['start'], info['uptime']) self.assertFalse(process.addresses) self.assertEqual(ProcessStates.STOPPING, process.state) self.assertTrue(process.expected_exit) # replace with an EXITED process info info = any_process_info_by_state(ProcessStates.EXITED) process.add_info('10.0.0.1', info) # check contents self.assertEqual(1, len(process.infos)) self.assertIs(info, process.infos['10.0.0.1']) self.assertIsNotNone(process.last_event_time) self.assertNotEqual(last_event_time, process.last_event_time) last_event_time = process.last_event_time self.assertIn('event_time', info) self.assertIn('local_time', info) self.assertIn('uptime', info) self.assertEqual(last_event_time, info['event_time']) self.assertEqual(last_event_time, info['now']) self.assertFalse(process.addresses) self.assertEqual(ProcessStates.EXITED, process.state) self.assertTrue(process.expected_exit) # add a RUNNING process info process.add_info('10.0.0.2', any_process_info_by_state(ProcessStates.RUNNING))
def test_loading(self): """ Test the loading method. """ from supvisors.address import AddressStatus from supvisors.process import ProcessStatus status = AddressStatus('10.0.0.1', self.supvisors.logger) for info in ProcessInfoDatabase: process = ProcessStatus(info['group'], info['name'], self.supvisors) process.add_info('10.0.0.1', info.copy()) status.add_process(process) # check the loading of the address: gives 5 (1 per running process) by default because no rule has been loaded self.assertEqual(4, status.loading()) # change expected_loading of any stopped process process = random.choice([proc for proc in status.processes.values() if proc.stopped()]) process.rules.expected_loading = 50 self.assertEqual(4, status.loading()) # change expected_loading of any running process process = random.choice([proc for proc in status.processes.values() if proc.running()]) process.rules.expected_loading = 50 self.assertEqual(53, status.loading())
def test_create(self): """ Test the values set at construction. """ from supervisor.states import ProcessStates from supvisors.process import ProcessRules, ProcessStatus info = any_stopped_process_info() process = ProcessStatus(info['group'], info['name'], self.supvisors) # check application default attributes self.assertIs(self.supvisors, process.supvisors) self.assertEqual(info['group'], process.application_name) self.assertEqual(info['name'], process.process_name) self.assertEqual(ProcessStates.UNKNOWN, process.state) self.assertTrue(process.expected_exit) self.assertEqual(0, process.last_event_time) self.assertEqual(set(), process.addresses) self.assertEqual({}, process.infos) self.assertEqual('', process.extra_args) self.assertFalse(process.ignore_wait_exit) # rules part self.assertDictEqual( ProcessRules(self.supvisors).__dict__, process.rules.__dict__)
def check_valid(self, parser): """ Test the parsing of a valid XML. """ from supvisors.application import ApplicationStatus from supvisors.process import ProcessStatus from supvisors.ttypes import RunningFailureStrategies, StartingFailureStrategies # test models & patterns self.assertItemsEqual([ 'dummy_model_01', 'dummy_model_02', 'dummy_model_03', 'dummy_model_04' ], parser.models.keys()) self.assertItemsEqual(['dummies_', 'dummies_01_', 'dummies_02_'], parser.patterns.keys()) # check unknown application application = ApplicationStatus('dummy_application_X', self.supvisors.logger) parser.load_application_rules(application) self.assert_default_application_rules(application.rules) # check first application application = ApplicationStatus('dummy_application_A', self.supvisors.logger) parser.load_application_rules(application) self.assert_default_application_rules(application.rules) # check second application application = ApplicationStatus('dummy_application_B', self.supvisors.logger) parser.load_application_rules(application) self.assert_application_rules(application.rules, 1, 4, StartingFailureStrategies.STOP, RunningFailureStrategies.RESTART_PROCESS) # check third application application = ApplicationStatus('dummy_application_C', self.supvisors.logger) parser.load_application_rules(application) self.assert_application_rules( application.rules, 20, 0, StartingFailureStrategies.ABORT, RunningFailureStrategies.STOP_APPLICATION) # check fourth application application = ApplicationStatus('dummy_application_D', self.supvisors.logger) parser.load_application_rules(application) self.assert_application_rules( application.rules, 0, 100, StartingFailureStrategies.CONTINUE, RunningFailureStrategies.RESTART_APPLICATION) # check program from unknown application: all default process = ProcessStatus('dummy_application_X', 'dummy_program_X0', self.supvisors) parser.load_process_rules(process) self.assert_default_process_rules(process.rules) # check unknown program from known application: all default process = ProcessStatus('dummy_application_A', 'dummy_program_A0', self.supvisors) parser.load_process_rules(process) self.assert_default_process_rules(process.rules) # check known program from known but not related application: all default process = ProcessStatus('dummy_application_A', 'dummy_program_B1', self.supvisors) parser.load_process_rules(process) self.assert_default_process_rules(process.rules) # check known empty program process = ProcessStatus('dummy_application_B', 'dummy_program_B0', self.supvisors) parser.load_process_rules(process) self.assert_default_process_rules(process.rules) # check dash addresses and valid other values process = ProcessStatus('dummy_application_B', 'dummy_program_B1', self.supvisors) parser.load_process_rules(process) self.assert_process_rules(process.rules, ['#'], 3, 50, True, False, 5, RunningFailureStrategies.CONTINUE) # check single address with required not applicable and out of range loading process = ProcessStatus('dummy_application_B', 'dummy_program_B2', self.supvisors) parser.load_process_rules(process) self.assert_process_rules(process.rules, ['10.0.0.3'], 0, 0, False, False, 1, RunningFailureStrategies.RESTART_PROCESS) # check wildcard address, optional and max loading process = ProcessStatus('dummy_application_B', 'dummy_program_B3', self.supvisors) parser.load_process_rules(process) self.assert_process_rules(process.rules, ['*'], 0, 0, False, False, 100, RunningFailureStrategies.STOP_APPLICATION) # check multiple addresses, all other incorrect values process = ProcessStatus('dummy_application_B', 'dummy_program_B4', self.supvisors) parser.load_process_rules(process) self.assert_process_rules(process.rules, ['10.0.0.3', '10.0.0.1', '10.0.0.5'], 0, 0, False, False, 1, RunningFailureStrategies.RESTART_APPLICATION) # check empty reference process = ProcessStatus('dummy_application_C', 'dummy_program_C0', self.supvisors) parser.load_process_rules(process) self.assert_default_process_rules(process.rules) # check unknown reference process = ProcessStatus('dummy_application_C', 'dummy_program_C1', self.supvisors) parser.load_process_rules(process) self.assert_default_process_rules(process.rules) # check known reference process = ProcessStatus('dummy_application_C', 'dummy_program_C2', self.supvisors) parser.load_process_rules(process) self.assert_process_rules(process.rules, ['*'], 0, 0, False, False, 25, RunningFailureStrategies.STOP_APPLICATION) # check other known reference process = ProcessStatus('dummy_application_C', 'dummy_program_C3', self.supvisors) parser.load_process_rules(process) self.assert_process_rules(process.rules, ['#'], 1, 0, True, True, 1, RunningFailureStrategies.CONTINUE) # check pattern with single matching and reference process = ProcessStatus('dummy_application_D', 'dummies_any', self.supvisors) parser.load_process_rules(process) self.assert_process_rules(process.rules, ['10.0.0.4', '10.0.0.2'], 0, 100, False, False, 10, RunningFailureStrategies.CONTINUE) # check pattern with multiple matching and configuration process = ProcessStatus('dummy_application_D', 'dummies_01_any', self.supvisors) parser.load_process_rules(process) self.assert_process_rules(process.rules, ['#'], 1, 1, False, True, 75, RunningFailureStrategies.CONTINUE) # check pattern with multiple matching and incorrect reference process = ProcessStatus('dummy_application_D', 'any_dummies_02_', self.supvisors) parser.load_process_rules(process) self.assert_default_process_rules(process.rules)
def test_update_status(self): """ Test the rules to update the status of the application method. """ from supervisor.states import ProcessStates from supvisors.application import ApplicationStatus from supvisors.process import ProcessStatus from supvisors.ttypes import ApplicationStates application = ApplicationStatus('ApplicationTest', self.supvisors.logger) # add processes to the application for info in ProcessInfoDatabase: process = ProcessStatus(info['group'], info['name'], self.supvisors) process.add_info('10.0.0.1', info.copy()) application.add_process(process) # init status # there are lots of states but the 'strongest' is STARTING # STARTING is a 'running' state so major/minor failures are applicable application.update_status() self.assertEqual(ApplicationStates.STARTING, application.state) # there is a FATAL state in the process database # no rule is set for processes, so there are only minor failures self.assertFalse(application.major_failure) self.assertTrue(application.minor_failure) # set FATAL process to major fatal_process = next((process for process in application.processes.values() if process.state == ProcessStates.FATAL), None) fatal_process.rules.required = True # update status. major failure is now expected application.update_status() self.assertEqual(ApplicationStates.STARTING, application.state) self.assertTrue(application.major_failure) self.assertFalse(application.minor_failure) # set STARTING process to RUNNING starting_process = next( (process for process in application.processes.values() if process.state == ProcessStates.STARTING), None) starting_process.state = ProcessStates.RUNNING # update status. there is still one BACKOFF process leading to STARTING application application.update_status() self.assertEqual(ApplicationStates.STARTING, application.state) self.assertTrue(application.major_failure) self.assertFalse(application.minor_failure) # set BACKOFF process to EXITED backoff_process = next( (process for process in application.processes.values() if process.state == ProcessStates.BACKOFF), None) backoff_process.state = ProcessStates.EXITED # update status. the 'strongest' state is now STOPPING # as STOPPING is not a 'running' state, failures are not applicable application.update_status() self.assertEqual(ApplicationStates.STOPPING, application.state) self.assertFalse(application.major_failure) self.assertFalse(application.minor_failure) # set STOPPING process to STOPPED stopping_process = next( (process for process in application.processes.values() if process.state == ProcessStates.STOPPING), None) stopping_process.state = ProcessStates.STOPPED # update status. the 'strongest' state is now RUNNING # failures are applicable again application.update_status() self.assertEqual(ApplicationStates.RUNNING, application.state) self.assertTrue(application.major_failure) self.assertFalse(application.minor_failure) # set RUNNING processes to STOPPED for process in application.processes.values(): if process.state == ProcessStates.RUNNING: process.state = ProcessStates.STOPPED # update status. the 'strongest' state is now RUNNING # failures are not applicable anymore application.update_status() self.assertEqual(ApplicationStates.STOPPED, application.state) self.assertFalse(application.major_failure) self.assertFalse(application.minor_failure)
def test_deployment_state(self): """ Test the Deployment state of the FSM. """ from supvisors.application import ApplicationStatus from supvisors.process import ProcessStatus from supvisors.statemachine import AbstractState, DeploymentState from supvisors.ttypes import ApplicationStates, SupvisorsStates state = DeploymentState(self.supvisors) self.assertIsInstance(state, AbstractState) # test enter method # test that start_applications is called only when local address is the master address with patch.object(self.supvisors.starter, 'start_applications') as mocked_starter: self.supvisors.context.master = False state.enter() self.assertEqual(0, mocked_starter.call_count) # now master address is local self.supvisors.context.master = True state.enter() self.assertEqual(1, mocked_starter.call_count) # create application context application = ApplicationStatus('sample_test_2', self.supvisors.logger) self.supvisors.context.applications['sample_test_2'] = application for info in database_copy(): if info['group'] == 'sample_test_2': process = ProcessStatus(info['group'], info['name'], self.supvisors) process.rules.start_sequence = len(process.namespec()) % 3 process.rules.stop_sequence = len(process.namespec()) % 3 + 1 process.add_info('10.0.0.1', info) application.add_process(process) # test application updates self.supvisors.context.master = False self.assertEqual(ApplicationStates.STOPPED, application.state) self.assertFalse(application.minor_failure) self.assertFalse(application.major_failure) self.assertDictEqual({}, application.start_sequence) self.assertDictEqual({}, application.stop_sequence) state.enter() application = self.supvisors.context.applications['sample_test_2'] self.assertEqual(ApplicationStates.RUNNING, application.state) self.assertTrue(application.minor_failure) self.assertFalse(application.major_failure) # list order may differ, so break down self.assertItemsEqual([0, 1], application.start_sequence.keys()) self.assertItemsEqual([ application.processes['yeux_01'], application.processes['yeux_00'] ], application.start_sequence[0]) self.assertItemsEqual([application.processes['sleep']], application.start_sequence[1]) self.assertItemsEqual([1, 2], application.stop_sequence.keys()) self.assertItemsEqual([ application.processes['yeux_01'], application.processes['yeux_00'] ], application.stop_sequence[1]) self.assertItemsEqual([application.processes['sleep']], application.stop_sequence[2]) # test next method # stay in DEPLOYMENT if local is master and a deployment is in progress, whatever the conflict status with patch.object(self.supvisors.starter, 'check_starting', return_value=False): for conflict in [True, False]: with patch.object(self.supvisors.context, 'conflicting', return_value=conflict): self.supvisors.context.master = True result = state.next() self.assertEqual(SupvisorsStates.DEPLOYMENT, result) # return OPERATION if local is not master and no conflict, whatever the starting status self.supvisors.context.master = False with patch.object(self.supvisors.context, 'conflicting', return_value=False): for starting in [True, False]: with patch.object(self.supvisors.starter, 'check_starting', return_value=starting): result = state.next() self.assertEqual(SupvisorsStates.OPERATION, result) # return OPERATION if a deployment is in progress and no conflict, whatever the master status with patch.object(self.supvisors.starter, 'check_starting', return_value=True): with patch.object(self.supvisors.context, 'conflicting', return_value=False): for master in [True, False]: self.supvisors.context.master = master result = state.next() self.assertEqual(SupvisorsStates.OPERATION, result) # return CONCILIATION if local is not master and conflict detected, whatever the starting status self.supvisors.context.master = False with patch.object(self.supvisors.context, 'conflicting', return_value=True): for starting in [True, False]: with patch.object(self.supvisors.starter, 'check_starting', return_value=starting): result = state.next() self.assertEqual(SupvisorsStates.CONCILIATION, result) # return CONCILIATION if a deployment is in progress and conflict detected, whatever the master status with patch.object(self.supvisors.starter, 'check_starting', return_value=True): with patch.object(self.supvisors.context, 'conflicting', return_value=True): for master in [True, False]: self.supvisors.context.master = master result = state.next() self.assertEqual(SupvisorsStates.CONCILIATION, result) # no exit implementation. just call it without test state.exit()
def test_update_info(self): """ Test the update of the ProcessStatus upon reception of a process event. """ from supervisor.states import ProcessStates from supvisors.process import ProcessStatus # add a STOPPED process infos into a process status info = any_process_info_by_state(ProcessStates.STOPPED) process = ProcessStatus(info['group'], info['name'], self.supvisors) process.add_info('10.0.0.1', info) self.assertEqual(ProcessStates.STOPPED, process.infos['10.0.0.1']['state']) self.assertEqual(ProcessStates.STOPPED, process.state) self.assertFalse(process.addresses) local_time = process.infos['10.0.0.1']['local_time'] # update with a STARTING event on an unknown address process.update_info('10.0.0.2', { 'state': ProcessStates.STARTING, 'now': 10 }) # check no change info = process.infos['10.0.0.1'] self.assertEqual(ProcessStates.STOPPED, process.infos['10.0.0.1']['state']) self.assertEqual(ProcessStates.STOPPED, process.state) self.assertFalse(process.addresses) # update with a STARTING event process.update_info('10.0.0.1', { 'state': ProcessStates.STARTING, 'now': 10 }) # check changes info = process.infos['10.0.0.1'] self.assertEqual(ProcessStates.STARTING, info['state']) self.assertEqual(ProcessStates.STARTING, process.state) self.assertSetEqual({'10.0.0.1'}, process.addresses) self.assertEqual(10, process.last_event_time) self.assertEqual(10, info['event_time']) self.assertEqual(10, info['now']) self.assertGreaterEqual(info['local_time'], local_time) local_time = info['local_time'] self.assertEqual(10, info['start']) self.assertEqual(0, info['stop']) self.assertEqual(0, info['uptime']) # update with a RUNNING event process.update_info('10.0.0.1', { 'state': ProcessStates.RUNNING, 'now': 15, 'pid': 1234 }) # check changes self.assertEqual(ProcessStates.RUNNING, info['state']) self.assertEqual(ProcessStates.RUNNING, process.state) self.assertSetEqual({'10.0.0.1'}, process.addresses) self.assertEqual(15, process.last_event_time) self.assertEqual(1234, info['pid']) self.assertEqual(15, info['event_time']) self.assertEqual(15, info['now']) self.assertGreaterEqual(info['local_time'], local_time) local_time = info['local_time'] self.assertEqual(10, info['start']) self.assertEqual(0, info['stop']) self.assertEqual(5, info['uptime']) # add a new STOPPED process info and update with STARTING / RUNNING events process.add_info('10.0.0.2', any_process_info_by_state(ProcessStates.STOPPED)) process.update_info('10.0.0.2', { 'state': ProcessStates.STARTING, 'now': 20 }) process.update_info('10.0.0.2', { 'state': ProcessStates.RUNNING, 'now': 25, 'pid': 4321 }) # check state and addresses self.assertEqual(ProcessStates.RUNNING, process.state) self.assertSetEqual({'10.0.0.1', '10.0.0.2'}, process.addresses) self.assertEqual(25, process.last_event_time) # update with an EXITED event process.update_info('10.0.0.1', { 'state': ProcessStates.EXITED, 'now': 30, 'expected': False }) # check changes self.assertEqual(ProcessStates.EXITED, info['state']) self.assertEqual(ProcessStates.RUNNING, process.state) self.assertSetEqual({'10.0.0.2'}, process.addresses) self.assertEqual(30, process.last_event_time) self.assertEqual(0, info['pid']) self.assertEqual(30, info['event_time']) self.assertEqual(30, info['now']) self.assertGreaterEqual(info['local_time'], local_time) self.assertEqual(10, info['start']) self.assertEqual(30, info['stop']) self.assertEqual(0, info['uptime']) self.assertFalse(info['expected']) # update with an STOPPING event info = process.infos['10.0.0.2'] local_time = info['local_time'] process.update_info('10.0.0.2', { 'state': ProcessStates.STOPPING, 'now': 35 }) # check changes self.assertEqual(ProcessStates.STOPPING, info['state']) self.assertEqual(ProcessStates.STOPPING, process.state) self.assertSetEqual({'10.0.0.2'}, process.addresses) self.assertEqual(35, process.last_event_time) self.assertEqual(4321, info['pid']) self.assertEqual(35, info['event_time']) self.assertEqual(35, info['now']) self.assertGreaterEqual(info['local_time'], local_time) local_time = info['local_time'] self.assertEqual(20, info['start']) self.assertEqual(0, info['stop']) self.assertEqual(15, info['uptime']) self.assertTrue(info['expected']) # update with an STOPPED event process.update_info('10.0.0.2', { 'state': ProcessStates.STOPPED, 'now': 40 }) # check changes self.assertEqual(ProcessStates.STOPPED, info['state']) self.assertEqual(ProcessStates.STOPPED, process.state) self.assertFalse(process.addresses) self.assertEqual(40, process.last_event_time) self.assertEqual(0, info['pid']) self.assertEqual(40, info['event_time']) self.assertEqual(40, info['now']) self.assertGreaterEqual(info['local_time'], local_time) self.assertEqual(20, info['start']) self.assertEqual(40, info['stop']) self.assertEqual(0, info['uptime']) self.assertTrue(info['expected'])