/*
* Copyright 1999-2008 University of Chicago
*
* 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.globus.workspace.network.defaults;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.globus.workspace.network.Association;
import org.globus.workspace.network.AssociationAdapter;
import org.globus.workspace.network.AssociationEntry;
import org.globus.workspace.persistence.PersistenceAdapter;
import org.globus.workspace.Lager;
import org.nimbustools.api.services.rm.ResourceRequestDeniedException;
import org.nimbustools.api.services.rm.ManageException;
import org.springframework.core.io.Resource;
import java.io.File;
import java.io.IOException;
import java.util.*;
public class DefaultAssociationAdapter implements AssociationAdapter {
private static final Log logger =
LogFactory.getLog(DefaultAssociationAdapter.class.getName());
private static final String MAC_MESSAGE =
"When choosing MAC addresses to use, ensure you choose a unicast address.\n" +
"That is, one with the low bit of the first octet set to zero. For example, an\n" +
"address starting aa: is OK but ab: is not. It is best to keep to the range of\n" +
"addresses declared to be \"locally assigned\" (rather than allocated globally to\n" +
"hardware vendors). These have the second lowest bit set to one in the first\n" +
"octet. For example, aa: is OK, a8: isn't.\n" +
"\n" +
"In summary, an address of the following form should be OK:\n" +
"\n" +
"XY:XX:XX:XX:XX:XX\n" +
"\n" +
"where X is any hexadecimal digit, and Y is one of 2, 6, A or E";
private final Object lock = new Object();
private List allMacs;
private static final String[] zeroLen = new String[0];
private final PersistenceAdapter persistence;
private final Lager lager;
private Resource networksDir;
private String macPrefix;
private String netSampleNetwork;
private Resource netSampleResource;
private Resource dhcpdEntriesResource;
private Resource ipMacResource;
public DefaultAssociationAdapter(PersistenceAdapter db,
Lager lagerImpl) {
if (db == null) {
throw new IllegalArgumentException("db may not be null");
}
this.persistence = db;
if (lagerImpl == null) {
throw new IllegalArgumentException("lagerImpl may not be null");
}
this.lager = lagerImpl;
}
// -------------------------------------------------------------------------
// implements AssociationAdapter
// -------------------------------------------------------------------------
public String[] getAssociationNames() throws ManageException {
final Hashtable associations = this.persistence.currentAssociations(false);
if (associations == null || associations.isEmpty()) {
return zeroLen;
} else {
final Set keys = associations.keySet();
return (String[])keys.toArray(new String[keys.size()]);
}
}
public Object[] getNextEntry(String name, int vmid)
throws ResourceRequestDeniedException {
if (this.persistence == null) {
throw new ResourceRequestDeniedException(
"networking initialization problem");
}
synchronized(this.lock) {
return Util.getNextEntry(name, this.persistence,
vmid, this.lager.eventLog);
}
}
public void retireEntry(String name, String ipAddress, int trackingID)
throws ManageException {
if (this.persistence == null) {
throw new ManageException(
"networking initialization problem");
}
synchronized(this.lock) {
Util.retireEntry(name, ipAddress, this.persistence, trackingID);
}
}
// -------------------------------------------------------------------------
// IoC INIT METHOD
// -------------------------------------------------------------------------
public void validate() throws Exception {
if (this.macPrefix != null) {
this.validateMacPrefix();
logger.info("MAC prefix: \"" + this.macPrefix + "\"");
}
if (this.networksDir != null) {
final File associationDir = this.networksDir.getFile();
Hashtable previous_associations;
try {
previous_associations = this.persistence.currentAssociations(false);
} catch (ManageException e) {
logger.error("",e);
previous_associations = null;
}
final Hashtable new_associations =
Util.loadDirectory(associationDir,
previous_associations);
if (this.macPrefix != null) {
long mstart = 0;
if (logger.isDebugEnabled()) {
mstart = System.currentTimeMillis();
}
this.allMacs = MacUtil.handleMacs(previous_associations,
new_associations,
this.macPrefix);
if (logger.isDebugEnabled()) {
final long mstop = System.currentTimeMillis();
logger.debug("MAC handling took " +
Long.toString(mstop - mstart) + "ms.");
}
}
this.persistence.replaceAssocations(new_associations);
final Enumeration en = new_associations.keys();
while (en.hasMoreElements()) {
final String assocName = (String) en.nextElement();
final Association assoc =
(Association) new_associations.get(assocName);
int numEntries = 0;
if (assoc.getEntries() != null) {
numEntries = assoc.getEntries().size();
}
if (numEntries == 1) {
logger.info("Network '" +
assocName + "' loaded with one address.");
} else {
logger.info("Network '" +
assocName + "' loaded with " + numEntries +
" addresses.");
}
}
// we write network info to various files (dhcpd entries, etc)
this.writeNetworkFiles(new_associations);
}
}
private void writeNetworkFiles(Map<String,Association> associations) {
if (this.netSampleResource != null) {
if (this.netSampleNetwork != null && this.netSampleNetwork.length() != 0) {
final Association assoc = associations.get(this.netSampleNetwork);
if (assoc == null || assoc.getEntries() == null || assoc.getEntries().isEmpty()) {
logger.warn ("Not writing netsample file because network '" +
this.netSampleNetwork + "' does not exist or has no entries");
} else {
final List entries = assoc.getEntries();
final File netsampleFile;
try {
netsampleFile = this.netSampleResource.getFile();
FileUtil.writeNetSample(netsampleFile,
(AssociationEntry) entries.get(0),
this.netSampleNetwork, assoc.getDns());
} catch (IOException e) {
logger.warn("Failed to write netsample file to "+
this.netSampleResource.getFilename(), e);
}
}
}
}
if (this.dhcpdEntriesResource != null) {
try {
final File dhcpdEntriesFile = this.dhcpdEntriesResource.getFile();
FileUtil.writeDhcpdEntries(dhcpdEntriesFile, associations);
} catch (IOException e) {
logger.warn("Failed to write dhcpd entries file to: "+
this.dhcpdEntriesResource.getFilename(), e);
}
}
if (this.ipMacResource != null) {
try {
final File ipMacFile = this.ipMacResource.getFile();
FileUtil.writeIpMacPairs(ipMacFile, associations);
} catch (IOException e) {
logger.warn("Failed to write IP -> MAC pairs to file: " +
this.ipMacResource.getFilename(), e);
}
}
}
private void validateMacPrefix() {
if (this.macPrefix.length() > 17) {
throw new IllegalArgumentException("MAC prefix is too long, " +
" it is " + this.macPrefix.length() +
" characters, max is 17");
}
if (!MacUtil.isValidMac(this.macPrefix, true)) {
throw new IllegalArgumentException(
"MAC prefix has invalid characters or format: "+
this.macPrefix);
}
final char[] macChars = this.macPrefix.toCharArray();
if (macChars.length > 1) {
boolean ok = false;
switch (macChars[1]) {
case '2': ok = true; break;
case '6': ok = true; break;
case 'A': ok = true; break;
case 'E': ok = true; break;
}
if (!ok) {
final String choice = "\n\nYou're seeing this message" +
" because you chose '" + macChars[1] +
"' as the second character in MAC prefix \"" +
this.macPrefix + "\"\n";
throw new IllegalArgumentException(MAC_MESSAGE + choice);
}
}
}
public String newMAC() throws ResourceRequestDeniedException {
if (this.macPrefix == null || this.allMacs == null) {
return null;
} else {
return MacUtil.pickNew(this.allMacs, this.macPrefix);
}
}
public void setNetworksDir(Resource dir) {
this.networksDir = dir;
}
public void setMacPrefix(String prefix) {
if (prefix == null || prefix.trim().length() == 0) {
this.macPrefix = null;
} else {
this.macPrefix = prefix.toUpperCase();
}
}
public void setNetSampleResource(Resource netSampleResource) {
this.netSampleResource = netSampleResource;
}
public Resource getNetSampleResource() {
return netSampleResource;
}
public void setDhcpdEntriesResource(Resource dhcpdEntriesResource) {
this.dhcpdEntriesResource = dhcpdEntriesResource;
}
public Resource getDhcpdEntriesResource() {
return dhcpdEntriesResource;
}
public void setIpMacResource(Resource ipMacResource) {
this.ipMacResource = ipMacResource;
}
public Resource getIpMacResource() {
return ipMacResource;
}
public String getNetSampleNetwork() {
return netSampleNetwork;
}
public void setNetSampleNetwork(String netSampleNetwork) {
this.netSampleNetwork = netSampleNetwork;
}
}