All Downloads are FREE. Search and download functionalities are using the official Maven repository.

brooklyn.location.basic.FixedListMachineProvisioningLocation Maven / Gradle / Ivy

There is a newer version: 0.7.0-M1
Show newest version
package brooklyn.location.basic;

import static brooklyn.util.GroovyJavaMethods.truth;

import java.io.Closeable;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

import brooklyn.location.Location;
import brooklyn.location.MachineLocation;
import brooklyn.location.MachineProvisioningLocation;
import brooklyn.location.NoMachinesAvailableException;
import brooklyn.util.collections.MutableMap;
import brooklyn.util.flags.SetFromFlag;
import brooklyn.util.text.WildcardGlobs;
import brooklyn.util.text.WildcardGlobs.PhraseTreatment;

import com.google.common.base.Objects;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.io.Closeables;

/**
 * A provisioner of {@link MachineLocation}s which takes a list of machines it can connect to.
 * The collection of initial machines should be supplied in the 'machines' flag in the constructor,
 * for example a list of machines which can be SSH'd to. 
 * 
 * This can be extended to have a mechanism to make more machines to be available
 * (override provisionMore and canProvisionMore).
 */
public class FixedListMachineProvisioningLocation extends AbstractLocation 
implements MachineProvisioningLocation, Closeable {

    // TODO Synchronization looks very wrong for accessing machines/inUse 
    // e.g. removeChildLocation doesn't synchronize when doing machines.remove(...),
    // and getMachines() returns the real sets risking 
    // ConcurrentModificationException in the caller if it iterates over them etc.
    
    private Object lock;
    
    @SetFromFlag
    protected Set machines;
    
    @SetFromFlag
    protected Set inUse;

    @SetFromFlag
    protected Set pendingRemoval;
    
    public FixedListMachineProvisioningLocation() {
        this(Maps.newLinkedHashMap());
    }
    public FixedListMachineProvisioningLocation(Map properties) {
        super(properties);

        if (isLegacyConstruction()) {
            init();
        }
    }

    @Override
    public void init() {
        super.init();
        for (MachineLocation location: machines) {
            // FIXME Bad casting
            Location machine = (Location) location;
            Location parent = machine.getParent();
            if (parent != null && !parent.equals(this))
                throw new IllegalStateException("Machines must not have a parent location, but machine '"+machine.getDisplayName()+"' has its parent location set");
            addChildLocation(machine);
        }
    }
    
    @Override
    public String toVerboseString() {
        return Objects.toStringHelper(this).omitNullValues()
                .add("id", getId()).add("name", getDisplayName())
                .add("machinesAvailable", getAvailable()).add("machinesInUse", getInUse())
                .toString();
    }

    @Override
    public void configure(Map properties) {
        if (lock == null) {
            lock = new Object();
            machines = Sets.newLinkedHashSet();
            inUse = Sets.newLinkedHashSet();
            pendingRemoval = Sets.newLinkedHashSet();
        }
        super.configure(properties);
    }
    
    public FixedListMachineProvisioningLocation newSubLocation(Map newFlags) {
        return LocationCreationUtils.newSubLocation(newFlags, this);
    }

    @Override
    public void close() {
        for (T machine : machines) {
            if (machine instanceof Closeable) Closeables.closeQuietly((Closeable)machine);
        }
    }
    
    public void addMachine(T machine) {
        synchronized (lock) {
            if (machines.contains(machine)) {
                throw new IllegalArgumentException("Cannot add "+machine+" to "+toString()+", because already contained");
            }
            
            Location existingParent = ((Location)machine).getParent();
            if (existingParent != null && !existingParent.equals(this))
                throw new IllegalStateException("Machine "+machine+" must not have a parent location to be added to "+toString()+", but parent is already set to '"+existingParent+"'");
            addChildLocation((Location)machine);
            
            machines.add(machine);
        }
    }
    
    public void removeMachine(T machine) {
        synchronized (lock) {
            if (inUse.contains(machine)) {
                pendingRemoval.add(machine);
            } else {
                machines.remove(machine);
                pendingRemoval.remove(machine);
                removeChildLocation((Location)machine);
            }
        }
    }
    
    protected Set getMachines() {
        return machines;
    }
    
    public Set getAvailable() {
        Set a = Sets.newLinkedHashSet(machines);
        a.removeAll(inUse);
        return a;
    }   
     
    public Set getInUse() {
        return Sets.newLinkedHashSet(inUse);
    }   
     
    public Set getAllMachines() {
        return ImmutableSet.copyOf(machines);
    }   
     
    @Override
    public void addChild(Location child) {
        super.addChild(child);
        machines.add((T)child);
    }

    @Override
    protected boolean removeChild(Location child) {
        if (inUse.contains(child)) {
            throw new IllegalStateException("Child location "+child+" is in use; cannot remove from "+this);
        }
        machines.remove(child);
        return super.removeChild(child);
    }

    public boolean canProvisionMore() { return false; }
    public void provisionMore(int size) { throw new IllegalStateException("more not permitted"); }

    public T obtain() throws NoMachinesAvailableException {
        return obtain(Maps.newLinkedHashMap());
    }
    
    @Override
    public T obtain(Map flags) throws NoMachinesAvailableException {
        T machine;
        T desiredMachine = (T) flags.get("desiredMachine");
        
        synchronized (lock) {
            Set a = getAvailable();
            if (a.isEmpty()) {
                if (canProvisionMore()) {
                    provisionMore(1);
                    a = getAvailable();
                }
                if (a.isEmpty())
                    throw new NoMachinesAvailableException("No machines available in "+toString());
            }
            if (desiredMachine != null) {
                if (a.contains(desiredMachine)) {
                    machine = desiredMachine;
                } else {
                    throw new IllegalStateException("Desired machine "+desiredMachine+" not available in "+toString()+"; "+
                            (inUse.contains(desiredMachine) ? "machine in use" : "machine unknown"));
                }
            } else {
                machine = a.iterator().next();
            }
            inUse.add(machine);
        }
        return machine;
    }

    @Override
    public void release(T machine) {
        synchronized (lock) {
            if (inUse.contains(machine) == false)
                throw new IllegalStateException("Request to release machine "+machine+", but this machine is not currently allocated");
            inUse.remove(machine);
            
            if (pendingRemoval.contains(machine)) {
                removeMachine(machine);
            }
        }
    }

    @Override
    public Map getProvisioningFlags(Collection tags) {
        return Maps.newLinkedHashMap();
    }
    
    /**
     * Facilitates fluent/programmatic style for constructing a fixed pool of machines.
     * 
     * {@code
     *   new FixedListMachineProvisioningLocation.Builder()
     *           .user("alex")
     *           .keyFile("/Users/alex/.ssh/id_rsa")
     *           .addAddress("10.0.0.1")
     *           .addAddress("10.0.0.2")
     *           .addAddress("10.0.0.3")
     *           .addAddressMultipleTimes("[email protected]", 5)
     *           .build();
     * }
     * 
*/ public static class Builder { String user; String privateKeyPassphrase; String privateKeyFile; String privateKeyData; File localTempDir; List machines = Lists.newArrayList(); public Builder user(String user) { this.user = user; return this; } public Builder keyPassphrase(String keyPassphrase) { this.privateKeyPassphrase = keyPassphrase; return this; } public Builder keyFile(String keyFile) { this.privateKeyFile = keyFile; return this; } public Builder keyData(String keyData) { this.privateKeyData = keyData; return this; } public Builder localTempDir(File val) { this.localTempDir = val; return this; } /** adds the locations; user and keyfile set in the builder are _not_ applied to the machine * (use add(String address) for that) */ public Builder add(SshMachineLocation location) { machines.add(location); return this; } public Builder addAddress(String address) { return addAddresses(address); } public Builder addAddressMultipleTimes(String address, int n) { for (int i=0; i addrs = new ArrayList(); addrs.addAll(WildcardGlobs.getGlobsAfterBraceExpansion("{"+address1+"}", true /* numeric */, /* no quote support though */ PhraseTreatment.NOT_A_SPECIAL_CHAR, PhraseTreatment.NOT_A_SPECIAL_CHAR)); for (String address: others) addrs.addAll(WildcardGlobs.getGlobsAfterBraceExpansion("{"+address+"}", true /* numeric */, /* no quote support though */ PhraseTreatment.NOT_A_SPECIAL_CHAR, PhraseTreatment.NOT_A_SPECIAL_CHAR)); for (String addr: addrs) add(new SshMachineLocation(makeConfig(addr))); return this; } private Map makeConfig(String address) { String user = this.user; if (address.contains("@")) { user = address.substring(0, address.indexOf("@")); address = address.substring(address.indexOf("@")+1); } Map config = MutableMap.of("address", address); if (truth(user)) { config.put("user", user); config.put("sshconfig.user", user); } if (truth(privateKeyPassphrase)) config.put("sshconfig.privateKeyPassphrase", privateKeyPassphrase); if (truth(privateKeyFile)) config.put("sshconfig.privateKeyFile", privateKeyFile); if (truth(privateKeyData)) config.put("sshconfig.privateKey", privateKeyData); if (truth(localTempDir)) config.put("localTempDir", localTempDir); return config; } public FixedListMachineProvisioningLocation build() { return new FixedListMachineProvisioningLocation(MutableMap.builder() .putIfNotNull("machines", machines) .putIfNotNull("user", user) .putIfNotNull("privateKeyPassphrase", privateKeyPassphrase) .putIfNotNull("privateKeyFile", privateKeyFile) .putIfNotNull("privateKeyData", privateKeyData) .putIfNotNull("localTempDir", localTempDir) .build()); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy