/**
* 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.admin.cli;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.ParameterException;
import com.beust.jcommander.Parameters;
import com.beust.jcommander.converters.CommaParameterSplitter;
import com.google.common.collect.Lists;
import com.yahoo.pulsar.admin.cli.utils.IOUtils;
import com.yahoo.pulsar.client.admin.PulsarAdmin;
import com.yahoo.pulsar.client.admin.PulsarAdminException;
import com.yahoo.pulsar.common.policies.data.BacklogQuota;
import com.yahoo.pulsar.common.policies.data.PersistencePolicies;
import com.yahoo.pulsar.common.policies.data.RetentionPolicies;
@Parameters(commandDescription = "Operations about namespaces")
public class CmdNamespaces extends CmdBase {
@Parameters(commandDescription = "Get the namespaces for a property")
private class GetNamespacesPerProperty extends CliCommand {
@Parameter(description = "property-name\n", required = true)
private java.util.List<String> params;
@Override
void run() throws PulsarAdminException {
String property = getOneArgument(params);
print(admin.namespaces().getNamespaces(property));
}
}
@Parameters(commandDescription = "Get the namespaces for a property in a cluster")
private class GetNamespacesPerCluster extends CliCommand {
@Parameter(description = "property/cluster\n", required = true)
private java.util.List<String> params;
@Override
void run() throws PulsarAdminException {
String[] parts = validatePropertyCluster(params);
print(admin.namespaces().getNamespaces(parts[0], parts[1]));
}
}
@Parameters(commandDescription = "Get the destinations for a namespace")
private class GetDestinations extends CliCommand {
@Parameter(description = "property/cluster/namespace\n", required = true)
private java.util.List<String> params;
@Override
void run() throws PulsarAdminException {
String namespace = validateNamespace(params);
print(admin.namespaces().getDestinations(namespace));
}
}
@Parameters(commandDescription = "Get the policies of a namspace")
private class GetPolicies extends CliCommand {
@Parameter(description = "property/cluster/namespace\n", required = true)
private java.util.List<String> params;
@Override
void run() throws PulsarAdminException {
String namespace = validateNamespace(params);
print(admin.namespaces().getPolicies(namespace));
}
}
@Parameters(commandDescription = "Creates a new namespace")
private class Create extends CliCommand {
@Parameter(description = "property/cluster/namespace\n", required = true)
private java.util.List<String> params;
@Parameter(names = { "--bundles", "-b" }, description = "number of bundles to activate", required = false)
private int numBundles = 0;
private static final long MAX_BUNDLES = ((long) 1) << 32;
@Override
void run() throws PulsarAdminException {
String namespace = validateNamespace(params);
if (numBundles < 0 || numBundles > MAX_BUNDLES) {
throw new ParameterException(
"Invalid number of bundles. Number of numbles has to be in the range of (0, 2^32].");
}
if (numBundles == 0) {
admin.namespaces().createNamespace(namespace);
} else {
admin.namespaces().createNamespace(namespace, numBundles);
}
}
}
@Parameters(commandDescription = "Deletes a namespace. The namespace needs to be empty")
private class Delete extends CliCommand {
@Parameter(description = "property/cluster/namespace\n", required = true)
private java.util.List<String> params;
@Override
void run() throws PulsarAdminException {
String namespace = validateNamespace(params);
admin.namespaces().deleteNamespace(namespace);
}
}
@Parameters(commandDescription = "Grant permissions on a namspace")
private class GrantPermissions extends CliCommand {
@Parameter(description = "property/cluster/namespace", required = true)
private java.util.List<String> params;
@Parameter(names = "--role", description = "Client role to which grant permissions", required = true)
private String role;
@Parameter(names = "--actions", description = "Actions to be granted (produce,consume)", required = true, splitter = CommaParameterSplitter.class)
private List<String> actions;
@Override
void run() throws PulsarAdminException {
String namespace = validateNamespace(params);
admin.namespaces().grantPermissionOnNamespace(namespace, role, getAuthActions(actions));
}
}
@Parameters(commandDescription = "Revoke permissions on a namspace")
private class RevokePermissions extends CliCommand {
@Parameter(description = "property/cluster/namespace", required = true)
private java.util.List<String> params;
@Parameter(names = "--role", description = "Client role to which revoke permissions", required = true)
private String role;
@Override
void run() throws PulsarAdminException {
String namespace = validateNamespace(params);
admin.namespaces().revokePermissionsOnNamespace(namespace, role);
}
}
@Parameters(commandDescription = "Get the permissions on a namspace")
private class Permissions extends CliCommand {
@Parameter(description = "property/cluster/namespace\n", required = true)
private java.util.List<String> params;
@Override
void run() throws PulsarAdminException {
String namespace = validateNamespace(params);
print(admin.namespaces().getPermissions(namespace));
}
}
@Parameters(commandDescription = "Set replication clusters for a namspace")
private class SetReplicationClusters extends CliCommand {
@Parameter(description = "property/cluster/namespace", required = true)
private java.util.List<String> params;
@Parameter(names = { "--clusters",
"-c" }, description = "Replication Cluster Ids list (comma separated values)", required = true)
private String clusterIds;
@Override
void run() throws PulsarAdminException {
String namespace = validateNamespace(params);
List<String> clusters = Lists.newArrayList(clusterIds.split(","));
admin.namespaces().setNamespaceReplicationClusters(namespace, clusters);
}
}
@Parameters(commandDescription = "Get replication clusters for a namspace")
private class GetReplicationClusters extends CliCommand {
@Parameter(description = "property/cluster/namespace\n", required = true)
private java.util.List<String> params;
@Override
void run() throws PulsarAdminException {
String namespace = validateNamespace(params);
print(admin.namespaces().getNamespaceReplicationClusters(namespace));
}
}
@Parameters(commandDescription = "Set Message TTL for a namspace")
private class SetMessageTTL extends CliCommand {
@Parameter(description = "property/cluster/namespace", required = true)
private java.util.List<String> params;
@Parameter(names = { "--messageTTL", "-ttl" }, description = "Message TTL in seconds", required = true)
private int messageTTL;
@Override
void run() throws PulsarAdminException {
String namespace = validateNamespace(params);
admin.namespaces().setNamespaceMessageTTL(namespace, messageTTL);
}
}
@Parameters(commandDescription = "Set the retention policy for a namespace")
private class SetRetention extends CliCommand {
@Parameter(description = "property/cluster/namespace", required = true)
private java.util.List<String> params;
@Parameter(names = { "--time",
"-t" }, description = "Retention time in minutes (or minutes, hours,days,weeks eg: 100m, 3h, 2d, 5w)", required = true)
private String retentionTimeStr;
@Parameter(names = { "--size", "-s" }, description = "Retention size limit (eg: 10M, 16G)", required = true)
private String limitStr;
@Override
void run() throws PulsarAdminException {
String namespace = validateNamespace(params);
long sizeLimit = validateSizeString(limitStr);
int retentionTimeInMin = validateTimeString(retentionTimeStr);
sizeLimit = sizeLimit / (1024 * 1024);
int retentionSizeInMB = (int) sizeLimit;
admin.namespaces().setRetention(namespace, new RetentionPolicies(retentionTimeInMin, retentionSizeInMB));
}
}
@Parameters(commandDescription = "Get the retention policy for a namespace")
private class GetRetention extends CliCommand {
@Parameter(description = "property/cluster/namespace\n", required = true)
private java.util.List<String> params;
@Override
void run() throws PulsarAdminException {
String namespace = validateNamespace(params);
print(admin.namespaces().getRetention(namespace));
}
}
@Parameters(commandDescription = "Get message TTL for a namspace")
private class GetMessageTTL extends CliCommand {
@Parameter(description = "property/cluster/namespace\n", required = true)
private java.util.List<String> params;
@Override
void run() throws PulsarAdminException {
String namespace = validateNamespace(params);
print(admin.namespaces().getNamespaceMessageTTL(namespace));
}
}
@Parameters(commandDescription = "Unload a namespace from the current serving broker")
private class Unload extends CliCommand {
@Parameter(description = "property/cluster/namespace\n", required = true)
private java.util.List<String> params;
@Parameter(names = { "--bundle", "-b" }, description = "{start-boundary}_{end-boundary}\n")
private String bundle;
@Override
void run() throws PulsarAdminException {
String namespace = validateNamespace(params);
if (bundle == null) {
admin.namespaces().unload(namespace);
} else {
admin.namespaces().unloadNamespaceBundle(namespace, bundle);
}
}
}
@Parameters(commandDescription = "Split a namespace-bundle from the current serving broker")
private class SplitBundle extends CliCommand {
@Parameter(description = "property/cluster/namespace/\n", required = true)
private java.util.List<String> params;
@Parameter(names = { "--bundle", "-b" }, description = "{start-boundary}_{end-boundary}\n", required = true)
private String bundle;
@Override
void run() throws PulsarAdminException {
String namespace = validateNamespace(params);
admin.namespaces().splitNamespaceBundle(namespace, bundle);
}
}
@Parameters(commandDescription = "Get the backlog quota policies for a namespace")
private class GetBacklogQuotaMap extends CliCommand {
@Parameter(description = "property/cluster/namespace\n", required = true)
private java.util.List<String> params;
@Override
void run() throws PulsarAdminException {
String namespace = validateNamespace(params);
print(admin.namespaces().getBacklogQuotaMap(namespace));
}
}
@Parameters(commandDescription = "Set a backlog quota policy for a namespace")
private class SetBacklogQuota extends CliCommand {
@Parameter(description = "property/cluster/namespace", required = true)
private java.util.List<String> params;
@Parameter(names = { "-l", "--limit" }, description = "Size limit (eg: 10M, 16G)", required = true)
private String limitStr;
@Parameter(names = { "-p", "--policy" }, description = "Retention policy to enforce when the limit is reached. "
+ "Valid options are: [producer_request_hold, producer_exception, consumer_backlog_eviction]", required = true)
private String policyStr;
@Override
void run() throws PulsarAdminException {
BacklogQuota.RetentionPolicy policy;
long limit;
try {
policy = BacklogQuota.RetentionPolicy.valueOf(policyStr);
} catch (IllegalArgumentException e) {
throw new ParameterException(String.format("Invalid retention policy type '%s'. Valid options are: %s",
policyStr, Arrays.toString(BacklogQuota.RetentionPolicy.values())));
}
limit = validateSizeString(limitStr);
String namespace = validateNamespace(params);
admin.namespaces().setBacklogQuota(namespace, new BacklogQuota(limit, policy));
}
}
@Parameters(commandDescription = "Remove a backlog quota policy from a namespace")
private class RemoveBacklogQuota extends CliCommand {
@Parameter(description = "property/cluster/namespace", required = true)
private java.util.List<String> params;
@Override
void run() throws PulsarAdminException {
String namespace = validateNamespace(params);
admin.namespaces().removeBacklogQuota(namespace);
}
}
@Parameters(commandDescription = "Get the persistence policies for a namespace")
private class GetPersistence extends CliCommand {
@Parameter(description = "property/cluster/namespace\n", required = true)
private java.util.List<String> params;
@Override
void run() throws PulsarAdminException {
String namespace = validateNamespace(params);
print(admin.namespaces().getPersistence(namespace));
}
}
@Parameters(commandDescription = "Set the persistence policies for a namespace")
private class SetPersistence extends CliCommand {
@Parameter(description = "property/cluster/namespace", required = true)
private java.util.List<String> params;
@Parameter(names = { "-e",
"--bookkeeper-ensemble" }, description = "Number of bookies to use for a topic", required = true)
private int bookkeeperEnsemble;
@Parameter(names = { "-w",
"--bookkeeper-write-quorum" }, description = "How many writes to make of each entry", required = true)
private int bookkeeperWriteQuorum;
@Parameter(names = { "-a",
"--bookkeeper-ack-quorum" }, description = "Number of acks (garanteed copies) to wait for each entry", required = true)
private int bookkeeperAckQuorum;
@Parameter(names = { "-r",
"--ml-mark-delete-max-rate" }, description = "Throttling rate of mark-delete operation (0 means no throttle)", required = true)
private double managedLedgerMaxMarkDeleteRate;
@Override
void run() throws PulsarAdminException {
String namespace = validateNamespace(params);
admin.namespaces().setPersistence(namespace, new PersistencePolicies(bookkeeperEnsemble,
bookkeeperWriteQuorum, bookkeeperAckQuorum, managedLedgerMaxMarkDeleteRate));
}
}
@Parameters(commandDescription = "Clear backlog for a namespace")
private class ClearBacklog extends CliCommand {
@Parameter(description = "property/cluster/namespace", required = true)
private java.util.List<String> params;
@Parameter(names = { "-s", "--sub" }, description = "subscription name")
private String subscription;
@Parameter(names = { "--bundle", "-b" }, description = "{start-boundary}_{end-boundary}\n")
private String bundle;
@Parameter(names = { "-force", "--force" }, description = "Whether to force clear backlog without prompt")
private boolean force;
@Override
void run() throws PulsarAdminException, IOException {
if (!force) {
String prompt = "Are you sure you want to clear the backlog?";
boolean confirm = IOUtils.confirmPrompt(prompt);
if (!confirm) {
return;
}
}
String namespace = validateNamespace(params);
if (subscription != null && bundle != null) {
admin.namespaces().clearNamespaceBundleBacklogForSubscription(namespace, bundle, subscription);
} else if (subscription != null) {
admin.namespaces().clearNamespaceBacklogForSubscription(namespace, subscription);
} else if (bundle != null) {
admin.namespaces().clearNamespaceBundleBacklog(namespace, bundle);
} else {
admin.namespaces().clearNamespaceBacklog(namespace);
}
}
}
@Parameters(commandDescription = "Unsubscribe the given subscription on all destinations on a namespace")
private class Unsubscribe extends CliCommand {
@Parameter(description = "property/cluster/namespace", required = true)
private java.util.List<String> params;
@Parameter(names = { "-s", "--sub" }, description = "subscription name", required = true)
private String subscription;
@Parameter(names = { "--bundle", "-b" }, description = "{start-boundary}_{end-boundary}\n")
private String bundle;
@Override
void run() throws Exception {
String namespace = validateNamespace(params);
if (bundle != null) {
admin.namespaces().unsubscribeNamespaceBundle(namespace, bundle, subscription);
} else {
admin.namespaces().unsubscribeNamespace(namespace, subscription);
}
}
}
private static long validateSizeString(String s) {
char last = s.charAt(s.length() - 1);
String subStr = s.substring(0, s.length() - 1);
switch (last) {
case 'k':
case 'K':
return Long.parseLong(subStr) * 1024;
case 'm':
case 'M':
return Long.parseLong(subStr) * 1024 * 1024;
case 'g':
case 'G':
return Long.parseLong(subStr) * 1024 * 1024 * 1024;
default:
return Long.parseLong(s);
}
}
private static int validateTimeString(String s) {
char last = s.charAt(s.length() - 1);
String subStr = s.substring(0, s.length() - 1);
switch (last) {
case 'm':
case 'M':
return Integer.parseInt(subStr);
case 'h':
case 'H':
return Integer.parseInt(subStr) * 60;
case 'd':
case 'D':
return Integer.parseInt(subStr) * 24 * 60;
case 'w':
case 'W':
return Integer.parseInt(subStr) * 7 * 24 * 60;
default:
return Integer.parseInt(s);
}
}
public CmdNamespaces(PulsarAdmin admin) {
super("namespaces", admin);
jcommander.addCommand("list", new GetNamespacesPerProperty());
jcommander.addCommand("list-cluster", new GetNamespacesPerCluster());
jcommander.addCommand("destinations", new GetDestinations());
jcommander.addCommand("policies", new GetPolicies());
jcommander.addCommand("create", new Create());
jcommander.addCommand("delete", new Delete());
jcommander.addCommand("permissions", new Permissions());
jcommander.addCommand("grant-permission", new GrantPermissions());
jcommander.addCommand("revoke-permission", new RevokePermissions());
jcommander.addCommand("set-clusters", new SetReplicationClusters());
jcommander.addCommand("get-clusters", new GetReplicationClusters());
jcommander.addCommand("get-backlog-quotas", new GetBacklogQuotaMap());
jcommander.addCommand("set-backlog-quota", new SetBacklogQuota());
jcommander.addCommand("remove-backlog-quota", new RemoveBacklogQuota());
jcommander.addCommand("get-persistence", new GetPersistence());
jcommander.addCommand("set-persistence", new SetPersistence());
jcommander.addCommand("get-message-ttl", new GetMessageTTL());
jcommander.addCommand("set-message-ttl", new SetMessageTTL());
jcommander.addCommand("get-retention", new GetRetention());
jcommander.addCommand("set-retention", new SetRetention());
jcommander.addCommand("unload", new Unload());
jcommander.addCommand("split-bundle", new SplitBundle());
jcommander.addCommand("clear-backlog", new ClearBacklog());
jcommander.addCommand("unsubscribe", new Unsubscribe());
}
}