/**
* Copyright 2014 NetApp Inc. All Rights Reserved.
*
* 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 org.apache.hadoop.fs.nfs.topology;
import java.io.IOException;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.nfs.NFSv3FileSystem;
import org.apache.hadoop.fs.nfs.NFSv3FileSystemStore;
public class SimpleTopologyRouter extends TopologyRouter {
NFSv3FileSystem fs;
Namespace space;
Map<Endpoint,NFSv3FileSystemStore> stores;
public final static Log LOG = LogFactory.getLog(SimpleTopologyRouter.class);
public SimpleTopologyRouter() {
stores = new HashMap<>();
}
@Override
public synchronized void initialize(NFSv3FileSystem fs, Namespace space, Configuration configuration) throws IOException {
this.fs = fs;
this.space = space;
this.configuration.addResource(configuration);
if(!verify(space)) {
throw new IOException("Check namespace to verify configuration");
}
}
private boolean verify(Namespace ns) {
// Go through the endpoints - they should not be ambiguous
// Two endpoints cannot cover the same path (unless they are identical)
// Really stupid configs are possible and this code won't detect that!
for(Endpoint epi : ns.getEndpoints()) {
for(Endpoint epj : ns.getEndpoints()) {
String pi = (epi.getPath() == null) ? "/" : epi.getPath();
String pj = (epj.getPath() == null) ? "/" : epj.getPath();
if(pi.startsWith(pj) && !pi.equals(pj)) {
LOG.warn("Two endpoints " + epi + " and " + epj + " are overlapping which could lead to confusion. Please fix.");
return false;
}
}
}
return true;
}
@Override
public synchronized NFSv3FileSystemStore getStore(Path p) throws IOException {
if(space == null) {
throw new IOException("No namespace defined!");
}
if(p == null) {
throw new IOException("Path is null!");
}
// Choose an endpoint using the path
Endpoint ep = chooseEndpoint(space, p);
if(!stores.containsKey(ep)) {
NFSv3FileSystemStore store = new NFSv3FileSystemStore(fs, space, ep);
store.initialize();
stores.put(ep, store);
}
return stores.get(ep);
}
@Override
public synchronized Endpoint chooseEndpoint(Namespace space, Path p) throws IOException {
List<Endpoint> endpoints = space.getEndpoints();
List<Endpoint> chosen = new LinkedList<>();
if(p == null || !p.isAbsolute()) {
throw new IOException("Need absolute path for choosing endpoint");
}
// Multiple endpoints are specified
if(endpoints != null && endpoints.size() > 0) {
for(Endpoint ep : endpoints) {
String epp = ep.getPath();
if(epp == null || epp.length() == 0 || Path.getPathWithoutSchemeAndAuthority(p).toString().startsWith(epp)) {
chosen.add(ep);
}
}
// No endpoint matches, so resort to using default one
if(chosen.isEmpty()) {
return space.getDefaultEndpoint();
}
// Pick one of the matching endpoints
String hostname = InetAddress.getLocalHost().getHostName();
int id = Math.abs((hostname == null ? 0 : hostname.hashCode())) % chosen.size();
LOG.debug("Choosing option " + id + " from " + chosen.size() + " options available");
return chosen.get(id);
}
else {
return null;
}
}
@Override
public synchronized List<NFSv3FileSystemStore> getAllStores() throws IOException {
return new LinkedList<>(stores.values());
}
}