def __api_openapi_descriptor(self, services, hostname=None, x_google_api_name=False):
    merged_api_info = self.__get_merged_api_info(services)
    descriptor = self.get_descriptor_defaults(merged_api_info,
                                              hostname=hostname,
                                              x_google_api_name=x_google_api_name)
    description = merged_api_info.description
    if not description and len(services) == 1:
      description = services[0].__doc__
    if description:
      descriptor['info']['description'] = description
    security_definitions = self.__security_definitions_descriptor(
        merged_api_info.issuers)
    method_map = {}
    method_collision_tracker = {}
    rest_collision_tracker = {}
    for service in services:
      remote_methods = service.all_remote_methods()
      for protorpc_meth_name in sorted(remote_methods.iterkeys()):
        protorpc_meth_info = remote_methods[protorpc_meth_name]
        method_info = getattr(protorpc_meth_info, 'method_info', None)
        if method_info is None:
          continue
        method_id = method_info.method_id(service.api_info)
        is_api_key_required = method_info.is_api_key_required(service.api_info)
        path = '/{0}/{1}/{2}'.format(merged_api_info.name,
                                     merged_api_info.path_version,
                                     method_info.get_path(service.api_info))
        verb = method_info.http_method.lower()
        if path not in method_map:
          method_map[path] = {}
        if is_api_key_required and _API_KEY not in security_definitions:
          security_definitions[_API_KEY] = {
              'type': 'apiKey',
              'name': _API_KEY_PARAM,
              'in': 'query'
          }
        operation_id = self._construct_operation_id(
            service.__name__, protorpc_meth_name)
        method_map[path][verb] = self.__method_descriptor(
            service, method_info, operation_id, protorpc_meth_info,
            security_definitions)
        if method_id in method_collision_tracker:
          raise api_exceptions.ApiConfigurationError(
              'Method %s used multiple times, in classes %s and %s' %
              (method_id, method_collision_tracker[method_id],
               service.__name__))
        else:
          method_collision_tracker[method_id] = service.__name__
        rest_identifier = (method_info.http_method,
                           method_info.get_path(service.api_info))
        if rest_identifier in rest_collision_tracker:
          raise api_exceptions.ApiConfigurationError(
              '%s path "%s" used multiple times, in classes %s and %s' %
              (method_info.http_method, method_info.get_path(service.api_info),
               rest_collision_tracker[rest_identifier],
               service.__name__))
        else:
          rest_collision_tracker[rest_identifier] = service.__name__
    if method_map:
      descriptor['paths'] = method_map
    definitions = self.__definitions_descriptor()
    if definitions:
      descriptor['definitions'] = definitions
    descriptor['securityDefinitions'] = security_definitions
    limit_definitions = self.__x_google_quota_definitions_descriptor(
        merged_api_info.limit_definitions)
    if limit_definitions:
      descriptor['x-google-management'] = limit_definitions
    return descriptor