/**
* Copyright 2016 Yahoo Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.yahoo.pulsar.broker.admin;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Map;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.StreamingOutput;
import com.yahoo.pulsar.broker.loadbalance.LoadManager;
import org.apache.bookkeeper.mledger.proto.PendingBookieOpsStats;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.yahoo.pulsar.broker.loadbalance.ResourceUnit;
import com.yahoo.pulsar.broker.loadbalance.impl.SimpleLoadManagerImpl;
import com.yahoo.pulsar.broker.stats.AllocatorStatsGenerator;
import com.yahoo.pulsar.broker.stats.BookieClientStatsGenerator;
import com.yahoo.pulsar.broker.stats.MBeanStatsGenerator;
import com.yahoo.pulsar.common.stats.Metrics;
import com.yahoo.pulsar.broker.web.RestException;
import com.yahoo.pulsar.common.naming.NamespaceName;
import com.yahoo.pulsar.common.policies.data.loadbalancer.LoadReport;
import com.yahoo.pulsar.common.stats.AllocatorStats;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
@Path("/broker-stats")
@Api(value = "/broker-stats", description = "Stats for broker", tags = "broker-stats")
@Produces(MediaType.APPLICATION_JSON)
public class BrokerStats extends AdminResource {
private static final Logger log = LoggerFactory.getLogger(BrokerStats.class);
@GET
@Path("/metrics")
@ApiOperation(value = "Gets the metrics for Monitoring", notes = "Requested should be executed by Monitoring agent on each broker to fetch the metrics", response = Metrics.class, responseContainer = "List")
@ApiResponses(value = { @ApiResponse(code = 403, message = "Don't have admin permission") })
public Collection<Metrics> getMetrics() throws Exception {
// Ensure super user access only
validateSuperUserAccess();
try {
Collection<Metrics> metrics = pulsar().getMetricsGenerator().generate();
return metrics;
} catch (Exception e) {
log.error("[{}] Failed to generate metrics", clientAppId(), e);
throw new RestException(e);
}
}
@GET
@Path("/mbeans")
@ApiOperation(value = "Get all the mbean details of this broker JVM", response = Metrics.class, responseContainer = "List")
@ApiResponses(value = { @ApiResponse(code = 403, message = "Don't have admin permission") })
public Collection<Metrics> getMBeans() throws Exception {
// Ensure super user access only
validateSuperUserAccess();
try {
Collection<Metrics> metrics = MBeanStatsGenerator.generate(pulsar());
return metrics;
} catch (Exception e) {
log.error("[{}] Failed to generate mbean stats", clientAppId(), e);
throw new RestException(e);
}
}
@GET
@Path("/destinations")
@ApiOperation(value = "Get all the destination stats by namesapce", response = OutputStream.class, responseContainer = "OutputStream") // https://github.com/swagger-api/swagger-ui/issues/558
// map
// support
// missing
@ApiResponses(value = { @ApiResponse(code = 403, message = "Don't have admin permission") })
public StreamingOutput getDestinations2() throws Exception {
// Ensure super user access only
validateSuperUserAccess();
return output -> pulsar().getBrokerService().getDimensionMetrics(statsBuf -> {
try {
output.write(statsBuf.array(), statsBuf.arrayOffset(), statsBuf.readableBytes());
} catch (Exception e) {
throw new WebApplicationException(e);
}
});
}
@GET
@Path("/allocator-stats/{allocator}")
@ApiOperation(value = "Get the stats for the Netty allocator. Available allocators are 'default' and 'ml-cache'", response = AllocatorStats.class)
@ApiResponses(value = { @ApiResponse(code = 403, message = "Don't have admin permission") })
public AllocatorStats getAllocatorStats(@PathParam("allocator") String allocatorName) throws Exception {
// Ensure super user access only
validateSuperUserAccess();
try {
return AllocatorStatsGenerator.generate(allocatorName);
} catch (IllegalArgumentException e) {
throw new RestException(Status.NOT_ACCEPTABLE, e.getMessage());
} catch (Exception e) {
log.error("[{}] Failed to generate allocator stats", clientAppId(), e);
throw new RestException(e);
}
}
@GET
@Path("/bookieops")
@ApiOperation(value = "Get pending bookie client op stats by namesapce", response = PendingBookieOpsStats.class, // https://github.com/swagger-api/swagger-core/issues/449
// nested
// containers
// are
// not
// supported
responseContainer = "Map")
@ApiResponses(value = { @ApiResponse(code = 403, message = "Don't have admin permission") })
public Map<String, Map<String, PendingBookieOpsStats>> getPendingBookieOpsStats() throws Exception {
// Ensure super user access only
validateSuperUserAccess();
try {
return BookieClientStatsGenerator.generate(pulsar());
} catch (Exception e) {
log.error("[{}] Failed to generate pending bookie ops stats for destinations", clientAppId(), e);
throw new RestException(e);
}
}
@GET
@Path("/load-report")
@ApiOperation(value = "Get Load for this broker", notes = "consists of destinationstats & systemResourceUsage", response = LoadReport.class)
@ApiResponses(value = { @ApiResponse(code = 403, message = "Don't have admin permission") })
public LoadReport getLoadReport() throws Exception {
// Ensure super user access only
validateSuperUserAccess();
try {
return (pulsar().getLoadManager().get()).generateLoadReport();
} catch (Exception e) {
log.error("[{}] Failed to generate LoadReport for broker, reason [{}]", clientAppId(), e.getMessage(), e);
throw new RestException(e);
}
}
@GET
@Path("/broker-resource-availability/{property}/{cluster}/{namespace}")
@ApiOperation(value = "Broker availability report", notes = "This API gives the current broker availability in percent, each resource percentage usage is calculated and then"
+ "sum of all of the resource usage percent is called broker-resource-availability"
+ "<br/><br/>THIS API IS ONLY FOR USE BY TESTING FOR CONFIRMING NAMESPACE ALLOCATION ALGORITHM", response = ResourceUnit.class, responseContainer = "Map")
@ApiResponses(value = { @ApiResponse(code = 403, message = "Don't have admin permission") })
public Map<Long, Collection<ResourceUnit>> getBrokerResourceAvailability(@PathParam("property") String property,
@PathParam("cluster") String cluster, @PathParam("namespace") String namespace) throws Exception {
try {
NamespaceName ns = new NamespaceName(property, cluster, namespace);
LoadManager lm = pulsar().getLoadManager().get();
if (lm instanceof SimpleLoadManagerImpl) {
return ((SimpleLoadManagerImpl) lm).getResourceAvailabilityFor(ns).asMap();
} else {
return null;
}
} catch (Exception e) {
log.error("Unable to get Resource Availability - [{}]", e);
throw new RestException(e);
}
}
}