diff options
-rw-r--r-- | .bumpversion.cfg | 2 | ||||
-rwxr-xr-x | bin/bt_connect | 2 | ||||
-rwxr-xr-x | bin/disk_smart | 61 | ||||
-rwxr-xr-x | bin/network | 29 | ||||
-rwxr-xr-x | bin/pm_test | 18 | ||||
-rw-r--r-- | jobs/ethernet.txt.in | 8 | ||||
-rw-r--r-- | jobs/info.txt.in | 4 | ||||
-rw-r--r-- | jobs/mediacard.txt.in | 36 | ||||
-rw-r--r-- | jobs/miscellanea.txt.in | 20 | ||||
-rw-r--r-- | jobs/suspend.txt.in | 32 | ||||
-rw-r--r-- | jobs/usb.txt.in | 6 | ||||
-rwxr-xr-x | manage.py | 2 |
12 files changed, 156 insertions, 64 deletions
diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 4e0c47a..c55c380 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.37.0.dev0 +current_version = 0.40.0.dev0 files = manage.py parse = (?P<major>\d+)\.(?P<minor>\d+)(\.(?P<patch>\d+))?((?P<release>\.?[a-z]+)(?P<N>\d+))? serialize = diff --git a/bin/bt_connect b/bin/bt_connect index b52642a..1fed30e 100755 --- a/bin/bt_connect +++ b/bin/bt_connect @@ -23,7 +23,7 @@ import sys import time -import checkbox_support.bt_helper +import checkbox_support.bt_helper as bt_helper from argparse import ArgumentParser diff --git a/bin/disk_smart b/bin/disk_smart index 804b11f..a9b294e 100755 --- a/bin/disk_smart +++ b/bin/disk_smart @@ -206,22 +206,23 @@ def initiate_smart_test(disk, raid_element, raid_type, type='short'): return smart_proc.returncode -def get_smart_entries(disk, raid_element, raid_type, type='selftest'): +def get_smart_entries(disk, raid_element, raid_type, verbose=False): entries = [] + returncode = 0 try: if raid_type == 'none': - stdout = check_output(['smartctl', '-l', type, disk], + stdout = check_output(['smartctl', '-l', 'selftest', disk], universal_newlines=True) else: - stdout = check_output(['smartctl', '-l', type, disk, + stdout = check_output(['smartctl', '-l', 'selftest', disk, '-d', '{},{}'. format(raid_type, raid_element)], universal_newlines=True) - returncode = 0 except CalledProcessError as err: - logging.error("Error encountered checking SMART Log") - logging.error("\tsmartctl returned code: {}".format(err.returncode)) - logging.error("\tSee 'man smartctl' for info on return code meanings") + if verbose: + logging.error("Error encountered checking SMART Log") + logging.error("\tsmartctl returned: {}".format(err.returncode)) + logging.error("\tSee 'man smartctl' for info on return codes") stdout = err.output returncode = err.returncode @@ -239,7 +240,7 @@ def get_smart_entries(disk, raid_element, raid_type, type='selftest'): logging.info('No entries found in log') if not line.startswith('Num'): entries.append('No entries found in log yet') - return entries, returncode + return entries, stdout, returncode columns = ['number', 'description', 'status', 'remaining', 'lifetime', 'lba'] lengths = [line.index(i) for i in line.split()] @@ -258,7 +259,7 @@ def get_smart_entries(disk, raid_element, raid_type, type='selftest'): entry['number'] = int(entry['number'][1:]) entries.append(entry) - return entries, returncode + return entries, stdout, returncode def in_progress(current_entries): @@ -316,11 +317,12 @@ def poll_for_status(args, disk, raid_element, raid_type, previous_entries): keep_going = True while keep_going: - # Poll every sleep seconds until test is complete$ + # Poll every sleep seconds until test is complete time.sleep(args.sleep) - current_entries, returncode = get_smart_entries(disk, raid_element, - raid_type) + current_entries, output, returncode = get_smart_entries(disk, + raid_element, + raid_type) if current_entries != previous_entries: if not in_progress(current_entries): logging.debug("Current log entries differ from starting log" @@ -354,8 +356,9 @@ def run_smart_test(args, disk, raid_element, raid_type): :returns: True for success, False for failure """ - previous_entries, returncode = get_smart_entries(disk, raid_element, - raid_type) + previous_entries, output, returncode = get_smart_entries(disk, + raid_element, + raid_type) if raid_type == 'none': logging.info("Starting SMART self-test on {}".format(disk)) else: @@ -375,32 +378,26 @@ def run_smart_test(args, disk, raid_element, raid_type): logging.debug("Log is 20+ entries long. Restarting test to add an" " abort message to make the log diff easier") initiate_smart_test(disk, raid_element, raid_type) - previous_entries, returncode = get_smart_entries(disk, raid_element, - raid_type) + previous_entries, output, returncode = get_smart_entries(disk, + raid_element, + raid_type) status, returncode = poll_for_status(args, disk, raid_element, raid_type, previous_entries) if returncode != 0: - log, returncode = get_smart_entries(disk, raid_element, raid_type) + log, output, returncode = get_smart_entries(disk, raid_element, + raid_type, True) + logging.error("FAIL: SMART Self-Test appears to have failed " + "for some reason.") + logging.error("\tLast smartctl return code: %d", returncode) + logging.error("\tLast smartctl run status: %s", status) if raid_type == 'none': - logging.error("FAIL: SMART Self-Test appears to have failed " - "for some reason. Run 'sudo smartctl -l selftest " - "{}' to see the SMART log".format(disk)) + logging.error("\t'smartctl -l selftest {}' output:".format(disk)) else: - logging.error("FAIL: SMART Self-Test appears to have failed " - "for some reason. Run 'sudo smartctl -l selftest " - "{} -d {},{}' to see the SMART log". + logging.error("\t'smartctl -l selftest {} -d {},{}' output:". format(disk, raid_type, raid_element)) - logging.error("\tLast smartctl return code: %d", returncode) - logging.error("\tLast smartctl run status: %s", status) - logging.debug("\tMost Recent SMART LOG Entry:") - for log_entry in log: - if log_entry['number'] == 1: - logging.debug("\t# {}\t{}\t{}\t{}\t{}\t{}".format( - log_entry['number'], log_entry['description'], - log_entry['status'], log_entry['remaining'], - log_entry['lifetime'], log_entry['lba'])) + logging.error("\n%s", output) return False else: if raid_type == 'none': diff --git a/bin/network b/bin/network index c855f0a..aaee226 100755 --- a/bin/network +++ b/bin/network @@ -28,6 +28,7 @@ import datetime import fcntl import ipaddress import logging +import math import os import re import shlex @@ -57,6 +58,7 @@ class IPerfPerformanceTest(object): fail_threshold, cpu_load_fail_threshold, iperf3, + num_threads, protocol="tcp", data_size="1", run_time=None, @@ -68,6 +70,7 @@ class IPerfPerformanceTest(object): self.fail_threshold = fail_threshold self.cpu_load_fail_threshold = cpu_load_fail_threshold self.iperf3 = iperf3 + self.num_threads = num_threads self.data_size = data_size self.run_time = run_time self.scan_timeout = scan_timeout @@ -84,10 +87,21 @@ class IPerfPerformanceTest(object): else: self.executable = "iperf" + # Determine number of parallel threads + if self.num_threads == -1: + # Below is a really crude guesstimate based on our + # initial testing. It's likely possible to improve + # this method of setting the number of threads. + threads = math.ceil(self.iface.link_speed / 10000) + else: + threads = self.num_threads + + logging.debug("Using {} threads.".format(threads)) + # If we set run_time, use that instead to build the command. if self.run_time is not None: - cmd = "{} -c {} -t {} -i 1 -f m".format( - self.executable, self.target, self.run_time) + cmd = "{} -c {} -t {} -i 1 -f m -P {}".format( + self.executable, self.target, self.run_time, threads) else: # Because we can vary the data size, we need to vary the timeout as # well. It takes an estimated 15 minutes to send 1GB over 10Mb/s. @@ -96,8 +110,9 @@ class IPerfPerformanceTest(object): # time without timeout to catch devices that slow down, and also # not prematurely end iperf on low-bandwidth devices. self.timeout = 1080*int(self.data_size) - cmd = "timeout {} {} -c {} -n {}G -i 1 -f -m".format( - self.timeout, self.executable, self.target, self.data_size) + cmd = "timeout {} {} -c {} -n {}G -i 1 -f -m -P {}".format( + self.timeout, self.executable, self.target, self.data_size, + threads) logging.debug("Executing command {}".format(cmd)) logging.debug("Starting iperf against {}, this could take a while...". @@ -389,7 +404,7 @@ def run_test(args, test_target): iperf_benchmark = IPerfPerformanceTest(args.interface, test_target, args.fail_threshold, args.cpu_load_fail_threshold, - args.iperf3) + args.iperf3, args.num_threads) if args.datasize: iperf_benchmark.data_size = args.datasize if args.runtime: @@ -709,6 +724,10 @@ TEST_TARGET_IPERF = iperf-server.example.com test_parser.add_argument( '--underspeed-ok', default=False, action="store_true", help="Run test even if an underspeed 1ink is detected") + test_parser.add_argument( + '--num-threads', type=int, default=-1, + help=("Number of threads to use in the test. " + "(Default is computed based on network speed.)")) # Sub info options info_parser.add_argument( diff --git a/bin/pm_test b/bin/pm_test index 8bc73f4..868b92b 100755 --- a/bin/pm_test +++ b/bin/pm_test @@ -137,16 +137,18 @@ class PowerManagementOperation(object): """Run suspend and resume cycles.""" if cycles_count < 1: return + script_path = '' if fwts: - script_name = 'fwts_test' - command_tpl = '{} -s s3 --s3-device-check --s3-sleep-delay=30 --s3-multiple={}' + script_path = 'checkbox-support-fwts_test' + command_tpl = '-s s3 --s3-device-check --s3-sleep-delay=30 --s3-multiple={}' if self.args.log_dir: - command_tpl += ' --log={}/fwts.log'.format(self.args.log_dir) + command_tpl = '--log={}/fwts.log '.format(self.args.log_dir) + command_tpl + command_tpl = '{} ' + command_tpl else: script_name = 'sleep_test' command_tpl = '{} -s mem -p -i {} -w 10' - script_path = os.path.join( - os.path.dirname(os.path.realpath(__file__)), script_name) + script_path = os.path.join( + os.path.dirname(os.path.realpath(__file__)), script_name) command_str = command_tpl.format(script_path, cycles_count) logging.info('Running suspend/resume cycles') logging.debug('Executing: {0!r}...'.format(command_str)) @@ -157,7 +159,7 @@ class PowerManagementOperation(object): universal_newlines=True, shell=True)) except subprocess.CalledProcessError as e: logging.error('Error while running {0}:'.format(e.cmd)) - logging.exception(e.output) + logging.error(e.output) def summary(self): """ @@ -714,9 +716,9 @@ class LoggingConfiguration(object): logger = logging.getLogger() logger.setLevel(logging.DEBUG) - # Log to sys.stderr using log level passed through command line + # Log to sys.stdout using log level passed through command line if log_level != logging.NOTSET: - log_handler = logging.StreamHandler() + log_handler = logging.StreamHandler(sys.stdout) formatter = logging.Formatter('%(levelname)-8s %(message)s') log_handler.setFormatter(formatter) log_handler.setLevel(log_level) diff --git a/jobs/ethernet.txt.in b/jobs/ethernet.txt.in index 8c059ce..7e384ec 100644 --- a/jobs/ethernet.txt.in +++ b/jobs/ethernet.txt.in @@ -64,7 +64,7 @@ _description: unit: template template-resource: device -template-filter: device.category == 'NETWORK' +template-filter: device.category == 'NETWORK' and device.interface != 'UNKNOWN' plugin: shell category_id: com.canonical.plainbox::ethernet id: ethernet/multi_iperf3_nic_device{__index__}_{interface} @@ -83,7 +83,7 @@ _description: unit: template template-resource: device -template-filter: device.category == 'NETWORK' +template-filter: device.category == 'NETWORK' and device.interface != 'UNKNOWN' plugin: shell category_id: com.canonical.plainbox::ethernet id: ethernet/ethertool_check_device{__index__}_{interface} @@ -98,7 +98,7 @@ _description: unit: template template-resource: device -template-filter: device.category == 'NETWORK' +template-filter: device.category == 'NETWORK' and device.interface != 'UNKNOWN' plugin: user-interact-verify category_id: com.canonical.plainbox::ethernet id: ethernet/maximum_bandwidth_device{__index__}_{interface} @@ -122,7 +122,7 @@ _verification: unit: template template-resource: device -template-filter: device.category == 'NETWORK' +template-filter: device.category == 'NETWORK' and device.interface != 'UNKNOWN' plugin: shell category_id: com.canonical.plainbox::ethernet id: ethernet/stress_performance_device{__index__}_{interface} diff --git a/jobs/info.txt.in b/jobs/info.txt.in index f22a3cb..0b270d8 100644 --- a/jobs/info.txt.in +++ b/jobs/info.txt.in @@ -44,14 +44,14 @@ estimated_duration: 0.044 _description: Attaches info on DMI _summary: Attach a copy of /sys/class/dmi/id/* requires: - cpuinfo.platform in ("i386", "x86_64") + dmi_present.state == 'supported' id: dmidecode_attachment plugin: attachment category_id: com.canonical.plainbox::info requires: package.name == 'dmidecode' or executable.name == 'dmidecode' - cpuinfo.platform in ("i386", "x86_64") + dmi_present.state == 'supported' user: root command: dmidecode | iconv -t 'utf-8' -c estimated_duration: 0.030 diff --git a/jobs/mediacard.txt.in b/jobs/mediacard.txt.in index 72c488e..3b5a648 100644 --- a/jobs/mediacard.txt.in +++ b/jobs/mediacard.txt.in @@ -1,3 +1,8 @@ +unit: manifest entry +id: has_card_reader +_name: Card Reader +value-type: bool + plugin: user-interact category_id: com.canonical.plainbox::mediacard id: mediacard/mmc-insert @@ -8,9 +13,10 @@ command: else removable_storage_watcher --memorycard insert sdio usb scsi fi +imports: from com.canonical.plainbox import manifest requires: package.name == 'udisks2' or snap.name == 'udisks2' - device.category == 'CARDREADER' + manifest.has_card_reader == 'True' user: root _description: PURPOSE: @@ -30,6 +36,7 @@ id: mediacard/mmc-storage estimated_duration: 30.0 depends: mediacard/mmc-insert user: root +flags: preserve-cwd command: removable_storage_test -s 67120000 --memorycard sdio usb scsi --auto-reduce-size requires: package.name == 'udisks2' or (snap.name == 'core' and int(snap.revision) >= 1804) @@ -70,9 +77,10 @@ command: else removable_storage_watcher --memorycard insert sdio usb scsi fi +imports: from com.canonical.plainbox import manifest requires: package.name == 'udisks2' or snap.name == 'udisks2' - device.category == 'CARDREADER' + manifest.has_card_reader == 'True' user: root _description: PURPOSE: @@ -92,6 +100,7 @@ id: mediacard/sd-storage estimated_duration: 30.0 depends: mediacard/sd-insert user: root +flags: preserve-cwd command: removable_storage_test -s 268400000 --memorycard sdio usb scsi requires: package.name == 'udisks2' or (snap.name == 'core' and int(snap.revision) >= 1804) @@ -127,11 +136,13 @@ category_id: com.canonical.plainbox::mediacard id: mediacard/sd-preinserted estimated_duration: 30.0 user: root +flags: preserve-cwd command: removable_storage_test -s 268400000 --memorycard -l sdio usb scsi && removable_storage_test --memorycard sdio usb scsi +imports: from com.canonical.plainbox import manifest requires: package.name == 'udisks2' or snap.name == 'udisks2' package.name == 'udisks2' or (snap.name == 'core' and int(snap.revision) >= 1804) - device.category == 'CARDREADER' + manifest.has_card_reader == 'True' _description: This is a fully automated version of mediacard/sd-automated and assumes that the system under test has a memory card device plugged in prior to checkbox execution. @@ -147,9 +158,10 @@ command: else removable_storage_watcher --memorycard insert sdio usb scsi fi +imports: from com.canonical.plainbox import manifest requires: package.name == 'udisks2' or snap.name == 'udisks2' - device.category == 'CARDREADER' + manifest.has_card_reader == 'True' user: root _description: PURPOSE: @@ -170,6 +182,7 @@ id: mediacard/sdhc-storage estimated_duration: 30.0 depends: mediacard/sdhc-insert user: root +flags: preserve-cwd command: removable_storage_test -s 268400000 --memorycard sdio usb scsi requires: package.name == 'udisks2' or (snap.name == 'core' and int(snap.revision) >= 1804) @@ -210,9 +223,10 @@ command: else removable_storage_watcher --memorycard insert sdio usb scsi fi +imports: from com.canonical.plainbox import manifest requires: package.name == 'udisks2' or snap.name == 'udisks2' - device.category == 'CARDREADER' + manifest.has_card_reader == 'True' user: root _description: PURPOSE: @@ -272,9 +286,10 @@ command: else removable_storage_watcher --memorycard insert sdio usb scsi fi +imports: from com.canonical.plainbox import manifest requires: package.name == 'udisks2' or snap.name == 'udisks2' - device.category == 'CARDREADER' + manifest.has_card_reader == 'True' user: root _description: PURPOSE: @@ -334,9 +349,10 @@ command: else removable_storage_watcher --memorycard insert sdio usb scsi fi +imports: from com.canonical.plainbox import manifest requires: package.name == 'udisks2' or snap.name == 'udisks2' - device.category == 'CARDREADER' + manifest.has_card_reader == 'True' user: root _description: PURPOSE: @@ -397,9 +413,10 @@ command: removable_storage_watcher --memorycard insert sdio usb scsi fi user: root +imports: from com.canonical.plainbox import manifest requires: package.name == 'udisks2' or snap.name == 'udisks2' - device.category == 'CARDREADER' + manifest.has_card_reader == 'True' _description: PURPOSE: This test will check that the systems media card reader can @@ -458,9 +475,10 @@ command: else removable_storage_watcher --memorycard insert sdio usb scsi fi +imports: from com.canonical.plainbox import manifest requires: package.name == 'udisks2' or snap.name == 'udisks2' - device.category == 'CARDREADER' + manifest.has_card_reader == 'True' user: root _description: PURPOSE: diff --git a/jobs/miscellanea.txt.in b/jobs/miscellanea.txt.in index dc79a32..6dec07d 100644 --- a/jobs/miscellanea.txt.in +++ b/jobs/miscellanea.txt.in @@ -334,3 +334,23 @@ _steps: 2. Compare items on System Manifest to the devices known to udev _verification: Do the devices reported by udev match the devices on the Manifest? + +plugin: shell +category_id: com.canonical.plainbox::miscellanea +estimated_duration: 20.0 +id: miscellanea/sosreport +user: root +requires: executable.name == 'sosreport' +command: sosreport --batch --tmp-dir $PLAINBOX_SESSION_SHARE +_summary: Generate baseline sosreport +_description: Generates a baseline sosreport of logs and system data + +plugin: attachment +category_id: com.canonical.plainbox::miscellanea +estimated_duration: 5.0 +id: miscellanea/sosreport_attachment +depends: miscellanea/sosreport +user: root +command: + SOSFILE=`ls -t $PLAINBOX_SESSION_SHARE/sosreport*xz | head -1`; [ -e ${SOSFILE} ] && base64 $SOSFILE +_summary: Attach the baseline sosreport file diff --git a/jobs/suspend.txt.in b/jobs/suspend.txt.in index 64c05f1..26b0f09 100644 --- a/jobs/suspend.txt.in +++ b/jobs/suspend.txt.in @@ -2223,6 +2223,10 @@ category_id: com.canonical.plainbox::suspend id: suspend/mmc-insert-after-suspend estimated_duration: 30.0 depends: suspend/suspend_advanced +imports: from com.canonical.plainbox import manifest +requires: + package.name == 'udisks2' or snap.name == 'udisks2' + manifest.has_card_reader == 'True' command: removable_storage_watcher --memorycard insert sdio usb scsi _description: PURPOSE: @@ -2270,6 +2274,10 @@ category_id: com.canonical.plainbox::suspend id: suspend/sd-insert-after-suspend estimated_duration: 30.0 depends: suspend/suspend_advanced +imports: from com.canonical.plainbox import manifest +requires: + package.name == 'udisks2' or snap.name == 'udisks2' + manifest.has_card_reader == 'True' command: removable_storage_watcher --memorycard insert sdio usb scsi _description: PURPOSE: @@ -2319,6 +2327,10 @@ category_id: com.canonical.plainbox::suspend id: suspend/sdhc-insert-after-suspend estimated_duration: 30.0 depends: suspend/suspend_advanced +imports: from com.canonical.plainbox import manifest +requires: + package.name == 'udisks2' or snap.name == 'udisks2' + manifest.has_card_reader == 'True' command: removable_storage_watcher --memorycard insert sdio usb scsi _description: PURPOSE: @@ -2367,6 +2379,10 @@ category_id: com.canonical.plainbox::suspend id: mediacard/cf-insert-after-suspend estimated_duration: 30.0 depends: suspend/suspend_advanced +imports: from com.canonical.plainbox import manifest +requires: + package.name == 'udisks2' or snap.name == 'udisks2' + manifest.has_card_reader == 'True' command: removable_storage_watcher --memorycard insert sdio usb scsi _description: PURPOSE: @@ -2414,6 +2430,10 @@ category_id: com.canonical.plainbox::suspend id: mediacard/sdxc-insert-after-suspend estimated_duration: 30.0 depends: suspend/suspend_advanced +imports: from com.canonical.plainbox import manifest +requires: + package.name == 'udisks2' or snap.name == 'udisks2' + manifest.has_card_reader == 'True' command: removable_storage_watcher --memorycard insert sdio usb scsi _description: PURPOSE: @@ -2461,6 +2481,10 @@ category_id: com.canonical.plainbox::suspend id: mediacard/ms-insert-after-suspend estimated_duration: 30.0 depends: suspend/suspend_advanced +imports: from com.canonical.plainbox import manifest +requires: + package.name == 'udisks2' or snap.name == 'udisks2' + manifest.has_card_reader == 'True' command: removable_storage_watcher --memorycard insert sdio usb scsi _description: PURPOSE: @@ -2508,6 +2532,10 @@ category_id: com.canonical.plainbox::suspend id: mediacard/msp-insert-after-suspend estimated_duration: 30.0 depends: suspend/suspend_advanced +imports: from com.canonical.plainbox import manifest +requires: + package.name == 'udisks2' or snap.name == 'udisks2' + manifest.has_card_reader == 'True' command: removable_storage_watcher --memorycard insert sdio usb scsi _description: PURPOSE: @@ -2555,6 +2583,10 @@ category_id: com.canonical.plainbox::suspend id: mediacard/xd-insert-after-suspend estimated_duration: 30.0 depends: suspend/suspend_advanced +imports: from com.canonical.plainbox import manifest +requires: + package.name == 'udisks2' or snap.name == 'udisks2' + manifest.has_card_reader == 'True' command: removable_storage_watcher --memorycard insert sdio usb scsi _description: PURPOSE: diff --git a/jobs/usb.txt.in b/jobs/usb.txt.in index 895ca44..8fa63d7 100644 --- a/jobs/usb.txt.in +++ b/jobs/usb.txt.in @@ -6,6 +6,8 @@ value-type: bool plugin: shell category_id: com.canonical.plainbox::usb id: usb/detect +requires: + cpuinfo.platform != 's390x' estimated_duration: 1.0 command: set -o pipefail @@ -225,8 +227,9 @@ id: usb/storage-preinserted user: root estimated_duration: 45.0 command: removable_storage_test -l usb && removable_storage_test -s 268400000 usb -flags: also-after-suspend +flags: also-after-suspend preserve-cwd requires: + cpuinfo.platform != 's390x' package.name == 'udisks2' or snap.name == 'udisks2' package.name == 'udisks2' or (snap.name == 'core' and int(snap.revision) >= 1804) _summary: @@ -242,6 +245,7 @@ id: usb3/storage-preinserted user: root flags: also-after-suspend requires: + cpuinfo.platform != 's390x' usb.usb3 == 'supported' package.name == 'udisks2' or snap.name == 'udisks2' package.name == 'udisks2' or (snap.name == 'core' and int(snap.revision) >= 1804) @@ -5,7 +5,7 @@ from plainbox.provider_manager import N_ setup( name='plainbox-provider-checkbox', namespace='com.canonical.certification', - version="0.37.0.dev0", + version="0.40.0.dev0", description=N_("Checkbox provider"), gettext_domain='plainbox-provider-checkbox', strict=False, deprecated=False, |