def _run(cls, config, *args, **kwargs): db_config = config.get('db') errors = [] try: mysql_utils = MySQLUtil(**db_config) cur_user = db_config.get('user') with mysql_utils.create_connection() as cursor: result = get_user_privileges(cursor, ','.join(PRIVILIGES), cur_user) for col in PRIVILIGES: if result[col] != SUCCESS: errors.append(col) except Exception as e: return results.Problem( cls, 'Unable to connect to db to check permisions', e, '', ) if errors: return results.Problem( cls, 'Some required db permisions are missing', 'Missing Permissions : {}'.format(' , '.join(PRIVILIGES)), '', ) return results.OK(cls)
def _run(cls, config, *args, **kwargs): all_wa_containers = docker_utils.get_wa_containers() stopped_wa_container_ids = [ wa_container.short_id for wa_container in all_wa_containers if not wa_container.is_running() ] remediation_msg = ( "List all containers on the current host with `docker ps -a` and " "run `docker start #container_id` to start a container. " "If a container stops due to errors, " "refer to messages from other checks to diagnose or contact support " "https://developers.facebook.com/docs/whatsapp/contact-support" ) if len(all_wa_containers) == len(stopped_wa_container_ids): return results.Problem( cls, "No WA container is running", "All WA containers are stopped.", remediation_msg, ) if len(stopped_wa_container_ids) > 0: return results.Warning( cls, "Not all WA containers are running.", f"WA containers with id {', '.join(stopped_wa_container_ids)}" " not running.", f"If this is expected, please ignore. {remediation_msg}", ) return results.OK(cls)
def _run(cls, config, *args, **kwargs): try: mysql_utils = MySQLUtil(**config.get("db")) mysql_utils.try_connect() return results.OK(cls) except Exception as e: return results.Problem(cls, "Unable to connect to db.", e, "")
def _run(cls, config, *args, **kwargs): wacore_containers = docker_utils.get_running_wacore_containers() short_error_message = "Network connectivity check fails" if not wacore_containers: return results.Problem( cls, short_error_message, "There is no wacore container running", "Please check results from other actions to diagnose", ) container = wacore_containers[0] hosts_not_reachable = get_hosts_not_reachable_from_container( container, WHATSAPP_SERVERS) TROUBLESHOOTING_URL = ( "https://developers.facebook.com/docs/whatsapp/network-debugging") if hosts_not_reachable: error_message = format_error_message(hosts_not_reachable) return results.Problem( cls, "Network connectivity check fails", error_message, "Please refer to {} for network requirements details.".format( TROUBLESHOOTING_URL), ) return results.OK(cls)
def _run(cls, config, *args, **kwargs): db_config = config.get('db') cur_user = db_config.get('user') errors = [] remediation = """ Run MySQL command: GRANT ALL PRIVILEGES ON *.* TO \'{}\'@\'{}\' to grant all privileges to the db user `{}` and rerun the checks. """.format(cur_user, db_config.get('host'), cur_user) try: mysql_utils = MySQLUtil(**db_config) with mysql_utils.create_connection() as cursor: result = get_user_privileges( cursor, ','.join(PRIVILIGES), cur_user) for col in PRIVILIGES: if result[col] != SUCCESS: errors.append(col) except Exception as e: return results.Problem( cls, 'Unable to connect to db to check permisions', e, remediation, ) if errors: return results.Problem( cls, 'Some required db permisions are missing', 'Missing Permissions : {}'.format(' , '.join(PRIVILIGES)), remediation,) return results.OK(cls)
def _run(cls, config, *args, **kwargs): running_waweb_containers = docker_utils.get_running_waweb_containers() short_error_message = "Check webapp port failed" if not running_waweb_containers: return results.Problem( cls, short_error_message, "There is no waweb container running", "Please check results from other actions to diagnose", ) waweb_container = running_waweb_containers[0] port_bindings = docker_utils.get_container_port_bindings(waweb_container) for key in port_bindings: if key == "{}/{}".format(WEBAPP_PRIVATE_PORT, WEBAPP_PRIVATE_PORT_PROTOCOL): return results.OK(cls) return results.Problem( cls, short_error_message, "Port {} inside the webapp container needs to be mapped to host".format( WEBAPP_PRIVATE_PORT ), "Please start the waweb container with port binding: " '\nports:\n\t- [Public Port]:{}"'.format(WEBAPP_PRIVATE_PORT), )
def _run(cls, config, *args, **kwargs): wacore_containers = docker_utils.get_running_wacore_containers() if not wacore_containers: return results.Skipped( cls, "Network connectivity check was skipped", "There is no wacore container running. Action skipped.", "If this is unexpected, check results from other actions to diagnose. " "If this is expected (e.g.: an HA/MC setup " "across multiple hosts is under test), no actions are required.", ) container = wacore_containers[0] hosts_not_reachable = get_hosts_not_reachable_from_container( container, WHATSAPP_SERVERS) TROUBLESHOOTING_URL = ( "https://developers.facebook.com/docs/whatsapp/network-debugging") if hosts_not_reachable: error_message = format_error_message(hosts_not_reachable) return results.Problem( cls, "Network connectivity check fails", error_message, "Please refer to {} for network requirements details.".format( TROUBLESHOOTING_URL), ) return results.OK(cls)
def _run(cls, config, *args, **kwargs): api = WABizAPI(**config.get("webapp")) try: webhook_url = api.get_webhook_url() if not webhook_url: return _result_webhook_not_set(cls) if not webhook_url.startswith("https"): return _result_webhook_not_https(cls) except (WABizAccessError, WABizAuthError, WABizNetworkError, ValueError) as e: return _result_get_webhook_error(cls, e) # explicitly catching a possible exception except WABizGeneralError as e: # WABizGeneralError is likely not a user error, # should be handled by app-wide catch raise e wacore_containers = docker_utils.get_running_wacore_containers() if not wacore_containers: return results.Problem( cls, "Webhook check failed", "There is no wacore container running", "Please check results from other actions to diagnose", ) container = wacore_containers[0] cert_str = api.get_webhook_cert() dest_cert = None if cert_str: with tempfile.NamedTemporaryFile() as cert_file: cert_file.write(cert_str) cert_file.seek(0) docker_utils.put_archive_to_container(container, cert_file.name, DEST_PATH) dest_cert = os.path.join(DEST_PATH, os.path.basename(cert_file.name)) result, response_time = curl_utils.https_post_request_from_container( container, webhook_url, TEST_POST_DATA_STRING, REQ_TIMEOUT, dest_cert) if result == CURLTestResult.CONNECTION_ERROR: return _result_webhook_could_not_connect(cls) elif result == CURLTestResult.SSL_CERT_UNKNOWN: return _result_webhook_no_cert_uploaded(cls) elif result == CURLTestResult.CONNECTION_TIMEOUT: return _result_webhook_did_not_respond(cls) elif result == CURLTestResult.HTTP_STATUS_NOT_OK: return _result_webhook_did_not_return_ok(cls) elif response_time > ACCEPTABLE_RESPONSE_TIME: return _result_webhook_slow_response(cls, response_time) return results.OK(cls)
def _run(cls, config, *args, **kwargs): try: mysql_utils = MySQLUtil(**config.get('db')) mysql_version = mysql_utils.get_mysql_version() if is_version_valid(mysql_version): return results.OK(cls) else: error_message = 'Please make sure MySQL version is higher than 5.7.xx but NOT version 8. ' 'Refer to https://developers.facebook.com/docs/whatsapp/guides/installation for more details' return results.Problem( cls, 'You are using an unsupported version of MySQL', error_message, '') except Exception as e: return results.Problem(cls, 'Unable to connect to db', e, '')
def _run(cls, config, *args, **kwargs): wacore_containers = docker_utils.get_running_wacore_containers() short_error_message = 'Network connectivity check fails' if not wacore_containers: return results.Problem( cls, short_error_message, 'There is no wacore container running', 'Please check results from other actions to diagnose') container = wacore_containers[0] hosts_in_warning_state = [] hosts_in_error_state = [] for host in HOSTS_USING_DEFAULT_PORT: hostname = host[0] if is_server_in_warning_state(container, hostname): hosts_in_warning_state.append(host) elif is_server_in_error_state(container, hostname): hosts_in_error_state.append(host) for host in HOSTS_USING_HTTPS_PORT: hostname = host[0] if is_server_in_error_state(container, hostname): hosts_in_error_state.append(host) TROUBLESHOOTING_URL = \ 'https://developers.facebook.com/docs/whatsapp/network-debugging' if hosts_in_error_state: error_message = format_error_message(hosts_in_error_state) + \ format_warning_message(hosts_in_warning_state) return results.Problem( cls, 'Network connectivity check fails', error_message, 'Please refer to {} for network requirements details.'.format( TROUBLESHOOTING_URL)) if hosts_in_warning_state: warning_message = format_warning_message(hosts_in_warning_state) return results.Warning( cls, 'Network connectivity check fails', warning_message, 'Please refer to {} for network requirements details.'.format( TROUBLESHOOTING_URL)) return results.OK(cls)
def _run(cls, config, *args, **kwargs): errors = defaultdict(list) containers = docker_utils.get_all_running_wa_containers_except_db() for container in containers: for item in DB_SETTINGS_CONTAINER: value = docker_utils.get_value_by_inspecting_container_environment( container, item) if not value: errors[container.name].append(item) if errors: err_str = "For container {}, missing the required database settings : {}" return results.Problem( cls, "Some required db settings are not passed", "\n".join( [err_str.format(key, value) for key, _ in errors.items()]), "Please make sure to pass required db configuration", ) return results.OK(cls)
def _run(cls, config, *args, **kwargs): db_config = config.get("db") cur_user = db_config.get("user") errors = [] remediation = """ Run MySQL command: GRANT ALL PRIVILEGES ON *.* TO \'{}\'@\'{}\' to grant all privileges to the db user `{}` and rerun the checks. """.format(cur_user, db_config.get("host"), cur_user) try: mysql_utils = MySQLUtil(**db_config) result = mysql_utils.user_has_privileges(cur_user, PRIVILEGES) if not result: return results.Problem( cls, "Checking permissions returns empty result", "User {} doesn't exist".format(cur_user), "Make sure the correct db user is set in the config file", ) for col in PRIVILEGES: if result[col] != SUCCESS: errors.append(col) except Exception as e: return results.Problem( cls, "Unable to connect to db to check permisions", e, remediation) if errors: return results.Problem( cls, "Some required db permisions are missing", "Missing Permissions : {}".format(" , ".join(errors)), remediation, ) return results.OK(cls)
def _run(cls, config, *args, **kwargs): errors = [] wa_containers = docker_utils.get_running_wa_containers() if wa_containers: password = docker_utils.get_mysql_password(wa_containers[0]) else: # if containers not giving password then get password from db config file password = config.get('db').get('password') if set(INVALID_CHARACTERS) & set(password): errors.append( 'Please make sure mysql password do not have any special characters from {}' .format(INVALID_CHARACTERS)) if errors: return results.Problem( cls, 'Your mysql password contains some invalid characters', '\n'.join(errors), '', ) return results.OK(cls)
def _run(cls, config, *args, **kwargs): stopped_containers = [] stopped_sql_containers = [] def get_all_running_wa_containers(): containers = docker_utils.get_wa_containers() versions_to_wa_containers_dict = defaultdict( lambda: defaultdict(list)) for wa_container in containers: container = wa_container.container container_type = wa_container.container_type if (docker_utils.is_container_running(container)): if (docker_utils.WA_WEBAPP_CONTAINER_TAG == container_type or docker_utils.WA_COREAPP_CONTAINER_TAG == container_type): version = docker_utils.get_wa_version_from_container( container) versions_to_wa_containers_dict[ version[1]][container_type].append(container) else: if docker_utils.MYSQL_CONTAINER_TAG == container_type: stopped_sql_containers.append(container) else: stopped_containers.append(container) return versions_to_wa_containers_dict errors = [] warnings = [] running_wa_containers = get_all_running_wa_containers() if not running_wa_containers: errors.append("Either core container or web container is missing") no_of_working_versions = 0 for key, value in running_wa_containers.items(): if (len(value) != 2): errors.append( "Either web container or core container missing for version {}" .format(key)) elif (len(value[docker_utils.WA_COREAPP_CONTAINER_TAG]) != len( value[docker_utils.WA_WEBAPP_CONTAINER_TAG])): errors.append( 'The number of running coreapp containers, for version {}, is not' 'same as number of running web containers'.format(key)) else: no_of_working_versions = no_of_working_versions + 1 if (no_of_working_versions > 1): warnings.append( 'More than one set of Web app and Core app are running. ' 'It may be fine, but worth noticing.') if errors or stopped_sql_containers: err_str = 'Message: {}\n' container_details = "Container - ID: {}, Name: {}, Status: {}" stopped_containers.extend(stopped_sql_containers) return results.Problem( cls, 'Some of your WhatsApp containers are missing or not running.', '{}\n{}'.format( '\n'.join([err_str.format(e) for e in errors]), '\n'.join([ container_details.format(c.short_id, c.name, c.status) for c in stopped_containers ])), 'Use the following commands to learn more:\n' '\tdocker ps -a\n' '\tdocker inspect <CONTAINER_ID>', ) if warnings: warning_str = 'Message: {}\n' return results.Warning( cls, 'More than one set(coreapp,webapp) of containers are running', '\n'.join([warning_str.format(w) for w in warnings]), 'Use the following commands to learn more:\n' '\tdocker ps -a\n') return results.OK(cls)
def _run(cls, *args, **kwargs): return results.OK(cls)
def _run(cls, config, *args, **kwargs): errors = [] warnings = [] expiration_date_map = docker_utils.get_expiration_map() def get_all_running_wa_containers(): wa_containers = docker_utils.get_wa_containers() versions_to_wa_containers_dict = defaultdict( lambda: defaultdict(list)) for wa_container in wa_containers: container = wa_container.container container_type = wa_container.get_container_type() if docker_utils.is_container_running(container): if wa_container.is_webapp() or wa_container.is_coreapp(): version = docker_utils.get_wa_version_from_container( container) versions_to_wa_containers_dict[version][ container_type].append(container) return versions_to_wa_containers_dict def is_valid_version(version, wa_containers_dict): containers = [] for container in wa_containers_dict.values(): containers.extend(container) valid_version = True cur = datetime.utcnow().strftime(docker_utils.DATE_FORMAT) try: days_delta = days_between(cur, expiration_date_map[version[0]]) if days_delta < 0: valid_version = False error_str = ( "{}: Current version of your container {} has expired on {} UTC" ) for container in containers: errors.append( error_str.format( container.name, version[1], expiration_date_map[version[0]], )) elif days_delta < 7: warnings_str = "{}: Current version of your software ({}) " "is expiring on {} UTC, please consider upgrading soon" for container in containers: warnings.append( warnings_str.format( container.name, version[1], expiration_date_map[version[0]], )) except KeyError: valid_version = False error_str = ( "{}: Current version of your container ({}) is deprecated or " ) "wadebug tool is not up-to-date" for container in containers: errors.append(error_str.format(container.name, version[1])) return valid_version running_wa_containers = get_all_running_wa_containers() valid_versions = [] for key, value in running_wa_containers.items(): if len(value[docker_utils.WA_COREAPP_CONTAINER_TAG]) != len( value[docker_utils.WA_WEBAPP_CONTAINER_TAG]): errors.append( "Number of coreapp containers for version {} is not same as " "number of webapp containers".format(key)) elif is_valid_version(key, value): valid_versions.append(key[1]) if len(valid_versions) > 1: warnings.append( "More than one set of valid software versions({}) are running. " "It may be fine, but worth noticing.".format(valid_versions)) if errors: error_str = "Error Message: {}\n" warning_str = "Warning Message: {}\n" return results.Problem( cls, "Some of your WhatsApp container versions are expired " "or there is mismatch between versions" " of coreapp and webapp", "{}\n{}".format( "\n".join([error_str.format(e) for e in errors]), "\n".join([warning_str.format(w) for w in warnings]), ), "Please find the following link to find latest version: " "https://developers.facebook.com/docs/whatsapp/changelog\n" "and please find upgrade instructions here:" "https://developers.facebook.com/docs/whatsapp/guides/installation\n", ) if warnings: warning_str = "Warning Message: {}\n" return results.Warning( cls, "Some of your WhatsApp conatiners are nearing expiration or " "more than one valid version is running", "\n".join([warning_str.format(w) for w in warnings]), "Please find the following link to find latest version: " "https://developers.facebook.com/docs/whatsapp/changelog\n" "and please find upgrade instructions here:" "https://developers.facebook.com/docs/whatsapp/guides/installation\n", ) return results.OK(cls)
def _run(cls, config, *args, **kwargs): errors = [] warnings = [] expiration_date_map = docker_utils.get_expiration_map() def get_all_running_wa_containers(): wa_containers = docker_utils.get_wa_containers() versions_to_wa_containers_dict = defaultdict( lambda: defaultdict(list)) for wa_container in wa_containers: container = wa_container.container container_type = wa_container.container_type if (docker_utils.is_container_running(container)): if (docker_utils.WA_WEBAPP_CONTAINER_TAG == container_type or docker_utils.WA_COREAPP_CONTAINER_TAG == container_type): version = docker_utils.get_wa_version_from_container( container) versions_to_wa_containers_dict[version][ container_type].append(container) return versions_to_wa_containers_dict def is_valid_version(version, wa_containers_dict): containers = [] for container in wa_containers_dict.values(): containers.extend(container) valid_version = True cur = datetime.utcnow().strftime(docker_utils.DATE_FORMAT) try: days_delta = days_between(cur, expiration_date_map[version[0]]) if days_delta < 0: valid_version = False error_str = '{}: Current version of your container {} has expired on {} UTC' for container in containers: errors.append( error_str.format(container.name, version[1], expiration_date_map[version[0]])) elif days_delta < 7: warnings_str = '{}: Current version of your software ({}) is expiring on {} UTC, ' 'please consider upgrading soon' for container in containers: warnings.append( warnings_str.format( container.name, version[1], expiration_date_map[version[0]])) except KeyError: valid_version = False error_str = '{}: Current version of your container ({}) is not supported anymore or ' 'wadebug tool is not up-to-date' for container in containers: errors.append(error_str.format(container.name, version[1])) return valid_version running_wa_containers = get_all_running_wa_containers() valid_versions = [] for key, value in running_wa_containers.items(): if (len(value[docker_utils.WA_COREAPP_CONTAINER_TAG]) != len( value[docker_utils.WA_WEBAPP_CONTAINER_TAG])): errors.append( 'Number of coreapp containers for version {} is not same as ' 'number of webapp containers'.format(key)) elif is_valid_version(key, value): valid_versions.append(key[1]) if (len(valid_versions) > 1): warnings.append( 'More than one set of valid software versions({}) are running. ' 'It may be fine, but worth noticing.'.format(valid_versions)) if errors: error_str = 'Error Message: {}\n' warning_str = 'Warning Message: {}\n' return results.Problem( cls, 'Some of your WhatsApp container versions are expired or there is mismatch between versions' ' of coreapp and webapp', '{}\n{}'.format( '\n'.join([error_str.format(e) for e in errors]), '\n'.join([warning_str.format(w) for w in warnings])), 'Please find the following link to find latest version: ' 'https://developers.facebook.com/docs/whatsapp/changelog\n' 'and please find upgrade instructions here:' 'https://developers.facebook.com/docs/whatsapp/guides/installation\n', ) if warnings: warning_str = 'Warning Message: {}\n' return results.Warning( cls, 'Some of your WhatsApp conatiners are nearing expiration or ' 'more than one valid version is running', '\n'.join([warning_str.format(w) for w in warnings]), 'Please find the following link to find latest version: ' 'https://developers.facebook.com/docs/whatsapp/changelog\n' 'and please find upgrade instructions here:' 'https://developers.facebook.com/docs/whatsapp/guides/installation\n', ) return results.OK(cls)