docs: def validate_config_data(self, sentry_unit, config_file, section,
                               expected):
          """Validate config file data.
  
             Verify that the specified section of the config file contains
             the expected option key:value pairs.
  
             Compare expected dictionary data vs actual dictionary data.
             The values in the 'expected' dictionary can be strings, bools, ints,
             longs, or can be a function that evaluates a variable and returns a
             bool.
             """
          self.log.debug('Validating config file data ({} in {} on {})'
                         '...'.format(section, config_file,
                                      sentry_unit.info['unit_name']))
          config = self._get_config(sentry_unit, config_file)
  
          if section != 'DEFAULT' and not config.has_section(section):
              return "section [{}] does not exist".format(section)
  
          for k in expected.keys():
              if not config.has_option(section, k):
                  return "section [{}] is missing option {}".format(section, k)
  
              actual = config.get(section, k)
              v = expected[k]
              if (isinstance(v, six.string_types) or
                      isinstance(v, bool) or
                      isinstance(v, six.integer_types)):
                  # handle explicit values
                  if actual != v:
                      return "section [{}] {}:{} != expected {}:{}".format(
                             section, k, actual, k, expected[k])
              # handle function pointers, such as not_null or valid_ip
              elif not v(actual):
                  return "section [{}] {}:{} != expected {}:{}".format(
                         section, k, actual, k, expected[k])
          return None