@POST
 @Path("/topology/shift")
 @Produces(MediaType.TEXT_HTML)
 @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
 public Response shiftTopologies(
     @FormParam("logicalName") String logicalName,
     @FormParam("direction") int direction,
     @FormParam("unhealthyPct") @DefaultValue("0.24") float unhealthyPct,
     @FormParam("probability") @DefaultValue("0.10") float probability) {
   try {
     ShiftPredicate shiftPredicate;
     boolean caterpillar;
     if (direction == 0) {
       // "zero" means evac
       caterpillar = false;
       shiftPredicate =
           new UnhealthyTopologyShiftPredicate(unhealthyPct); // TODO should be passed in
     } else {
       // "non-zero" means shift by N places
       caterpillar = true;
       shiftPredicate = new RandomShiftPredicate(probability);
     }
     rebalanceDirector.shiftTopologies(
         Optional.of(new MiruHost(logicalName)),
         shiftPredicate,
         new CaterpillarSelectHostsStrategy(caterpillar, direction, false));
     return Response.ok("success").build();
   } catch (Throwable t) {
     LOG.error(
         "POST /topology/shift {} {} {} {} {}",
         new Object[] {logicalName, direction, unhealthyPct, probability},
         t);
     return Response.serverError().entity(t.getMessage()).build();
   }
 }
 @GET
 @Path("/balancer/export/{forceInstance}")
 @Produces(MediaType.TEXT_PLAIN)
 public Response importTopology(@PathParam("forceInstance") boolean forceInstance) {
   return Response.ok(
           (StreamingOutput) output -> rebalanceDirector.exportTopology(output, forceInstance))
       .build();
 }
 @GET
 @Path("/topology/debugTenant/{tenantId}")
 @Produces(MediaType.TEXT_HTML)
 public Response debugTenant(@PathParam("tenantId") String tenantId) {
   try {
     StringBuilder stringBuilder = new StringBuilder();
     rebalanceDirector.debugTenant(
         new MiruTenantId(tenantId.getBytes(Charsets.UTF_8)), stringBuilder);
     return Response.ok(stringBuilder.toString()).build();
   } catch (Throwable t) {
     LOG.error("GET /topology/debugTenant/{}", new Object[] {tenantId}, t);
     return Response.serverError().entity(t.getMessage()).build();
   }
 }
 @POST
 @Path("/balancer/import/{forceInstance}")
 @Consumes(MediaType.MULTIPART_FORM_DATA)
 @Produces(MediaType.TEXT_HTML)
 public Response importTopology(
     @PathParam("forceInstance") boolean forceInstance,
     @FormDataParam("file") InputStream fileInputStream,
     @FormDataParam("file") FormDataContentDisposition contentDispositionHeader) {
   try {
     rebalanceDirector.importTopology(fileInputStream, forceInstance);
     return Response.ok("success").build();
   } catch (Throwable t) {
     return Response.serverError().entity(t.getMessage()).build();
   }
 }
 @GET
 @Path("/tenants/diff/{tenantId}/{partitionId}")
 @Produces(MediaType.TEXT_PLAIN)
 public Response getTenantsForTenant(
     @PathParam("tenantId") String tenantId, @PathParam("partitionId") int partitionId) {
   try {
     String rendered =
         rebalanceDirector.diffTenantPartition(
             new MiruTenantId(tenantId.getBytes(Charsets.UTF_8)), MiruPartitionId.of(partitionId));
     return Response.ok(rendered).build();
   } catch (Throwable t) {
     LOG.error("GET /tenants/diff/{}/{}", new Object[] {tenantId, partitionId}, t);
     return Response.serverError().entity(t.getMessage()).build();
   }
 }
 @POST
 @Path("/topology/repair")
 @Produces(MediaType.TEXT_HTML)
 @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
 public Response repairTopologies() {
   try {
     rebalanceDirector.shiftTopologies(
         Optional.<MiruHost>absent(),
         (tenantId, partitionId, hostHeartbeats, partitions) -> true,
         new CaterpillarSelectHostsStrategy(true, 0, false));
     return Response.ok("success").build();
   } catch (Throwable t) {
     LOG.error("POST /topology/repair", t);
     return Response.serverError().entity(t.getMessage()).build();
   }
 }
 @POST
 @Path("/tenants/rebuild")
 @Produces(MediaType.TEXT_HTML)
 @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
 public Response rebuildTenantPartition(
     @FormParam("logicalName") String logicalName,
     @FormParam("tenantId") String tenantId,
     @FormParam("partitionId") int partitionId) {
   try {
     rebalanceDirector.rebuildTenantPartition(
         new MiruHost(logicalName),
         new MiruTenantId(tenantId.getBytes(Charsets.UTF_8)),
         MiruPartitionId.of(partitionId));
     return Response.ok("success").build();
   } catch (Throwable t) {
     LOG.error(
         "POST /tenants/rebuild {} {} {} {}",
         new Object[] {logicalName, tenantId, partitionId},
         t);
     return Response.serverError().entity(t.getMessage()).build();
   }
 }