package org.sdnplatform.sync.client;
import java.util.ArrayList;
import org.kohsuke.args4j.Option;
import org.sdnplatform.sync.IStoreClient;
import org.sdnplatform.sync.Versioned;
import org.sdnplatform.sync.error.ObsoleteVersionException;
import org.sdnplatform.sync.internal.config.Node;
import org.sdnplatform.sync.internal.config.SyncStoreCCProvider;
import com.google.common.base.Joiner;
import com.google.common.net.HostAndPort;
/**
* This tool makes bootstrapping a cluster simpler by writing the appropriate
* configuration parameters to the local system store
* @author readams
*/
public class BootstrapTool extends SyncClientBase {
/**
* Command-line settings
*/
protected BootstrapToolSettings bSettings;
protected static class BootstrapToolSettings
extends SyncClientBaseSettings {
@Option(name="--seeds", aliases="-s",
usage="Comma-separated list of seeds specified as " +
"ipaddr:port, such as 192.168.5.2:6642,192.168.6.2:6642")
protected String seeds;
@Option(name="--localNodeDomain", aliases="-d",
usage="Set the domain ID of the local node")
protected short domainId;
@Option(name="--localNodePort",
usage="Set the listen port for the local node")
protected int localNodePort;
@Option(name="--localNodeIface",
usage="Set the interface name to listen on for the local node")
protected String localNodeIface;
@Option(name="--localNodeHost",
usage="Set the hostname for the local node (overrides " +
"localNodeIface)")
protected String localNodeHost;
@Option(name="--reseed", aliases="-r",
usage="If you simultaneously change the IP of every node " +
"in the cluster, the cluster may not automatically " +
"reform. Run this command to cause it to rerun the " +
"bootstrap process while retaining existing node IDs. " +
"The node will be put into its own local domain.")
protected boolean reseed;
@Option(name="--delete",
usage="Remove the specified node from the cluster. Note " +
"that if the node is still active it will rejoin " +
"automatically, so only run this once the node has " +
"been disabled.")
protected short deleteNode;
}
public BootstrapTool(BootstrapToolSettings bootstrapSettings) {
super(bootstrapSettings);
this.bSettings = bootstrapSettings;
}
protected void bootstrap() throws Exception {
IStoreClient<String, String> uStoreClient =
syncManager.getStoreClient(SyncStoreCCProvider.
SYSTEM_UNSYNC_STORE,
String.class, String.class);
IStoreClient<Short, Node> nodeStoreClient =
syncManager.getStoreClient(SyncStoreCCProvider.
SYSTEM_NODE_STORE,
Short.class, Node.class);
if (bSettings.localNodeIface != null) {
while (true) {
try {
uStoreClient.put(SyncStoreCCProvider.LOCAL_NODE_IFACE,
bSettings.localNodeIface);
break;
} catch (ObsoleteVersionException e) {}
}
}
if (bSettings.localNodePort != 0) {
while (true) {
try {
uStoreClient.put(SyncStoreCCProvider.LOCAL_NODE_IFACE,
Integer.toString(bSettings.localNodePort));
break;
} catch (ObsoleteVersionException e) {}
}
}
if (bSettings.localNodeHost != null) {
while (true) {
try {
uStoreClient.put(SyncStoreCCProvider.LOCAL_NODE_HOSTNAME,
bSettings.localNodeHost);
break;
} catch (ObsoleteVersionException e) {}
}
}
if (bSettings.seeds != null) {
String[] seedsStr = bSettings.seeds.split(",");
boolean seedsvalid = true;
ArrayList<HostAndPort> seedsList = new ArrayList<HostAndPort>();
for (String s : seedsStr) {
try {
seedsList.add(HostAndPort.fromString(s));
} catch (IllegalArgumentException e) {
err.println("Invalid seed: " + e.getMessage());
seedsvalid = false;
}
}
if (!seedsvalid) {
System.exit(2);
}
String seeds = Joiner.on(',').join(seedsList);
while (true) {
try {
uStoreClient.put(SyncStoreCCProvider.AUTH_SCHEME,
bSettings.authScheme.toString());
uStoreClient.put(SyncStoreCCProvider.KEY_STORE_PATH,
bSettings.keyStorePath);
uStoreClient.put(SyncStoreCCProvider.KEY_STORE_PASSWORD,
bSettings.keyStorePassword);
uStoreClient.put(SyncStoreCCProvider.SEEDS, seeds);
break;
} catch (ObsoleteVersionException e) {}
}
}
Short localNodeId = null;
if (bSettings.reseed || bSettings.domainId != 0) {
String localNodeIdStr =
waitForValue(uStoreClient,
SyncStoreCCProvider.LOCAL_NODE_ID,
10000000000L);
if (localNodeIdStr == null) {
err.println("Error: Local node ID is not set; you must " +
"first seed the cluster by using the --seeds " +
"option");
System.exit(3);
}
localNodeId = Short.valueOf(localNodeIdStr);
}
if (bSettings.reseed) {
while (true) {
try {
nodeStoreClient.delete(localNodeId);
break;
} catch (ObsoleteVersionException e) { };
}
}
if (bSettings.domainId != 0) {
while (true) {
try {
Versioned<Node> localNode =
nodeStoreClient.get(localNodeId);
if (localNode.getValue() == null) {
err.println("Could not set domain ID for local node " +
"because local node not found in system " +
"node store");
System.exit(4);
}
Node o = localNode.getValue();
localNode.setValue(new Node(o.getHostname(),
o.getPort(),
o.getNodeId(),
bSettings.domainId));
nodeStoreClient.put(localNodeId, localNode);
break;
} catch (ObsoleteVersionException e) { };
}
}
if (bSettings.deleteNode != 0) {
while (true) {
try {
nodeStoreClient.delete(bSettings.deleteNode);
break;
} catch (ObsoleteVersionException e) {}
}
}
}
private String waitForValue(IStoreClient<String, String> uStoreClient,
String key,
long maxWait) throws Exception {
long start = System.nanoTime();
while (start + maxWait > System.nanoTime()) {
String v = uStoreClient.getValue(key);
if (v != null) {
return v;
}
Thread.sleep(100);
}
return null;
}
// ****
// Main
// ****
public static void main(String[] args) throws Exception {
BootstrapToolSettings settings = new BootstrapToolSettings();
settings.init(args);
BootstrapTool client = new BootstrapTool(settings);
client.connect();
try {
client.bootstrap();
} finally {
client.cleanup();
}
}
}