예제 #1
0
    def _start_test(self):
        logger.info("Starting ndt5custom test...")

        # Check that a configuration file has been specified
        if "config" not in self._config:
            raise RunnerError(
                'ndt5custom',
                'No configuration file specified for the custom runner, \
                    skipping.')

        # Check that the ndt5-client executable is available.
        if shutil.which('ndt5-client') is None:
            raise RunnerError(
                'ndt5custom',
                "Executable ndt5-client does not exist, please install ndt5-client-go.",
            )

        custom_config = {}
        try:
            with open(self._config['config']) as config_file:
                custom_config = json.load(config_file)
        except IOError as err:
            raise RunnerError(
                'ndt5custom',
                'Cannot open the custom configuration file: ' + str(err))

        # Get all the servers to run measurements against from the config,
        # applying the corresponding selection algorithm.
        servers = set()
        for group in custom_config.get('serverGroups', []):
            # the default selection algorithm is 'random'
            selection = group.get('selection', 'random')
            if selection not in self._server_selection:
                raise RunnerError(
                    'ndt5custom',
                    'Invalid server selection algorithm specified:' +
                    selection)

            servers = servers | self._server_selection[selection].get_servers(
                group.get('servers', []))

        # Run a measurement against each of the selected servers.
        results = []
        for server in servers:
            cmdargs = [
                "ndt5-client", "-protocol=ndt5", "-format=json", "-quiet",
                "-server=" + server
            ]
            logger.info("Running ndt5custom measurement (server): " + server)
            results.append(self._run_client(cmdargs))

        return results
예제 #2
0
파일: speedtest.py 프로젝트: m-lab/murakami
    def _start_test(self):
        logger.info("Starting Speedtest multi-stream test...")
        if shutil.which("speedtest-cli") is not None:

            starttime = datetime.datetime.utcnow()
            output = subprocess.run(["speedtest-cli", "--json"],
                                    text=True,
                                    capture_output=True)
            endtime = datetime.datetime.utcnow()

            murakami_output = {
                'TestName': "speedtest-cli-multi-stream",
                'TestStartTime': starttime.strftime('%Y-%m-%dT%H:%M:%S.%f'),
                'TestEndTime': endtime.strftime('%Y-%m-%dT%H:%M:%S.%f'),
                'MurakamiLocation': self._location,
                'MurakamiConnectionType': self._connection_type,
                'MurakamiNetworkType': self._network_type,
                'MurakamiDeviceID': self._device_id,
            }

            murakami_output.update(self._parse_summary(output))
            return json.dumps(murakami_output)

        else:
            raise RunnerError(
                "speedtest",
                "Executable does not exist, please install speedtest-cli.")
예제 #3
0
파일: dash.py 프로젝트: nashtash/murakami
 def _start_test():
     logger.info("Starting DASH test...")
     if shutil.which("dash-client") is not None:
         output = subprocess.run(["dash-client"],
                                 check=True,
                                 text=True,
                                 capture_output=True)
         logger.info("Dash test complete.")
         # TODO: write parser. Only print the last line for now.
         return output.stdout.splitlines()[-1]
     else:
         raise RunnerError(
             "dash",
             "Executable dash-client does not exist, please install DASH.")
예제 #4
0
    def _run_client(self, args):
        starttime = datetime.datetime.utcnow()
        output = subprocess.run(
            args,
            text=True,
            capture_output=True,
        )
        endtime = datetime.datetime.utcnow()

        murakami_output = {
            'TestName': "ndt5",
            'TestStartTime': starttime.strftime('%Y-%m-%dT%H:%M:%S.%f'),
            'TestEndTime': endtime.strftime('%Y-%m-%dT%H:%M:%S.%f'),
            'MurakamiLocation': self._location,
            'MurakamiConnectionType': self._connection_type,
            'MurakamiNetworkType': self._network_type,
            'MurakamiDeviceID': self._device_id,
        }

        if output.returncode == 0:
            # Parse ndt5 summary.
            summary = {}
            try:
                summary = json.loads(output.stdout)
            except json.JSONDecodeError:
                raise RunnerError(
                    'ndt5-client',
                    'ndt5-client did not return a valid JSON summary.')

            logger.info("ndt5 test completed successfully.")

            # Parse ndt7-client-go's summary JSON and generate Murakami's
            # output format.
            download = summary.get('Download')
            upload = summary.get('Upload')
            retrans = summary.get('DownloadRetrans')
            min_rtt = summary.get('MinRTT')

            murakami_output['ServerName'] = summary.get('ServerFQDN')
            murakami_output['ServerIP'] = summary.get('ServerIP')
            murakami_output['ClientIP'] = summary.get('ClientIP')
            murakami_output['DownloadUUID'] = summary.get('DownloadUUID')
            if download is not None:
                murakami_output['DownloadValue'] = download.get('Value')
                murakami_output['DownloadUnit'] = download.get('Unit')
            if upload is not None:
                murakami_output['UploadValue'] = upload.get('Value')
                murakami_output['UploadUnit'] = upload.get('Unit')
            if retrans is not None:
                murakami_output['DownloadRetransValue'] = retrans.get('Value')
                murakami_output['DownloadRetransUnit'] = retrans.get('Unit')
            if min_rtt is not None:
                murakami_output['MinRTTValue'] = min_rtt.get('Value')
                murakami_output['MinRTTUnit'] = min_rtt.get('Unit')
        else:
            logger.warn("ndt5 test completed with errors.")

            # Consider any output as 'TestError'.
            murakami_output['TestError'] = output.stdout

            # All the other fields are set to None (which will become null
            # in the JSON.)
            murakami_output['ServerName'] = None
            murakami_output['ServerIP'] = None
            murakami_output['ClientIP'] = None
            murakami_output['DownloadUUID'] = None
            murakami_output['DownloadValue'] = None
            murakami_output['DownloadUnit'] = None
            murakami_output['UploadValue'] = None
            murakami_output['UploadUnit'] = None
            murakami_output['DownloadRetransValue'] = None
            murakami_output['DownloadRetransUnit'] = None
            murakami_output['MinRTTValue'] = None
            murakami_output['MinRTTUnit'] = None

        return json.dumps(murakami_output)
예제 #5
0
    def _start_test(self):
        logger.info("Starting ndt7 test...")

        # Check that a configuration file has been specified
        if "config" not in self._config:
            raise RunnerError(
                'ndt7custom',
                'No configuration file specified for the custom runner, \
                    skipping.')

        # Check that the ndt7-client executable is available.
        if shutil.which('ndt7-client') is None:
            raise RunnerError(
                'ndt7custom',
                "Executable ndt7-client does not exist, please install ndt7-client-go.",
            )

        custom_config = {}
        try:
            with open(self._config['config']) as config_file:
                custom_config = json.load(config_file)
        except IOError as err:
            raise RunnerError(
                'ndt7custom',
                'Cannot open the custom configuration file: ' + str(err))

        # Get all the servers to run measurements against from the config,
        # applying the corresponding selection algorithm.
        servers = set()
        for group in custom_config.get('serverGroups', []):
            # the default selection algorithm is 'random'
            selection = group.get('selection', 'random')
            if selection not in self._server_selection:
                raise RunnerError(
                    'ndt7custom',
                    'Invalid server selection algorithm specified:' +
                    selection)

            servers = servers | self._server_selection[selection].get_servers(
                group.get('servers', []))

        # Run a measurement against each of the selected servers.
        results = []
        for server in servers:
            cmdargs = [
                "ndt7-client", "-format=json", "-quiet", "-scheme=ws",
                "-server=" + server
            ]

            insecure = self._config.get('insecure', True)
            if insecure:
                cmdargs.append('-no-verify')

            logger.info("Running ndt7custom measurement (server): " + server)
            results.append(self._run_client(cmdargs))

        # Check for additional countries/regions specified and run the client
        # using the locate service for each of them.
        countries = custom_config.get('countries', [])
        for country in countries:
            cmdargs = [
                "ndt7-client", "-format=json", "-quiet", "-scheme=ws",
                "-locate.url=https://locate.measurementlab.net/v2/nearest/?country="
                + country
            ]
            logger.info("Running ndt7custom measurement (country): " + country)
            results.append(self._run_client(cmdargs))

        regions = custom_config.get('regions', [])
        for region in regions:
            cmdargs = [
                "ndt7-client", "-format=json", "-quiet", "-scheme=ws",
                "-locate.url=https://locate.measurementlab.net/v2/nearest/?region="
                + region
            ]
            logger.info("Running ndt7custom measurement (region): " + region)
            results.append(self._run_client(cmdargs))

        return results
예제 #6
0
파일: ooniprobe.py 프로젝트: m-lab/murakami
    def _start_test(self):
        logger.info("Starting ooniprobe test...")
        if shutil.which("ooniprobe") is not None:
            output = None

            # Empty the ooniprobe database.
            # We do that to avoid wasting disk space with ooniprobe's database (which we don't use).
            logger.info("Emptying ooniprobe database...")
            cmdargs = [
                "ooniprobe",
                "reset",
                "--force",
            ]
            try:
                output = subprocess.run(
                    cmdargs,
                    check=True,
                )
            except subprocess.CalledProcessError as e:
                raise RunnerError(
                    "ooniprobe reset --force returned a non-zero exit code.")

            # Programmatically perform ooniprobe's onboarding process.
            logger.info("Programmatically perform the onboarding process...")
            cmdargs = [
                "ooniprobe",
                "onboard",
                "--yes",
            ]

            try:
                output = subprocess.run(cmdargs,
                                        check=True,
                                        stderr=subprocess.PIPE)
            except subprocess.CalledProcessError as e:
                raise RunnerError(
                    "ooniprobe onboard --yes returned a non-zero exit code. (err: "
                    + output.stderr + ")")

            # Run "ooniprobe run unattended" to start the tests.
            logger.info("Running ooniprobe tests...")
            starttime = datetime.datetime.utcnow()
            cmdargs = [
                "ooniprobe", "--software-name=murakami-ooniprobe", "run",
                "unattended"
            ]

            try:
                output = subprocess.run(cmdargs,
                                        check=True,
                                        stderr=subprocess.PIPE)
            except subprocess.CalledProcessError as e:
                raise RunnerError(
                    "ooniprobe run unattended returned a non-zero exit code. (err: "
                    + output.stderr + ")")
            endtime = datetime.datetime.utcnow()

            # If the previous commands succeeded, then we can parse the output
            # of "ooniprobe list --batch", which only contains the results of
            # the current run (since the database was emptied beforehand).
            logging.info("Reading ooniprobe results...")
            cmdargs = ["ooniprobe", "list", "--batch"]

            try:
                output = subprocess.run(cmdargs,
                                        check=True,
                                        stdout=subprocess.PIPE,
                                        stderr=subprocess.PIPE)
            except subprocess.CalledProcessError as e:
                raise RunnerError(
                    "ooniprobe list --batch returned a non-zero exit code. (err: "
                    + output.stderr + ")")

            # Parse the output of "ooniprobe list --batch". The output is in
            # JSONL format, and we need to only parse lines with type result_item.
            results = []
            try:
                results = [
                    json.loads(line) for line in output.stdout.splitlines()
                ]
            except json.decoder.JSONDecodeError as e:
                raise RunnerError(
                    "ooniprobe list --batch returned invalid JSON. (err: " +
                    str(e) + ")")

            test_results = []
            for js in results:
                # If the JSON type is result_item, then it is a nettest result.
                # Get the corresponding nettest summary data with:
                # "ooniprobe list <id> --batch".
                if js["fields"].get(
                        "type") == "result_item" and "id" in js["fields"]:
                    # Get the test's name.
                    test_name = js["fields"]["name"]

                    cmdargs = [
                        "ooniprobe", "list",
                        str(js["fields"]["id"]), "--batch"
                    ]

                    try:
                        output = subprocess.run(cmdargs,
                                                check=True,
                                                stdout=subprocess.PIPE,
                                                stderr=subprocess.PIPE)
                    except subprocess.CalledProcessError as e:
                        raise RunnerError(
                            "ooni probe list <id> --batch returned a non-zero exit code. (err: "
                            + output.stderr + ")")

                    try:
                        # Wrap the test results in a JSON that's compatible with Murakami.
                        murakami_output = {
                            'TestName':
                            "ooniprobe-" + test_name,
                            'TestStartTime':
                            starttime.strftime('%Y-%m-%dT%H:%M:%S.%f'),
                            'TestEndTime':
                            endtime.strftime('%Y-%m-%dT%H:%M:%S.%f'),
                            'MurakamiLocation':
                            self._location,
                            'MurakamiConnectionType':
                            self._connection_type,
                            'MurakamiNetworkType':
                            self._network_type,
                            'MurakamiDeviceID':
                            self._device_id,
                        }
                        test_measurements = []
                        for line in output.stdout.splitlines():
                            js = json.loads(line)
                            if js["fields"].get("type") == "measurement_item":
                                test_measurements.append(js)
                        murakami_output["TestResults"] = test_measurements
                        test_results.append(json.dumps(murakami_output))
                    except json.decoder.JSONDecodeError as e:
                        raise RunnerError(
                            "ooni probe list <id> --batch returned invalid JSON. (err: "
                            + str(e) + ")")

            return test_results
예제 #7
0
 def _start_test(self):
     raise RunnerError(self.title, "No _start_test() function implemented.")