From 83478c8c4e5fe3bb2657e593891698fbeb9d355a Mon Sep 17 00:00:00 2001 From: Veselin Penev Date: Sat, 28 Sep 2024 10:03:32 +0200 Subject: [PATCH 1/2] deleted: bitdust/automats/global_state.py --- bitdust/automats/global_state.py | 127 -------------------- bitdust/contacts/contactsdb.py | 1 - bitdust/customer/fire_hire.py | 2 - bitdust/p2p/network_connector.py | 3 +- bitdust/services/service_backups.py | 1 - bitdust/services/service_keys_registry.py | 1 - bitdust/services/service_message_history.py | 25 ---- bitdust/services/service_my_data.py | 2 - bitdust/services/service_p2p_hookups.py | 1 - bitdust/storage/backup_rebuilder.py | 1 - 10 files changed, 1 insertion(+), 163 deletions(-) delete mode 100644 bitdust/automats/global_state.py diff --git a/bitdust/automats/global_state.py b/bitdust/automats/global_state.py deleted file mode 100644 index ecd8ea989..000000000 --- a/bitdust/automats/global_state.py +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/env python -# global_state.py -# -# Copyright (C) 2008 Veselin Penev, https://bitdust.io -# -# This file (global_state.py) is part of BitDust Software. -# -# BitDust is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# BitDust Software is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with BitDust Software. If not, see . -# -# Please contact us if you have any questions at bitdust.io@gmail.com -# -# -# -# -# -""" -.. module:: global_state. - -This module is to keep track of changing states of State Machines. -It also remember the current ``global`` state of the program - this a stats of a several most important automats. -""" - -from __future__ import absolute_import - -#------------------------------------------------------------------------------ - -from bitdust.logs import lg - -from bitdust.automats import automat - -#------------------------------------------------------------------------------ - -_StatesDict = { - 'init at startup': 'beginning', - 'init local': 'local settings initialization', - 'init contacts': 'contacts initialization', - 'init connection': 'initializing connections', - 'init modules': 'starting modules', - 'init install': 'preparing install section', - 'network at startup': 'starting connection', - 'network stun': 'detecting external IP address', - 'network upnp': 'configuring UPnP', - 'network connected': 'internet connection is fine', - 'network disconnected': 'internet connection is not working', - 'network network?': 'checking network interfaces', - 'network google?': 'is www.google.com available?', - 'p2p at startup': 'initial peer-to-peer state', - 'p2p transports': 'starting network transports', - 'p2p propagate': 'propagate my identity', - 'p2p incomming?': 'waiting response from others', - 'p2p connected': 'ready', - 'p2p disconnected': 'starting disconnected', -} - -_GlobalState = 'AT_STARTUP' -_GlobalStateNotifyFunc = None - -#------------------------------------------------------------------------------ - - -def set_global_state(st): - """ - This method is called from State Machines when ``state`` is changed: - global_state.set_global_state('P2P ' + newstate) So ``st`` is a string - like: 'P2P CONNECTED'. - - ``_GlobalStateNotifyFunc`` can be used to keep track of changing - program states. - """ - global _GlobalState - global _GlobalStateNotifyFunc - oldstate = _GlobalState - _GlobalState = st - # lg.out(6, (' ' * 40) + '{%s}->{%s}' % (oldstate, _GlobalState)) - if _GlobalStateNotifyFunc is not None and oldstate != _GlobalState: - try: - _GlobalStateNotifyFunc(_GlobalState) - except: - lg.exc() - - -def get_global_state(): - """ - Return the current ``global state``, for example: P2P CONNECTED. - """ - global _GlobalState - # lg.out(6, 'global_state.get_global_state return [%s]' % _GlobalState) - return _GlobalState - - -def get_global_state_label(): - """ - Return a label describing current global state, for example: 'checking - network interfaces'. - """ - global _GlobalState - global _StatesDict - return _StatesDict.get(_GlobalState.replace('_', ' ').lower(), '') - - -def SetGlobalStateNotifyFunc(f): - """ - Set callback to catch a global state changed event. - """ - global _GlobalStateNotifyFunc - _GlobalStateNotifyFunc = f - - -def SetSingleStateNotifyFunc(f): - """ - Set callback to catch state change of any automat. - """ - automat.SetStateChangedCallback(f) - - -#------------------------------------------------------------------------------ diff --git a/bitdust/contacts/contactsdb.py b/bitdust/contacts/contactsdb.py index 73e03fdd1..ba72fc192 100644 --- a/bitdust/contacts/contactsdb.py +++ b/bitdust/contacts/contactsdb.py @@ -847,7 +847,6 @@ def load_contacts(): _CorrespondentsChangedCallback([], correspondents()) AddContactsChangedCallback(on_contacts_changed) if listeners.is_populate_required('correspondent'): - # listeners.populate_later().remove('correspondent') populate_correspondents() diff --git a/bitdust/customer/fire_hire.py b/bitdust/customer/fire_hire.py index 1a19d07fd..385ba1ef9 100644 --- a/bitdust/customer/fire_hire.py +++ b/bitdust/customer/fire_hire.py @@ -120,7 +120,6 @@ from bitdust.logs import lg -from bitdust.automats import global_state from bitdust.automats import automat from bitdust.lib import misc @@ -267,7 +266,6 @@ def state_changed(self, oldstate, newstate, event, *args, **kwargs): """ This method intended to catch the moment when automat's state was changed. """ - global_state.set_global_state('FIREHIRE ' + newstate) if newstate == 'READY' and event != 'instant': self.automat('instant') diff --git a/bitdust/p2p/network_connector.py b/bitdust/p2p/network_connector.py index b2afaf0fa..7c7e7fe9d 100644 --- a/bitdust/p2p/network_connector.py +++ b/bitdust/p2p/network_connector.py @@ -90,7 +90,6 @@ from bitdust.logs import lg from bitdust.automats import automat -from bitdust.automats import global_state from bitdust.lib import net_misc from bitdust.lib import misc @@ -146,6 +145,7 @@ def Destroy(): class NetworkConnector(automat.Automat): + """ Class to monitor Internet connection and reconnect when needed. """ @@ -174,7 +174,6 @@ def init(self): net_misc.SetConnectionFailedCallbackFunc(ConnectionFailedCallback) def state_changed(self, oldstate, newstate, event, *args, **kwargs): - global_state.set_global_state('NETWORK ' + newstate) if driver.is_on('service_p2p_hookups'): from bitdust.p2p import p2p_connector from bitdust.system import tray_icon diff --git a/bitdust/services/service_backups.py b/bitdust/services/service_backups.py index 962d68687..eb5634c2a 100644 --- a/bitdust/services/service_backups.py +++ b/bitdust/services/service_backups.py @@ -73,7 +73,6 @@ def start(self): events.add_subscriber(self._on_my_identity_rotated, 'my-identity-rotated') events.add_subscriber(self._on_key_erased, 'key-erased') if listeners.is_populate_required('remote_version'): - # listeners.populate_later().remove('remote_version') backup_matrix.populate_remote_versions() return True diff --git a/bitdust/services/service_keys_registry.py b/bitdust/services/service_keys_registry.py index 99d8afd53..0df67f798 100644 --- a/bitdust/services/service_keys_registry.py +++ b/bitdust/services/service_keys_registry.py @@ -56,7 +56,6 @@ def start(self): callback.add_outbox_callback(self._on_outbox_packet_sent) callback.append_inbox_callback(self._on_inbox_packet_received) if listeners.is_populate_required('key'): - # listeners.populate_later().remove('key') my_keys.populate_keys() return True diff --git a/bitdust/services/service_message_history.py b/bitdust/services/service_message_history.py index 6aef3b11b..90582be96 100644 --- a/bitdust/services/service_message_history.py +++ b/bitdust/services/service_message_history.py @@ -60,10 +60,8 @@ def start(self): events.add_subscriber(self.on_key_generated, 'key-generated') events.add_subscriber(self.on_key_erased, 'key-erased') if listeners.is_populate_required('conversation'): - # listeners.populate_later().remove('conversation') message_database.populate_conversations() if listeners.is_populate_required('message'): - # listeners.populate_later().remove('message') message_database.populate_messages() return True @@ -100,26 +98,3 @@ def on_key_erased(self, evt): if evt.data['key_id'].startswith('group_'): conversation_id = message_database.get_conversation_id(evt.data['local_key_id'], evt.data['local_key_id'], 3) listeners.push_snapshot('conversation', snap_id=conversation_id, deleted=True) - - # def do_check_create_rename_key(self, new_key_id): - # from bitdust.logs import lg - # from bitdust.crypt import my_keys - # from bitdust.chat import message_database - # try: - # new_public_key = my_keys.get_public_key_raw(new_key_id) - # except: - # lg.exc() - # return - # try: - # new_local_key_id = my_keys.get_local_key_id(new_key_id) - # except: - # lg.exc() - # return - # if new_local_key_id is None: - # lg.err('did not found local_key_id for %r' % new_key_id) - # return - # message_database.check_create_rename_key( - # new_public_key=new_public_key, - # new_key_id=new_key_id, - # new_local_key_id=new_local_key_id, - # ) diff --git a/bitdust/services/service_my_data.py b/bitdust/services/service_my_data.py index de6d7bfa2..0b1c77de7 100644 --- a/bitdust/services/service_my_data.py +++ b/bitdust/services/service_my_data.py @@ -70,7 +70,6 @@ def start(self): if keys_synchronizer.is_synchronized() and index_synchronizer.is_synchronized(): self.confirm_service_started(result=True) if listeners.is_populate_required('private_file'): - # listeners.populate_later().remove('private_file') backup_fs.populate_private_files() else: lg.warn('can not start service_my_data right now, keys_synchronizer.is_synchronized=%r index_synchronizer.is_synchronized=%r' % (keys_synchronizer.is_synchronized(), index_synchronizer.is_synchronized())) @@ -90,7 +89,6 @@ def _on_my_storage_ready(self, evt): if self.starting_deferred: self.confirm_service_started(result=True) if listeners.is_populate_required('private_file'): - # listeners.populate_later().remove('private_file') backup_fs.populate_private_files() if driver.is_enabled('service_my_data'): if not driver.is_started('service_my_data'): diff --git a/bitdust/services/service_p2p_hookups.py b/bitdust/services/service_p2p_hookups.py index a30f68b3c..0512718d5 100644 --- a/bitdust/services/service_p2p_hookups.py +++ b/bitdust/services/service_p2p_hookups.py @@ -76,7 +76,6 @@ def start(self): events.add_subscriber(self._on_identity_url_changed, 'identity-url-changed') events.add_subscriber(self._on_my_identity_url_changed, 'my-identity-url-changed') if listeners.is_populate_required('online_status'): - # listeners.populate_later().remove('online_status') online_status.populate_online_statuses() return True diff --git a/bitdust/storage/backup_rebuilder.py b/bitdust/storage/backup_rebuilder.py index 7f147d7bf..0206aaca6 100644 --- a/bitdust/storage/backup_rebuilder.py +++ b/bitdust/storage/backup_rebuilder.py @@ -184,7 +184,6 @@ def state_changed(self, oldstate, newstate, event, *args, **kwargs): Need to notify backup_monitor() machine about my new state. """ - # global_state.set_global_state('REBUILD ' + newstate) data = self.to_json() if newstate in ['STOPPED', 'DONE']: data['rebuilding'] = False From 2c6a2c7e85a50e02299da3bb0033e2f649db279f Mon Sep 17 00:00:00 2001 From: Veselin Penev Date: Sat, 28 Sep 2024 21:47:46 +0200 Subject: [PATCH 2/2] various improvements in shared_access_coordinator(), restore_worker() and service_message_history() --- bitdust/access/shared_access_coordinator.py | 33 +++++++++++++-------- bitdust/services/service_message_history.py | 1 - bitdust/storage/restore_worker.py | 8 +++-- regress/scenarios.py | 17 ++++++++++- regress/tests/stream/conf.json | 18 +++++++++++ regress/tests/stream/docker-compose.yml | 11 +++++++ regress/tests/stream/test_stream.py | 6 ++-- regress/testsupport.py | 16 +++++----- 8 files changed, 83 insertions(+), 27 deletions(-) diff --git a/bitdust/access/shared_access_coordinator.py b/bitdust/access/shared_access_coordinator.py index 239fcce02..b231d1e14 100644 --- a/bitdust/access/shared_access_coordinator.py +++ b/bitdust/access/shared_access_coordinator.py @@ -488,6 +488,7 @@ def __init__(self, key_id, debug_level=_DebugLevel, log_events=_Debug, log_trans self.customer_idurl = self.glob_id['idurl'] self.known_suppliers_list = [] self.known_ecc_map = None + self.critical_suppliers_number = 1 self.dht_lookup_use_cache = True self.received_index_file_revision = {} self.last_time_in_sync = -1 @@ -595,18 +596,12 @@ def A(self, event, *args, **kwargs): if event == 'shutdown': self.state = 'CLOSED' self.doDestroyMe(*args, **kwargs) - elif event == 'timer-30sec' or event == 'all-suppliers-disconnected': - self.state = 'DISCONNECTED' - self.doReportDisconnected(*args, **kwargs) elif event == 'list-files-received': self.doSupplierSendIndexFile(*args, **kwargs) elif event == 'key-not-registered': self.doSupplierTransferPubKey(*args, **kwargs) elif event == 'supplier-connected' or event == 'key-sent': self.doSupplierRequestIndexFile(*args, **kwargs) - elif event == 'all-suppliers-connected': - self.state = 'CONNECTED' - self.doReportConnected(*args, **kwargs) elif event == 'index-sent' or event == 'index-up-to-date' or event == 'index-failed' or event == 'list-files-failed' or event == 'supplier-failed': self.doRemember(event, *args, **kwargs) self.doCheckAllConnected(*args, **kwargs) @@ -614,6 +609,12 @@ def A(self, event, *args, **kwargs): self.doSupplierProcessListFiles(*args, **kwargs) elif event == 'supplier-file-modified' or event == 'index-received' or event == 'index-missing': self.doSupplierRequestListFiles(event, *args, **kwargs) + elif (event == 'timer-30sec' and not self.isEnoughConnected(*args, **kwargs)) or event == 'all-suppliers-disconnected': + self.state = 'DISCONNECTED' + self.doReportDisconnected(*args, **kwargs) + elif (event == 'timer-30sec' and self.isEnoughConnected(*args, **kwargs)) or event == 'all-suppliers-connected': + self.state = 'CONNECTED' + self.doReportConnected(*args, **kwargs) #---DISCONNECTED--- elif self.state == 'DISCONNECTED': if event == 'shutdown': @@ -639,6 +640,14 @@ def A(self, event, *args, **kwargs): pass return None + def isEnoughConnected(self, *args, **kwargs): + """ + Action method. + """ + if _Debug: + lg.args(_DebugLevel, progress=len(self.suppliers_in_progress), succeed=self.suppliers_succeed, critical_suppliers_number=self.critical_suppliers_number) + return len(self.suppliers_succeed) >= self.critical_suppliers_number + def doInit(self, *args, **kwargs): """ Action method. @@ -667,6 +676,10 @@ def doConnectCustomerSuppliers(self, *args, **kwargs): self.automat('all-suppliers-disconnected') return self.known_ecc_map = args[0].get('ecc_map') + self.critical_suppliers_number = 1 + if self.known_ecc_map: + from bitdust.raid import eccmap + self.critical_suppliers_number = eccmap.GetCorrectableErrors(eccmap.GetEccMapSuppliersNumber(self.known_ecc_map)) self.suppliers_in_progress.clear() self.suppliers_succeed.clear() for supplier_idurl in self.known_suppliers_list: @@ -811,14 +824,10 @@ def doCheckAllConnected(self, *args, **kwargs): """ Action method. """ - critical_suppliers_number = 1 - if self.known_ecc_map: - from bitdust.raid import eccmap - critical_suppliers_number = eccmap.GetCorrectableErrors(eccmap.GetEccMapSuppliersNumber(self.known_ecc_map)) if _Debug: - lg.args(_DebugLevel, progress=len(self.suppliers_in_progress), succeed=self.suppliers_succeed, critical_suppliers_number=critical_suppliers_number) + lg.args(_DebugLevel, progress=len(self.suppliers_in_progress), succeed=self.suppliers_succeed, critical_suppliers_number=self.critical_suppliers_number) if len(self.suppliers_in_progress) == 0: - if len(self.suppliers_succeed) >= critical_suppliers_number: + if len(self.suppliers_succeed) >= self.critical_suppliers_number: self.automat('all-suppliers-connected') else: self.automat('all-suppliers-disconnected') diff --git a/bitdust/services/service_message_history.py b/bitdust/services/service_message_history.py index 90582be96..fd2bd79bd 100644 --- a/bitdust/services/service_message_history.py +++ b/bitdust/services/service_message_history.py @@ -44,7 +44,6 @@ class MessageHistoryService(LocalService): def dependent_on(self): return [ - 'service_my_data', 'service_private_groups', ] diff --git a/bitdust/storage/restore_worker.py b/bitdust/storage/restore_worker.py index 7a1ad14c6..ab59e1668 100644 --- a/bitdust/storage/restore_worker.py +++ b/bitdust/storage/restore_worker.py @@ -176,7 +176,7 @@ def __init__(self, BackupID, OutputFile, KeyID=None, ecc_map=None, debug_level=_ self.blockRestoredCallback = None self.Attempts = 0 - super(RestoreWorker, self).__init__(name='restore_worker_%s' % self.version, state='AT_STARTUP', debug_level=debug_level, log_events=log_events, log_transitions=log_transitions, publish_events=publish_events, **kwargs) + super(RestoreWorker, self).__init__(name='restore_%s' % self.version, state='AT_STARTUP', debug_level=debug_level, log_events=log_events, log_transitions=log_transitions, publish_events=publish_events, **kwargs) events.send('restore-started', data=dict(backup_id=self.backup_id)) def set_packet_in_callback(self, cb): @@ -377,7 +377,11 @@ def doInit(self, *args, **kwargs): num_suppliers = settings.DefaultDesiredSuppliers() self.EccMap = eccmap.eccmap(eccmap.GetEccMapName(num_suppliers)) lg.warn('no meta info found, guessed ECC map %r from %d known suppliers' % (self.EccMap, len(self.known_suppliers))) - self.max_errors = eccmap.GetCorrectableErrors(self.EccMap.NumSuppliers()) + # TODO: here we multiply by two because we have always two packets for each fragment: Data and Parity + # so number of possible errors can be two times larger + # however we may also add another check here to identify dead suppliers as well + # and dead suppliers number must be lower than "max_errors" in order restore to continue + self.max_errors = eccmap.GetCorrectableErrors(self.EccMap.NumSuppliers())*2 if data_receiver.A(): data_receiver.A().addStateChangedCallback(self._on_data_receiver_state_changed) diff --git a/regress/scenarios.py b/regress/scenarios.py index 997539af4..3af56a858 100644 --- a/regress/scenarios.py +++ b/regress/scenarios.py @@ -102,6 +102,11 @@ 'supplier-1', 'supplier-2', ] +SUPPLIERS_IDS_123 = [ + 'supplier-1', + 'supplier-2', + 'supplier-3', +] CUSTOMERS_IDS = [ 'customer-1', 'customer-2', @@ -2676,10 +2681,20 @@ def scenario27(): assert customer_1_groupB_messages_before > 0 assert customer_2_groupB_messages_before > 0 + # prepare customers before supplier-1 goes offline, replace supplier-1 with supplier-3 + kw.config_set_v1('customer-1', 'services/employer/candidates', 'http://id-a:8084/supplier-3.xml,http://id-a:8084/supplier-2.xml') + kw.config_set_v1('customer-2', 'services/employer/candidates', 'http://id-a:8084/supplier-3.xml,http://id-a:8084/supplier-2.xml') + kw.config_set_v1('customer-3', 'services/employer/candidates', 'http://id-a:8084/supplier-3.xml,http://id-a:8084/supplier-2.xml') + # stop supplier-1 kw.wait_packets_finished(CUSTOMERS_IDS_123 + SUPPLIERS_IDS_12) kw.config_set_v1('supplier-1', 'services/network/enabled', 'false') - kw.wait_packets_finished(CUSTOMERS_IDS_123 + ['supplier-2', ]) + kw.wait_packets_finished(CUSTOMERS_IDS_123 + ['supplier-2', 'supplier-3', ]) + + # make sure customers are all switched to the new supplier + kw.supplier_list_v1('customer-1', expected_min_suppliers=2, expected_max_suppliers=2) + kw.supplier_list_v1('customer-2', expected_min_suppliers=2, expected_max_suppliers=2) + kw.supplier_list_v1('customer-3', expected_min_suppliers=2, expected_max_suppliers=2) # send again a message to the second group from customer-1 # this should rotate active queue supplier for customer-1 in the second group only diff --git a/regress/tests/stream/conf.json b/regress/tests/stream/conf.json index f4e0cd1d9..ec48699e3 100644 --- a/regress/tests/stream/conf.json +++ b/regress/tests/stream/conf.json @@ -90,6 +90,23 @@ "preferred_routers": "" } }, + "supplier-3": { + "links": [ + "dht-2", + "dht-3", + "stun-1", + "id-a" + ], + "ports": "10043:22", + "node": { + "role": "supplier", + "name": "supplier-3", + "join_network": true, + "known_id_servers": "id-a:8084", + "known_dht_seeds": "dht-2:14441,dht-3:14441", + "preferred_routers": "" + } + }, "customer-1": { "links": [ "dht-2", @@ -174,6 +191,7 @@ "stun-1", "supplier-1", "supplier-2", + "supplier-3", "customer-1", "customer-2", "customer-3" diff --git a/regress/tests/stream/docker-compose.yml b/regress/tests/stream/docker-compose.yml index f0ca039be..6a3b8c1a1 100644 --- a/regress/tests/stream/docker-compose.yml +++ b/regress/tests/stream/docker-compose.yml @@ -79,6 +79,16 @@ services: - stun-1 - id-a + supplier-3: + image: bitdust/app + ports: + - "10843:22" + links: + - dht-2 + - dht-3 + - stun-1 + - id-a + customer-1: image: bitdust/app ports: @@ -132,6 +142,7 @@ services: - stun-1 - supplier-1 - supplier-2 + - supplier-3 - customer-1 - customer-2 - customer-3 diff --git a/regress/tests/stream/test_stream.py b/regress/tests/stream/test_stream.py index 5883ba20c..3d2182b1d 100644 --- a/regress/tests/stream/test_stream.py +++ b/regress/tests/stream/test_stream.py @@ -55,10 +55,10 @@ def test_stream(): def prepare(): set_active_scenario('PREPARE') kw.wait_suppliers_connected(scenarios.CUSTOMERS_IDS_123, expected_min_suppliers=2, expected_max_suppliers=2) - kw.wait_service_state(scenarios.SUPPLIERS_IDS_12, 'service_supplier', 'ON') + kw.wait_service_state(scenarios.SUPPLIERS_IDS_123, 'service_supplier', 'ON') kw.wait_service_state(scenarios.CUSTOMERS_IDS_123, 'service_customer', 'ON') kw.wait_service_state(scenarios.CUSTOMERS_IDS_123, 'service_shared_data', 'ON') kw.wait_service_state(scenarios.CUSTOMERS_IDS_123, 'service_private_groups', 'ON') kw.wait_service_state(scenarios.CUSTOMERS_IDS_123, 'service_message_history', 'ON') - kw.wait_service_state(scenarios.SUPPLIERS_IDS_12, 'service_joint_postman', 'ON') - kw.wait_packets_finished(scenarios.CUSTOMERS_IDS_123 + scenarios.SUPPLIERS_IDS_12) + kw.wait_service_state(scenarios.SUPPLIERS_IDS_123, 'service_joint_postman', 'ON') + kw.wait_packets_finished(scenarios.CUSTOMERS_IDS_123 + scenarios.SUPPLIERS_IDS_123) diff --git a/regress/testsupport.py b/regress/testsupport.py index f6075955f..cf5aa1868 100644 --- a/regress/testsupport.py +++ b/regress/testsupport.py @@ -430,8 +430,8 @@ def start_daemon(node, skip_initialize=False, verbose=False): if verbose: dbg('\n' + bitdust_daemon[0].strip()) assert ( - bitdust_daemon[0].strip().startswith('main BitDust process already started') or - bitdust_daemon[0].strip().startswith('new BitDust process will be started in daemon mode') + bitdust_daemon[0].strip().count('main BitDust process already started') or + bitdust_daemon[0].strip().count('new BitDust process will be started in daemon mode') ), bitdust_daemon[0].strip() if verbose: dbg(f'\nstart_daemon [{node}] OK\n') @@ -447,8 +447,8 @@ async def start_daemon_async(node, loop, verbose=False): if verbose: dbg('\n' + bitdust_daemon[0].strip()) assert ( - bitdust_daemon[0].strip().startswith('main BitDust process already started') or - bitdust_daemon[0].strip().startswith('new BitDust process will be started in daemon mode') + bitdust_daemon[0].strip().count('main BitDust process already started') or + bitdust_daemon[0].strip().count('new BitDust process will be started in daemon mode') ), bitdust_daemon[0].strip() if verbose: dbg(f'\nstart_daemon_async [{node}] OK\n') @@ -718,8 +718,8 @@ def stop_daemon(node, skip_checks=False, verbose=False): bitdust_stop = run_ssh_command_and_wait(node, 'bitdust stop', verbose=verbose) if not skip_checks: resp = bitdust_stop[0].strip() - assert ((resp.startswith('BitDust child processes found') and resp.endswith('BitDust stopped')) or - (resp.startswith('found main BitDust process:') and resp.count('finished')) or (resp == 'BitDust is not running at the moment') or (resp == '')) + assert ((resp.count('BitDust child processes found') and resp.count('BitDust stopped')) or + (resp.count('found main BitDust process:') and resp.count('finished')) or resp.count('BitDust is not running at the moment') or (resp == '')) async def stop_daemon_async(node, loop, skip_checks=False, verbose=False): @@ -731,8 +731,8 @@ async def stop_daemon_async(node, loop, skip_checks=False, verbose=False): if verbose: dbg(f'stop_daemon_async [{node}] DONE\n') return - if not ((resp.startswith('BitDust child processes found') and resp.endswith('BitDust stopped')) or - (resp.startswith('found main BitDust process:') and resp.count('finished')) or (resp == 'BitDust is not running at the moment') or (resp == '')): + if not ((resp.count('BitDust child processes found') and resp.count('BitDust stopped')) or + (resp.count('found main BitDust process:') and resp.count('finished')) or resp.count('BitDust is not running at the moment') or (resp == '')): if verbose: warn('process finished with unexpected response: %r' % resp) assert False, resp