Extracts keyword/value settings from the ntpd config.

    The keyword is always the first entry item.
    Values are the remainder of the entries. In cases where an ntpd config
    allows multiple values, these are split according to whitespace or
    duplicate entries.

    Keywords and values are normalized. Keywords are converted to lowercase.
    Values are converted into integers, floats or strings. Strings are always
    lowercased.

    Args:
      entries: A list of items making up a single line of a ntp.conf file.

def ParseLine(self, entries):
    """Extracts keyword/value settings from the ntpd config.

    The keyword is always the first entry item.
    Values are the remainder of the entries. In cases where an ntpd config
    allows multiple values, these are split according to whitespace or
    duplicate entries.

    Keywords and values are normalized. Keywords are converted to lowercase.
    Values are converted into integers, floats or strings. Strings are always
    lowercased.

    Args:
      entries: A list of items making up a single line of a ntp.conf file.
    """
    # If no entries were found, short circuit.
    if not entries:
      return
    keyword = entries[0].lower()
    # Set the argument string if it wasn't found.
    values = entries[1:] or [""]

    # Convert any types we need too.
    if keyword in self._integers:
      values = [int(v) for v in values]
    if keyword in self._floats:
      values = [float(v) for v in values]

    if keyword not in self._repeated | self._duplicates:
      # We have a plain and simple single key/value config line.
      if isinstance(values[0], string_types):
        self.config[keyword] = " ".join(values)
      else:
        self.config[keyword] = values

    elif keyword in self._repeated:
      # The keyword can have multiple single-word options, so add them as a list
      # and overwrite previous settings.
      self.config[keyword] = values

    elif keyword in self._duplicates:
      if keyword in self._address_based:
        # If we have an address keyed keyword, join the keyword and address
        # together to make the complete key for this data.
        address = values[0].lower()
        values = values[1:] or [""]
        # Add/overwrite the address in this 'keyed' keywords dictionary.
        existing_keyword_config = self.keyed.setdefault(keyword, [])
        # Create a dict which stores the server name and the options.
        # Flatten the remaining options into a single string.
        existing_keyword_config.append({
            "address": address,
            "options": " ".join(values)
        })

      # Are we toggling an option?
      elif keyword in self._boolean:
        for option in values:
          if keyword == "enable":
            self.config[option] = True
          else:
            # As there are only two items in this set, we can assume disable.
            self.config[option] = False

      else:
        # We have a non-keyed & non-boolean keyword, so add to the collected
        # data so far. Order matters technically.
        prev_settings = self.config.setdefault(keyword, [])
        prev_settings.append(" ".join(values))