def check_cf_role(self, ds):
        """
        Check variables defining cf_role for legal cf_role values.

        §9.5 The only acceptable values of cf_role for Discrete Geometry CF
        data sets are timeseries_id, profile_id, and trajectory_id

        :param netCDF4.Dataset ds: An open netCDF dataset
        :rtype: compliance_checker.base.Result
        """
        valid_roles = ['timeseries_id', 'profile_id', 'trajectory_id']
        variable_count = 0
        for variable in ds.get_variables_by_attributes(cf_role=lambda x: x is not None):
            variable_count += 1
            name = variable.name
            valid_cf_role = TestCtx(BaseCheck.HIGH, self.section_titles['9.5'])
            cf_role = variable.cf_role
            valid_cf_role.assert_true(cf_role in valid_roles,
                                      "{} is not a valid cf_role value. It must be one of {}"
                                      "".format(name, ', '.join(valid_roles)))
        if variable_count > 0:
            m = "§9.5 The only acceptable values of cf_role for Discrete Geometry CF"+\
                " data sets are timeseries_id, profile_id, and trajectory_id"
            valid_cf_role.assert_true(variable_count < 3, m)
            return valid_cf_role.to_result()
Check variables defining cf_role for legal cf_role values.

        §9.5 The only acceptable values of cf_role for Discrete Geometry CF
        data sets are timeseries_id, profile_id, and trajectory_id

        :param netCDF4.Dataset ds: An open netCDF dataset
        :rtype: compliance_checker.base.Result