|  | @@ -82,28 +82,20 @@ class XdsClusterResolverLbConfig : public LoadBalancingPolicy::Config {
 | 
	
		
			
				|  |  |    };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    XdsClusterResolverLbConfig(
 | 
	
		
			
				|  |  | -      std::vector<DiscoveryMechanism> discovery_mechanisms,
 | 
	
		
			
				|  |  | -      Json locality_picking_policy, Json endpoint_picking_policy)
 | 
	
		
			
				|  |  | +      std::vector<DiscoveryMechanism> discovery_mechanisms, Json xds_lb_policy)
 | 
	
		
			
				|  |  |        : discovery_mechanisms_(std::move(discovery_mechanisms)),
 | 
	
		
			
				|  |  | -        locality_picking_policy_(std::move(locality_picking_policy)),
 | 
	
		
			
				|  |  | -        endpoint_picking_policy_(std::move(endpoint_picking_policy)) {}
 | 
	
		
			
				|  |  | +        xds_lb_policy_(std::move(xds_lb_policy)) {}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    const char* name() const override { return kXdsClusterResolver; }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |    const std::vector<DiscoveryMechanism>& discovery_mechanisms() const {
 | 
	
		
			
				|  |  |      return discovery_mechanisms_;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -  const Json& locality_picking_policy() const {
 | 
	
		
			
				|  |  | -    return locality_picking_policy_;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  const Json& endpoint_picking_policy() const {
 | 
	
		
			
				|  |  | -    return endpoint_picking_policy_;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  const Json& xds_lb_policy() const { return xds_lb_policy_; }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |   private:
 | 
	
		
			
				|  |  |    std::vector<DiscoveryMechanism> discovery_mechanisms_;
 | 
	
		
			
				|  |  | -  Json locality_picking_policy_;
 | 
	
		
			
				|  |  | -  Json endpoint_picking_policy_;
 | 
	
		
			
				|  |  | +  Json xds_lb_policy_;
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  // Xds Cluster Resolver LB policy.
 | 
	
	
		
			
				|  | @@ -856,7 +848,11 @@ ServerAddressList XdsClusterResolverLb::CreateChildPolicyAddressesLocked() {
 | 
	
		
			
				|  |  |                                 MakeHierarchicalPathAttribute(hierarchical_path))
 | 
	
		
			
				|  |  |                  .WithAttribute(kXdsLocalityNameAttributeKey,
 | 
	
		
			
				|  |  |                                 absl::make_unique<XdsLocalityAttribute>(
 | 
	
		
			
				|  |  | -                                   locality_name->Ref())));
 | 
	
		
			
				|  |  | +                                   locality_name->Ref()))
 | 
	
		
			
				|  |  | +                .WithAttribute(ServerAddressWeightAttribute::
 | 
	
		
			
				|  |  | +                                   kServerAddressWeightAttributeKey,
 | 
	
		
			
				|  |  | +                               absl::make_unique<ServerAddressWeightAttribute>(
 | 
	
		
			
				|  |  | +                                   locality.lb_weight)));
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
	
		
			
				|  | @@ -882,36 +878,61 @@ XdsClusterResolverLb::CreateChildPolicyConfigLocked() {
 | 
	
		
			
				|  |  |        child_policy = discovery_mechanisms_[discovery_index]
 | 
	
		
			
				|  |  |                           .discovery_mechanism->override_child_policy();
 | 
	
		
			
				|  |  |      } else {
 | 
	
		
			
				|  |  | -      const auto& localities = priority_list_[priority].localities;
 | 
	
		
			
				|  |  | -      Json::Object weighted_targets;
 | 
	
		
			
				|  |  | -      for (const auto& p : localities) {
 | 
	
		
			
				|  |  | -        XdsLocalityName* locality_name = p.first;
 | 
	
		
			
				|  |  | -        const auto& locality = p.second;
 | 
	
		
			
				|  |  | -        // Construct JSON object containing locality name.
 | 
	
		
			
				|  |  | -        Json::Object locality_name_json;
 | 
	
		
			
				|  |  | -        if (!locality_name->region().empty()) {
 | 
	
		
			
				|  |  | -          locality_name_json["region"] = locality_name->region();
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        if (!locality_name->zone().empty()) {
 | 
	
		
			
				|  |  | -          locality_name_json["zone"] = locality_name->zone();
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        if (!locality_name->sub_zone().empty()) {
 | 
	
		
			
				|  |  | -          locality_name_json["subzone"] = locality_name->sub_zone();
 | 
	
		
			
				|  |  | +      const auto& xds_lb_policy = config_->xds_lb_policy().object_value();
 | 
	
		
			
				|  |  | +      if (xds_lb_policy.find("ROUND_ROBIN") != xds_lb_policy.end()) {
 | 
	
		
			
				|  |  | +        const auto& localities = priority_list_[priority].localities;
 | 
	
		
			
				|  |  | +        Json::Object weighted_targets;
 | 
	
		
			
				|  |  | +        for (const auto& p : localities) {
 | 
	
		
			
				|  |  | +          XdsLocalityName* locality_name = p.first;
 | 
	
		
			
				|  |  | +          const auto& locality = p.second;
 | 
	
		
			
				|  |  | +          // Construct JSON object containing locality name.
 | 
	
		
			
				|  |  | +          Json::Object locality_name_json;
 | 
	
		
			
				|  |  | +          if (!locality_name->region().empty()) {
 | 
	
		
			
				|  |  | +            locality_name_json["region"] = locality_name->region();
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +          if (!locality_name->zone().empty()) {
 | 
	
		
			
				|  |  | +            locality_name_json["zone"] = locality_name->zone();
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +          if (!locality_name->sub_zone().empty()) {
 | 
	
		
			
				|  |  | +            locality_name_json["subzone"] = locality_name->sub_zone();
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +          // Add weighted target entry.
 | 
	
		
			
				|  |  | +          weighted_targets[locality_name->AsHumanReadableString()] =
 | 
	
		
			
				|  |  | +              Json::Object{
 | 
	
		
			
				|  |  | +                  {"weight", locality.lb_weight},
 | 
	
		
			
				|  |  | +                  {"childPolicy",
 | 
	
		
			
				|  |  | +                   Json::Array{
 | 
	
		
			
				|  |  | +                       Json::Object{
 | 
	
		
			
				|  |  | +                           {"round_robin", Json::Object()},
 | 
	
		
			
				|  |  | +                       },
 | 
	
		
			
				|  |  | +                   }},
 | 
	
		
			
				|  |  | +              };
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | -        // Add weighted target entry.
 | 
	
		
			
				|  |  | -        weighted_targets[locality_name->AsHumanReadableString()] = Json::Object{
 | 
	
		
			
				|  |  | -            {"weight", locality.lb_weight},
 | 
	
		
			
				|  |  | -            {"childPolicy", config_->endpoint_picking_policy()},
 | 
	
		
			
				|  |  | +        // Construct locality-picking policy.
 | 
	
		
			
				|  |  | +        // Start with field from our config and add the "targets" field.
 | 
	
		
			
				|  |  | +        child_policy = Json::Array{
 | 
	
		
			
				|  |  | +            Json::Object{
 | 
	
		
			
				|  |  | +                {"weighted_target_experimental",
 | 
	
		
			
				|  |  | +                 Json::Object{
 | 
	
		
			
				|  |  | +                     {"targets", Json::Object()},
 | 
	
		
			
				|  |  | +                 }},
 | 
	
		
			
				|  |  | +            },
 | 
	
		
			
				|  |  | +        };
 | 
	
		
			
				|  |  | +        Json::Object& config =
 | 
	
		
			
				|  |  | +            *(*child_policy.mutable_array())[0].mutable_object();
 | 
	
		
			
				|  |  | +        auto it = config.begin();
 | 
	
		
			
				|  |  | +        GPR_ASSERT(it != config.end());
 | 
	
		
			
				|  |  | +        (*it->second.mutable_object())["targets"] = std::move(weighted_targets);
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        auto it = xds_lb_policy.find("RING_HASH");
 | 
	
		
			
				|  |  | +        GPR_ASSERT(it != xds_lb_policy.end());
 | 
	
		
			
				|  |  | +        Json::Object ring_hash_experimental_policy = it->second.object_value();
 | 
	
		
			
				|  |  | +        child_policy = Json::Array{
 | 
	
		
			
				|  |  | +            Json::Object{
 | 
	
		
			
				|  |  | +                {"ring_hash_experimental", ring_hash_experimental_policy},
 | 
	
		
			
				|  |  | +            },
 | 
	
		
			
				|  |  |          };
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | -      // Construct locality-picking policy.
 | 
	
		
			
				|  |  | -      // Start with field from our config and add the "targets" field.
 | 
	
		
			
				|  |  | -      child_policy = config_->locality_picking_policy();
 | 
	
		
			
				|  |  | -      Json::Object& config =
 | 
	
		
			
				|  |  | -          *(*child_policy.mutable_array())[0].mutable_object();
 | 
	
		
			
				|  |  | -      auto it = config.begin();
 | 
	
		
			
				|  |  | -      GPR_ASSERT(it != config.end());
 | 
	
		
			
				|  |  | -      (*it->second.mutable_object())["targets"] = std::move(weighted_targets);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      // Wrap it in the drop policy.
 | 
	
		
			
				|  |  |      Json::Array drop_categories;
 | 
	
	
		
			
				|  | @@ -1132,58 +1153,104 @@ class XdsClusterResolverLbFactory : public LoadBalancingPolicyFactory {
 | 
	
		
			
				|  |  |          discovery_mechanisms.emplace_back(std::move(discovery_mechanism));
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -    // Locality-picking policy.
 | 
	
		
			
				|  |  | -    Json locality_picking_policy;
 | 
	
		
			
				|  |  | -    it = json.object_value().find("localityPickingPolicy");
 | 
	
		
			
				|  |  | -    if (it == json.object_value().end()) {
 | 
	
		
			
				|  |  | -      locality_picking_policy = Json::Array{
 | 
	
		
			
				|  |  | -          Json::Object{
 | 
	
		
			
				|  |  | -              {"weighted_target_experimental",
 | 
	
		
			
				|  |  | -               Json::Object{
 | 
	
		
			
				|  |  | -                   {"targets", Json::Object()},
 | 
	
		
			
				|  |  | -               }},
 | 
	
		
			
				|  |  | -          },
 | 
	
		
			
				|  |  | -      };
 | 
	
		
			
				|  |  | -    } else {
 | 
	
		
			
				|  |  | -      locality_picking_policy = it->second;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    grpc_error* parse_error = GRPC_ERROR_NONE;
 | 
	
		
			
				|  |  | -    if (LoadBalancingPolicyRegistry::ParseLoadBalancingConfig(
 | 
	
		
			
				|  |  | -            locality_picking_policy, &parse_error) == nullptr) {
 | 
	
		
			
				|  |  | -      GPR_DEBUG_ASSERT(parse_error != GRPC_ERROR_NONE);
 | 
	
		
			
				|  |  | -      error_list.push_back(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | -          "localityPickingPolicy", &parse_error, 1));
 | 
	
		
			
				|  |  | -      GRPC_ERROR_UNREF(parse_error);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    // Endpoint-picking policy.  Called "childPolicy" for xds policy.
 | 
	
		
			
				|  |  | -    Json endpoint_picking_policy;
 | 
	
		
			
				|  |  | -    it = json.object_value().find("endpointPickingPolicy");
 | 
	
		
			
				|  |  | -    if (it == json.object_value().end()) {
 | 
	
		
			
				|  |  | -      endpoint_picking_policy = Json::Array{
 | 
	
		
			
				|  |  | -          Json::Object{
 | 
	
		
			
				|  |  | -              {"round_robin", Json::Object()},
 | 
	
		
			
				|  |  | -          },
 | 
	
		
			
				|  |  | -      };
 | 
	
		
			
				|  |  | -    } else {
 | 
	
		
			
				|  |  | -      endpoint_picking_policy = it->second;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    parse_error = GRPC_ERROR_NONE;
 | 
	
		
			
				|  |  | -    if (LoadBalancingPolicyRegistry::ParseLoadBalancingConfig(
 | 
	
		
			
				|  |  | -            endpoint_picking_policy, &parse_error) == nullptr) {
 | 
	
		
			
				|  |  | -      GPR_DEBUG_ASSERT(parse_error != GRPC_ERROR_NONE);
 | 
	
		
			
				|  |  | -      error_list.push_back(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | -          "endpointPickingPolicy", &parse_error, 1));
 | 
	
		
			
				|  |  | -      GRPC_ERROR_UNREF(parse_error);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  |      if (discovery_mechanisms.empty()) {
 | 
	
		
			
				|  |  |        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  |            "field:discovery_mechanism error:list is missing or empty"));
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +    Json xds_lb_policy = Json::Object{
 | 
	
		
			
				|  |  | +        {"ROUND_ROBIN", Json::Object()},
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +    it = json.object_value().find("xdsLbPolicy");
 | 
	
		
			
				|  |  | +    if (it != json.object_value().end()) {
 | 
	
		
			
				|  |  | +      if (it->second.type() != Json::Type::ARRAY) {
 | 
	
		
			
				|  |  | +        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | +            "field:xdsLbPolicy error:type should be array"));
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        const Json::Array& array = it->second.array_value();
 | 
	
		
			
				|  |  | +        for (size_t i = 0; i < array.size(); ++i) {
 | 
	
		
			
				|  |  | +          if (array[i].type() != Json::Type::OBJECT) {
 | 
	
		
			
				|  |  | +            error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | +                "field:xdsLbPolicy error:element should be of type object"));
 | 
	
		
			
				|  |  | +            continue;
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +          const Json::Object& policy = array[i].object_value();
 | 
	
		
			
				|  |  | +          auto policy_it = policy.find("ROUND_ROBIN");
 | 
	
		
			
				|  |  | +          if (policy_it != policy.end()) {
 | 
	
		
			
				|  |  | +            if (policy_it->second.type() != Json::Type::OBJECT) {
 | 
	
		
			
				|  |  | +              error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | +                  "field:ROUND_ROBIN error:type should be object"));
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            break;
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +          policy_it = policy.find("RING_HASH");
 | 
	
		
			
				|  |  | +          if (policy_it != policy.end()) {
 | 
	
		
			
				|  |  | +            if (policy_it->second.type() != Json::Type::OBJECT) {
 | 
	
		
			
				|  |  | +              error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | +                  "field:RING_HASH error:type should be object"));
 | 
	
		
			
				|  |  | +              continue;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            // TODO(donnadionne): Move this to a method in
 | 
	
		
			
				|  |  | +            // ring_hash_experimental and call it here.
 | 
	
		
			
				|  |  | +            const Json::Object& ring_hash = policy_it->second.object_value();
 | 
	
		
			
				|  |  | +            xds_lb_policy = array[i];
 | 
	
		
			
				|  |  | +            size_t min_ring_size = 1024;
 | 
	
		
			
				|  |  | +            size_t max_ring_size = 8388608;
 | 
	
		
			
				|  |  | +            auto ring_hash_it = ring_hash.find("min_ring_size");
 | 
	
		
			
				|  |  | +            if (ring_hash_it == ring_hash.end()) {
 | 
	
		
			
				|  |  | +              error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | +                  "field:min_ring_size missing"));
 | 
	
		
			
				|  |  | +            } else if (ring_hash_it->second.type() != Json::Type::NUMBER) {
 | 
	
		
			
				|  |  | +              error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | +                  "field:min_ring_size error: should be of "
 | 
	
		
			
				|  |  | +                  "number"));
 | 
	
		
			
				|  |  | +            } else {
 | 
	
		
			
				|  |  | +              min_ring_size = gpr_parse_nonnegative_int(
 | 
	
		
			
				|  |  | +                  ring_hash_it->second.string_value().c_str());
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            ring_hash_it = ring_hash.find("max_ring_size");
 | 
	
		
			
				|  |  | +            if (ring_hash_it == ring_hash.end()) {
 | 
	
		
			
				|  |  | +              error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | +                  "field:max_ring_size missing"));
 | 
	
		
			
				|  |  | +            } else if (ring_hash_it->second.type() != Json::Type::NUMBER) {
 | 
	
		
			
				|  |  | +              error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | +                  "field:max_ring_size error: should be of "
 | 
	
		
			
				|  |  | +                  "number"));
 | 
	
		
			
				|  |  | +            } else {
 | 
	
		
			
				|  |  | +              max_ring_size = gpr_parse_nonnegative_int(
 | 
	
		
			
				|  |  | +                  ring_hash_it->second.string_value().c_str());
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            if (min_ring_size <= 0 || min_ring_size > 8388608 ||
 | 
	
		
			
				|  |  | +                max_ring_size <= 0 || max_ring_size > 8388608 ||
 | 
	
		
			
				|  |  | +                min_ring_size > max_ring_size) {
 | 
	
		
			
				|  |  | +              error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | +                  "field:max_ring_size and or min_ring_size error: "
 | 
	
		
			
				|  |  | +                  "values need to be in the range of 1 to 8388608 "
 | 
	
		
			
				|  |  | +                  "and max_ring_size cannot be smaller than "
 | 
	
		
			
				|  |  | +                  "min_ring_size"));
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            ring_hash_it = ring_hash.find("hash_function");
 | 
	
		
			
				|  |  | +            if (ring_hash_it == ring_hash.end()) {
 | 
	
		
			
				|  |  | +              error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | +                  "field:hash_function missing"));
 | 
	
		
			
				|  |  | +            } else if (ring_hash_it->second.type() != Json::Type::STRING) {
 | 
	
		
			
				|  |  | +              error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | +                  "field:hash_function error: should be a "
 | 
	
		
			
				|  |  | +                  "string"));
 | 
	
		
			
				|  |  | +            } else if (ring_hash_it->second.string_value() != "XX_HASH" &&
 | 
	
		
			
				|  |  | +                       ring_hash_it->second.string_value() != "MURMUR_HASH_2") {
 | 
	
		
			
				|  |  | +              error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | +                  "field:hash_function error: unsupported "
 | 
	
		
			
				|  |  | +                  "hash_function"));
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            break;
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |      // Construct config.
 | 
	
		
			
				|  |  |      if (error_list.empty()) {
 | 
	
		
			
				|  |  |        return MakeRefCounted<XdsClusterResolverLbConfig>(
 | 
	
		
			
				|  |  | -          std::move(discovery_mechanisms), std::move(locality_picking_policy),
 | 
	
		
			
				|  |  | -          std::move(endpoint_picking_policy));
 | 
	
		
			
				|  |  | +          std::move(discovery_mechanisms), std::move(xds_lb_policy));
 | 
	
		
			
				|  |  |      } else {
 | 
	
		
			
				|  |  |        *error = GRPC_ERROR_CREATE_FROM_VECTOR(
 | 
	
		
			
				|  |  |            "xds_cluster_resolver_experimental LB policy config", &error_list);
 |