This closes #1158
diff --git a/brooklyn-dist/downstream-parent/pom.xml b/brooklyn-dist/downstream-parent/pom.xml
index 6a7d694..f97dba3 100644
--- a/brooklyn-dist/downstream-parent/pom.xml
+++ b/brooklyn-dist/downstream-parent/pom.xml
@@ -57,7 +57,7 @@
     <jclouds.groupId>org.apache.jclouds</jclouds.groupId> <!-- JCLOUDS_GROUPID_VERSION -->
 
     <!-- versions should match those used by Brooklyn, to avoid conflicts -->
-    <jclouds.version>1.9.1</jclouds.version> <!-- JCLOUDS_VERSION -->
+    <jclouds.version>1.9.2</jclouds.version> <!-- JCLOUDS_VERSION -->
     <logback.version>1.0.7</logback.version>
     <slf4j.version>1.6.6</slf4j.version>  <!-- used for java.util.logging jul-to-slf4j interception -->
     <guava.version>17.0</guava.version>
diff --git a/brooklyn-library/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlNode.java b/brooklyn-library/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlNode.java
index 6306956..5003d4d 100644
--- a/brooklyn-library/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlNode.java
+++ b/brooklyn-library/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlNode.java
@@ -75,6 +75,9 @@
     @SetFromFlag("socketUid")
     StringAttributeSensorAndConfigKey SOCKET_UID = new StringAttributeSensorAndConfigKey(
             "mysql.socketUid", "Socket uid, for use in file /tmp/mysql.sock.<uid>.3306 (or randomly generated if not set)", null);
+
+    @SetFromFlag("generalLog")
+    ConfigKey GENERAL_LOG = ConfigKeys.newBooleanConfigKey("mysql.general_log", "Enable general log", false);
     
     /** @deprecated since 0.7.0 use DATASTORE_URL */ @Deprecated
     AttributeSensor<String> MYSQL_URL = DATASTORE_URL;
diff --git a/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql.conf b/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql.conf
index 85f55ab..40b4086 100644
--- a/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql.conf
+++ b/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql.conf
@@ -13,7 +13,15 @@
 basedir         = ${driver.baseDir}
 datadir         = ${driver.dataDir}
 bind-address    = 0.0.0.0
+log_error       = ${driver.runDir}/mysql_error_${entity.getId()}.log
+log_warnings    = 2
+general_log      = ${config["mysql.general_log"]?string('on','off')}
+general_log_file = ${driver.runDir}/mysql_general_${entity.getId()}.log
 # skip-networking
 
+#Prevent the GRANT statement from automatically creating new user accounts if it would otherwise do so,
+#unless authentication information is specified
+sql_mode = NO_AUTO_CREATE_USER
+
 # Custom configuration options
 ${driver.mySqlServerOptionsString}
\ No newline at end of file
diff --git a/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql_master.conf b/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql_master.conf
index 791f2da..54a773b 100644
--- a/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql_master.conf
+++ b/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql_master.conf
@@ -15,6 +15,10 @@
 bind-address    = 0.0.0.0
 # skip-networking
 
+#Prevent the GRANT statement from automatically creating new user accounts if it would otherwise do so,
+#unless authentication information is specified
+sql_mode = NO_AUTO_CREATE_USER
+
 # Replication config
 server-id       = 1
 binlog-format   = mixed
diff --git a/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql_slave.conf b/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql_slave.conf
index 1c69423..b4af02a 100644
--- a/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql_slave.conf
+++ b/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql_slave.conf
@@ -16,6 +16,10 @@
 bind-address    = 0.0.0.0
 # skip-networking
 
+#Prevent the GRANT statement from automatically creating new user accounts if it would otherwise do so,
+#unless authentication information is specified
+sql_mode = NO_AUTO_CREATE_USER
+
 # Replication config
 server-id       = ${config["mysql.server_id"]}
 relay-log       = mysql-slave-${config["mysql.server_id"]}-relay
diff --git a/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/kafka/KafkaClusterImpl.java b/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/kafka/KafkaClusterImpl.java
index 8f4e7fb..a95adf9 100644
--- a/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/kafka/KafkaClusterImpl.java
+++ b/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/kafka/KafkaClusterImpl.java
@@ -28,6 +28,7 @@
 import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.core.entity.trait.Startable;
 import org.apache.brooklyn.core.feed.ConfigToAttributes;
+import org.apache.brooklyn.core.location.Locations;
 import org.apache.brooklyn.enricher.stock.Enrichers;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -104,11 +105,14 @@
     @Override
     public void start(Collection<? extends Location> locations) {
         if (isLegacyConstruction()) {
+            // TODO should no longer be needed?
             init();
         }
 
-        if (locations.isEmpty()) locations = getLocations();
+        locations = MutableList.copyOf(Locations.getLocationsCheckingAncestors(locations, this));
+
         Iterables.getOnlyElement(locations); // Assert just one
+        // set it; here we don't allow changing locations
         addLocations(locations);
 
         List<Entity> childrenToStart = MutableList.<Entity>of(getCluster());
diff --git a/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/rabbit/RabbitBroker.java b/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/rabbit/RabbitBroker.java
index fc4eddf..874bed8 100644
--- a/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/rabbit/RabbitBroker.java
+++ b/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/rabbit/RabbitBroker.java
@@ -44,14 +44,19 @@
 public interface RabbitBroker extends SoftwareProcess, MessageBroker, AmqpServer {
 
     @SetFromFlag("version")
-    public static final ConfigKey<String> SUGGESTED_VERSION = ConfigKeys.newConfigKeyWithDefault(SoftwareProcess.SUGGESTED_VERSION, "2.8.7");
+    public static final ConfigKey<String> SUGGESTED_VERSION = ConfigKeys.newConfigKeyWithDefault(SoftwareProcess.SUGGESTED_VERSION, "3.6.0");
 
     @SetFromFlag("downloadUrl")
     public static final BasicAttributeSensorAndConfigKey<String> DOWNLOAD_URL = new BasicAttributeSensorAndConfigKey<String>(
-            SoftwareProcess.DOWNLOAD_URL, "http://www.rabbitmq.com/releases/rabbitmq-server/v${version}/rabbitmq-server-generic-unix-${version}.tar.gz");
+            SoftwareProcess.DOWNLOAD_URL, "http://www.rabbitmq.com/releases/rabbitmq-server/v${version}/rabbitmq-server-generic-unix-${version}.tar.xz");
     
     @SetFromFlag("erlangVersion")
-    public static final BasicConfigKey<String> ERLANG_VERSION = new BasicConfigKey<String>(String.class, "erlang.version", "Erlang runtime version", "R15B");
+    public static final BasicConfigKey<String> ERLANG_VERSION = new BasicConfigKey<String>(String.class, "erlang.version", "Erlang runtime version", "18.2");
+
+    @SetFromFlag("erlangDebRepoUrl")
+    public static final BasicConfigKey<String> ERLANG_DEB_REPO_URL = new BasicConfigKey<String>(String.class, "erlang.deb.repo.url", 
+            "Deb file used to configure an external Erlang repository which provides up to date packages for Ubuntu/Debian", 
+            "http://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb");
 
     @SetFromFlag("rabbitmqConfigTemplateUrl")
     ConfigKey<String> CONFIG_TEMPLATE_URL = ConfigKeys.newStringConfigKey(
diff --git a/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/rabbit/RabbitSshDriver.java b/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/rabbit/RabbitSshDriver.java
index c8d0a89..ad04d55 100644
--- a/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/rabbit/RabbitSshDriver.java
+++ b/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/rabbit/RabbitSshDriver.java
@@ -27,7 +27,6 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 
@@ -39,6 +38,7 @@
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.net.Networking;
 import org.apache.brooklyn.util.os.Os;
+import org.apache.brooklyn.util.text.Strings;
 
 /**
  * TODO javadoc
@@ -81,40 +81,32 @@
     public void install() {
         List<String> urls = resolver.getTargets();
         String saveAs = resolver.getFilename();
-        // Version and architecture are only required for download of epel package on RHEL/Centos systems so pick sensible
-        // defaults if unavailable
-        String osMajorVersion = getMachine().getOsDetails().getVersion();
-        if (Strings.isNullOrEmpty(osMajorVersion)) {
-            osMajorVersion = "7";
-        } else {
-            osMajorVersion = osMajorVersion.indexOf(".") > 0 ? osMajorVersion.substring(0, osMajorVersion.indexOf('.')) : osMajorVersion;
-            if (!CENTOS_VERSION_TO_EPEL_VERSION.keySet().contains(osMajorVersion)) {
-                osMajorVersion = "7";
-            }
-        }
-        String epelVersion = CENTOS_VERSION_TO_EPEL_VERSION.get(osMajorVersion);
-        String osArchitecture = getMachine().getOsDetails().getArch();
-        if (Strings.isNullOrEmpty(osArchitecture)) {
-            osArchitecture = "x86_64";
-        }
+
 
         List<String> commands = ImmutableList.<String>builder()
-                // EPEL repository for erlang install required on some Centos distributions
-                .add(chainGroup("which yum", sudo("yum -y update ca-certificates"), sudo("rpm -Uvh --replacepkgs " +
-                        format("http://download.fedoraproject.org/pub/epel/%s/%s/epel-release-%s.noarch.rpm", osMajorVersion, osArchitecture, epelVersion))))
-                .add(ifExecutableElse0("zypper", chainGroup(
-                        ok(sudo("zypper --non-interactive addrepo http://download.opensuse.org/repositories/devel:/languages:/erlang/SLE_11_SP3 erlang_sles_11")),
-                        ok(sudo("zypper --non-interactive addrepo http://download.opensuse.org/repositories/devel:/languages:/erlang/openSUSE_11.4 erlang_suse_11")),
-                        ok(sudo("zypper --non-interactive addrepo http://download.opensuse.org/repositories/devel:/languages:/erlang/openSUSE_12.3 erlang_suse_12")),
-                        ok(sudo("zypper --non-interactive addrepo http://download.opensuse.org/repositories/devel:/languages:/erlang/openSUSE_13.1 erlang_suse_13")))))
+                // RabbitMQ recommends Erlang 18. Release notes state:
+                // ====
+                // Minimum required Erlang version is R16B03 for plain ("just TCP") connections for all protocols 
+                // and 17.5 for TLS ones (18.x is recommended for both).
+                // ====
+                //
+                // The recommended provider for up to date Erlang versions is Erlang Solutions now.
+                // Supported platforms by Erlang Solutions are: CentOS, RHEL, Ubuntu and Debian.
+                // 
+                // New Erlang versions for SUSE are provided via the openSUSE repositories
+                // 
+                // EPEL 6 provides only packages for Erlang 14, but EPEL 7 - Erlang 16
+                // 
+                .add(ifExecutableElse0("apt-get", getAptRepository()))
+                .add(ifExecutableElse0("yum", getYumRepository()))
+                .add(ifExecutableElse0("zypper", getZypperRepository()))
                 .add(installPackage( // NOTE only 'port' states the version of Erlang used, maybe remove this constraint?
                         ImmutableMap.of(
-                                "apt", "erlang-nox erlang-dev",
                                 "port", "erlang@"+getErlangVersion()+"+ssl"),
-                        "erlang"))
+                                "erlang"))
                 .addAll(commandsToDownloadUrlsAs(urls, saveAs))
                 .add(installExecutable("tar"))
-                .add(format("tar xvzf %s",saveAs))
+                .add(format("tar xvf %s",saveAs))
                 .build();
 
         newScript(INSTALLING).
@@ -147,9 +139,9 @@
         newScript(MutableMap.of("usePidFile", false), LAUNCHING)
             .body.append(
                 "nohup ./sbin/rabbitmq-server > console-out.log 2> console-err.log &",
-                "for i in {1..30}\n" +
+                "for i in {1..60}\n" +
                     "do\n" +
-                     "    grep 'broker running' console-out.log && exit\n" +
+                     "    grep 'Starting broker... completed' console-out.log && exit\n" +
                      "    sleep 1\n" +
                      "done",
                 "echo \"Couldn't determine if rabbitmq-server is running\"",
@@ -166,7 +158,6 @@
             ).execute();
     }
 
-
     public String getPidFile() { return "rabbitmq.pid"; }
 
     @Override
@@ -183,7 +174,6 @@
                 .execute();
     }
 
-
     @Override
     public void kill() {
         stop(); // TODO No pid file to easily do `kill -9`
@@ -205,4 +195,69 @@
     private String getConfigPath() {
         return getRunDir() + "/rabbitmq";
     }
+
+    private String getYumRepository() {
+        String yumRepoFileName = "erlang_solutions.repo";
+        
+        // Version and architecture are only required for download of epel package on RHEL/Centos systems so pick sensible
+        // defaults if unavailable
+        //
+        // EPEL is still required as it is a prerequisite for the packages provided by Erlang Solutions
+        
+        String osMajorVersion = getMachine().getOsDetails().getVersion();
+        if (Strings.isBlank(osMajorVersion)) {
+            osMajorVersion = "7";
+        } else {
+            osMajorVersion = osMajorVersion.indexOf(".") > 0 ? osMajorVersion.substring(0, osMajorVersion.indexOf('.')) : osMajorVersion;
+            if (!CENTOS_VERSION_TO_EPEL_VERSION.keySet().contains(osMajorVersion)) {
+                osMajorVersion = "7";
+            }
+        }
+        String epelVersion = CENTOS_VERSION_TO_EPEL_VERSION.get(osMajorVersion);
+        String osArchitecture = getMachine().getOsDetails().getArch();
+        if (Strings.isBlank(osArchitecture)) {
+            osArchitecture = "x86_64";
+        }
+
+        // Erlang Solutions provide separate packages for RHEL and CentOS, but they are hosted in a single repo.
+        // E.g. - http://packages.erlang-solutions.com/rpm/centos/6/x86_64/
+        // 
+        // Erlang Solutions created a repo configuration RPM which can NOT be used on RedHat, because it has explicit checks for CentOS.
+        // 
+        // Bellow we are creating a repo file that works for CentOS and RedHat
+        return chainGroup(
+                sudo("yum -y update ca-certificates"),
+                sudo("rpm -Uvh --replacepkgs " + format("http://download.fedoraproject.org/pub/epel/%s/%s/epel-release-%s.noarch.rpm", osMajorVersion, osArchitecture, epelVersion)),
+                "( cat << 'EOF_BROOKLYN'\n"
+                   + "[erlang-solutions]\n"
+                   + "name=Centos / RHEL " + osMajorVersion + " - $basearch - Erlang Solutions\n"
+                   + "baseurl=http://packages.erlang-solutions.com/rpm/centos/" + osMajorVersion + "/$basearch\n"
+                   + "gpgcheck=0\n"
+                   + "gpgkey=http://packages.erlang-solutions.com/debian/erlang_solutions.asc\n"
+                   + "enabled=1\n"
+                   + "EOF_BROOKLYN\n"
+                   + ") > " + yumRepoFileName,
+                  sudo(format("mv %s /etc/yum.repos.d/", yumRepoFileName)),
+                  sudo(format("chown root:root /etc/yum.repos.d/%s", yumRepoFileName))
+            );
+    }
+
+    private String getAptRepository() {
+        String debFileName = "erlang-repo.deb";
+
+        return chainGroup(
+                INSTALL_WGET,
+                format("wget --quiet -O %s %s", debFileName, entity.getConfig(RabbitBroker.ERLANG_DEB_REPO_URL)),
+                sudo(format("dpkg -i %s", debFileName))
+            );
+    }
+
+    private String getZypperRepository() {
+        return chainGroup(
+                ok(sudo("zypper --non-interactive addrepo http://download.opensuse.org/repositories/devel:/languages:/erlang/SLE_11_SP3 erlang_sles_11")),
+                ok(sudo("zypper --non-interactive addrepo http://download.opensuse.org/repositories/devel:/languages:/erlang/openSUSE_11.4 erlang_suse_11")),
+                ok(sudo("zypper --non-interactive addrepo http://download.opensuse.org/repositories/devel:/languages:/erlang/openSUSE_12.3 erlang_suse_12")),
+                ok(sudo("zypper --non-interactive addrepo http://download.opensuse.org/repositories/devel:/languages:/erlang/openSUSE_13.1 erlang_suse_13")));
+    }
 }
+
diff --git a/brooklyn-library/software/messaging/src/main/resources/org/apache/brooklyn/entity/messaging/rabbit/rabbitmq.config b/brooklyn-library/software/messaging/src/main/resources/org/apache/brooklyn/entity/messaging/rabbit/rabbitmq.config
index b4428f0..c350b65 100644
--- a/brooklyn-library/software/messaging/src/main/resources/org/apache/brooklyn/entity/messaging/rabbit/rabbitmq.config
+++ b/brooklyn-library/software/messaging/src/main/resources/org/apache/brooklyn/entity/messaging/rabbit/rabbitmq.config
@@ -1,5 +1,6 @@
 [
 <#if entity.enableManagementPlugin>
+    {rabbit, [{loopback_users, []}]},
     {rabbitmq_mochiweb, [{listeners, [{mgmt, [{port, ${entity.managementPort?c}}]}]}]}
 </#if>
 ].
\ No newline at end of file
diff --git a/brooklyn-library/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/ControlledDynamicWebAppClusterImpl.java b/brooklyn-library/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/ControlledDynamicWebAppClusterImpl.java
index 78898bd..8c52746 100644
--- a/brooklyn-library/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/ControlledDynamicWebAppClusterImpl.java
+++ b/brooklyn-library/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/ControlledDynamicWebAppClusterImpl.java
@@ -38,6 +38,7 @@
 import org.apache.brooklyn.core.entity.trait.Startable;
 import org.apache.brooklyn.core.entity.trait.StartableMethods;
 import org.apache.brooklyn.core.feed.ConfigToAttributes;
+import org.apache.brooklyn.core.location.Locations;
 import org.apache.brooklyn.enricher.stock.Enrichers;
 import org.apache.brooklyn.entity.group.DynamicGroupImpl;
 import org.apache.brooklyn.entity.proxy.LoadBalancer;
@@ -207,7 +208,8 @@
                 init();
             }
 
-            if (locations.isEmpty()) locations = getLocations();
+            locations = Locations.getLocationsCheckingAncestors(locations, this);
+            // store inherited locations
             addLocations(locations);
 
             LoadBalancer loadBalancer = getController();
@@ -229,7 +231,8 @@
                 }
             }
 
-            Entities.invokeEffectorList(this, childrenToStart, Startable.START, ImmutableMap.of("locations", locations)).get();
+            // don't propagate start locations
+            Entities.invokeEffectorList(this, childrenToStart, Startable.START, ImmutableMap.of("locations", MutableList.of())).get();
             if (startControllerTask != null) {
                 startControllerTask.get();
             }
diff --git a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EntitiesYamlTest.java b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EntitiesYamlTest.java
index af2faf9..337c302 100644
--- a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EntitiesYamlTest.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EntitiesYamlTest.java
@@ -53,6 +53,7 @@
 import org.apache.brooklyn.core.test.entity.TestEntity;
 import org.apache.brooklyn.core.test.entity.TestEntityImpl;
 import org.apache.brooklyn.entity.group.DynamicCluster;
+import org.apache.brooklyn.entity.group.DynamicFabric;
 import org.apache.brooklyn.entity.software.base.SameServerEntity;
 import org.apache.brooklyn.entity.stock.BasicEntity;
 import org.apache.brooklyn.util.collections.MutableMap;
@@ -69,6 +70,7 @@
 import org.testng.annotations.Test;
 import org.testng.collections.Lists;
 
+import com.google.common.base.Joiner;
 import com.google.common.base.Suppliers;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -726,6 +728,48 @@
     }
 
     @Test
+    public void testCreateFabricWithLocationsAtTopLevel() throws Exception {
+        String yaml = Joiner.on("\n").join(
+                "services:",
+                "- type: org.apache.brooklyn.entity.group.DynamicFabric",
+                "  memberSpec:",
+                "    $brooklyn:entitySpec:",
+                "      type: org.apache.brooklyn.core.test.entity.TestEntity",
+                "locations:",
+                "- byon(hosts=\"1.1.1.1\")",
+                "- byon(hosts=\"1.1.1.2\")"
+                );
+
+        Entity app = createAndStartApplication(yaml);
+        waitForApplicationTasks(app);
+        DynamicFabric fabric = Iterables.getOnlyElement(Entities.descendants(app, DynamicFabric.class));
+        Iterable<TestEntity> members = Entities.descendants(fabric, TestEntity.class);
+        
+        assertEquals(Iterables.size(members), 2);
+    }
+
+    @Test
+    public void testCreateFabricWithLocationsInline() throws Exception {
+        String yaml = Joiner.on("\n").join(
+                "services:",
+                "- type: org.apache.brooklyn.entity.group.DynamicFabric",
+                "  memberSpec:",
+                "    $brooklyn:entitySpec:",
+                "      type: org.apache.brooklyn.core.test.entity.TestEntity",
+                "  locations:",
+                "  - byon(hosts=\"1.1.1.1\")",
+                "  - byon(hosts=\"1.1.1.2\")"
+                );
+
+        Entity app = createAndStartApplication(yaml);
+        waitForApplicationTasks(app);
+        DynamicFabric fabric = Iterables.getOnlyElement(Entities.descendants(app, DynamicFabric.class));
+        Iterable<TestEntity> members = Entities.descendants(fabric, TestEntity.class);
+        
+        assertEquals(Iterables.size(members), 2);
+    }
+
+    @Test
     public void testEntitySpecConfig() throws Exception {
         String yaml =
                 "services:\n"+
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
index 0599373..5422fb6 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
@@ -874,15 +874,18 @@
 
     @Override
     public void addLocations(Collection<? extends Location> newLocations) {
+        if (newLocations==null || newLocations.isEmpty()) {
+            return;
+        }
         synchronized (locations) {
             List<Location> oldLocations = locations.get();
-            Set<Location> truelyNewLocations = Sets.newLinkedHashSet(newLocations);
-            truelyNewLocations.removeAll(oldLocations);
-            if (truelyNewLocations.size() > 0) {
-                locations.set(ImmutableList.<Location>builder().addAll(oldLocations).addAll(truelyNewLocations).build());
+            Set<Location> trulyNewLocations = Sets.newLinkedHashSet(newLocations);
+            trulyNewLocations.removeAll(oldLocations);
+            if (trulyNewLocations.size() > 0) {
+                locations.set(ImmutableList.<Location>builder().addAll(oldLocations).addAll(trulyNewLocations).build());
             }
             
-            for (Location loc : truelyNewLocations) {
+            for (Location loc : trulyNewLocations) {
                 sensors().emit(AbstractEntity.LOCATION_ADDED, loc);
             }
         }
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/EntityInternal.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/EntityInternal.java
index 4056eca..3602bee 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/EntityInternal.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/EntityInternal.java
@@ -21,6 +21,8 @@
 import java.util.Collection;
 import java.util.Map;
 
+import javax.annotation.Nullable;
+
 import org.apache.brooklyn.api.effector.Effector;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.EntityLocal;
@@ -48,7 +50,7 @@
 @Beta
 public interface EntityInternal extends BrooklynObjectInternal, EntityLocal, Rebindable {
     
-    void addLocations(Collection<? extends Location> locations);
+    void addLocations(@Nullable Collection<? extends Location> locations);
 
     void removeLocations(Collection<? extends Location> locations);
 
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/location/BasicLocationRegistry.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/location/BasicLocationRegistry.java
index 4954393..60fb061 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/location/BasicLocationRegistry.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/location/BasicLocationRegistry.java
@@ -430,7 +430,9 @@
     public List<Location> resolve(Iterable<?> spec) {
         List<Location> result = new ArrayList<Location>();
         for (Object id : spec) {
-            if (id instanceof String) {
+            if (id==null) {
+                // drop a null entry
+            } if (id instanceof String) {
                 result.add(resolve((String) id));
             } else if (id instanceof Location) {
                 result.add((Location) id);
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicClusterImpl.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicClusterImpl.java
index 16a82d4..f434fcf 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicClusterImpl.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicClusterImpl.java
@@ -21,6 +21,7 @@
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
@@ -311,15 +312,16 @@
         setConfigEvenIfOwned(FACTORY, factory);
     }
 
-    private Location getLocation() {
+    private Location getLocation(boolean required) {
         Collection<? extends Location> ll = Locations.getLocationsCheckingAncestors(getLocations(), this);
-        try {
-            return Iterables.getOnlyElement(ll);
-        } catch (Exception e) {
-            Exceptions.propagateIfFatal(e);
-            if (ll.isEmpty()) throw new IllegalStateException("No location available for "+this);
-            else throw new IllegalStateException("Ambiguous location for "+this+"; expected one but had "+ll);
+        if (ll.isEmpty()) {
+            if (!required) return null;
+            throw new IllegalStateException("No location available for "+this);
         }
+        if (ll.size()>1) {
+            throw new IllegalStateException("Ambiguous location for "+this+"; expected one but had "+ll);
+        }
+        return Iterables.getOnlyElement(ll);
     }
 
     protected boolean isAvailabilityZoneEnabled() {
@@ -360,18 +362,17 @@
 
     @Override
     public void start(Collection<? extends Location> locsO) {
-        if (locsO!=null) {
-            checkArgument(locsO.size() <= 1, "Wrong number of locations supplied to start %s: %s", this, locsO);
-            addLocations(locsO);
-        }
-        Location loc = getLocation();
+        addLocations(locsO);
+        Location loc = getLocation(false);
 
         EntitySpec<?> spec = getConfig(MEMBER_SPEC);
         if (spec!=null) {
-            setDefaultDisplayName("Cluster of "+JavaClassNames.simpleClassName(spec.getType()) +" ("+loc+")");
+            setDefaultDisplayName("Cluster of "+JavaClassNames.simpleClassName(spec.getType()) +
+                (loc!=null ? " ("+loc+")" : ""));
         }
 
         if (isAvailabilityZoneEnabled()) {
+            if (loc==null) throw new IllegalStateException("When using availability zones, a location must be specified on the cluster");
             sensors().set(SUB_LOCATIONS, findSubLocations(loc));
         }
 
@@ -574,7 +575,7 @@
             Location memberLoc = null;
             if (isAvailabilityZoneEnabled()) {
                 // this member's location could be a machine provisioned by a sub-location, or the actual sub-location
-                List<Location> subLocations = findSubLocations(getLocation());
+                List<Location> subLocations = findSubLocations(getLocation(true));
                 Collection<Location> actualMemberLocs = member.getLocations();
                 boolean foundMatch = false;
                 for (Iterator<Location> iter = actualMemberLocs.iterator(); !foundMatch && iter.hasNext();) {
@@ -605,7 +606,7 @@
                 // Replacing member, so new member should be in the same location as that being replaced.
                 // Expect this to agree with `getMemberSpec().getLocations()` (if set). If not, then 
                 // presumably there was a reason this specific member was started somewhere else!
-                memberLoc = getLocation();
+                memberLoc = getLocation(false);
             }
 
             Entity replacement = replaceMember(member, memberLoc, ImmutableMap.of());
@@ -616,7 +617,7 @@
     /**
      * @throws StopFailedRuntimeException If stop failed, after successfully starting replacement
      */
-    protected Entity replaceMember(Entity member, Location memberLoc, Map<?, ?> extraFlags) {
+    protected Entity replaceMember(Entity member, @Nullable Location memberLoc, Map<?, ?> extraFlags) {
         synchronized (mutex) {
             ReferenceWithError<Optional<Entity>> added = addInSingleLocation(memberLoc, extraFlags);
 
@@ -653,7 +654,7 @@
     protected List<Location> getNonFailedSubLocations() {
         List<Location> result = Lists.newArrayList();
         Set<Location> failed = Sets.newLinkedHashSet();
-        List<Location> subLocations = findSubLocations(getLocation());
+        List<Location> subLocations = findSubLocations(getLocation(true));
         Set<Location> oldFailedSubLocations = getAttribute(FAILED_SUB_LOCATIONS);
         if (oldFailedSubLocations == null)
             oldFailedSubLocations = ImmutableSet.<Location> of();
@@ -722,7 +723,7 @@
                         + ", when expected delta " + delta + " in " + this);
             }
         } else {
-            chosenLocations = Collections.nCopies(delta, getLocation());
+            chosenLocations = Collections.nCopies(delta, getLocation(false));
         }
 
         // create and start the entities.
@@ -759,8 +760,8 @@
         }
     }
 
-    protected ReferenceWithError<Optional<Entity>> addInSingleLocation(Location location, Map<?,?> flags) {
-        ReferenceWithError<Collection<Entity>> added = addInEachLocation(ImmutableList.of(location), flags);
+    protected ReferenceWithError<Optional<Entity>> addInSingleLocation(@Nullable Location location, Map<?,?> flags) {
+        ReferenceWithError<Collection<Entity>> added = addInEachLocation(Arrays.asList(location), flags);
         
         Optional<Entity> result = Iterables.isEmpty(added.getWithoutError()) ? Optional.<Entity>absent() : Optional.of(Iterables.getOnlyElement(added.get()));
         if (!added.hasError()) {
@@ -885,7 +886,7 @@
     }
 
     @Override
-    public Entity addNode(Location loc, Map<?, ?> extraFlags) {
+    public Entity addNode(@Nullable Location loc, Map<?, ?> extraFlags) {
         // In case subclasses are foolish and do not call super.init() when overriding.
         initialiseMemberId();
         Map<?, ?> createFlags = MutableMap.builder()
@@ -917,7 +918,9 @@
         if (memberSpec == null) memberSpec = getMemberSpec();
         
         if (memberSpec != null) {
-            return addChild(EntitySpec.create(memberSpec).configure(flags).location(loc));
+            EntitySpec<?> specConfigured = EntitySpec.create(memberSpec).configure(flags);
+            if (loc!=null) specConfigured.location(loc);
+            return addChild(specConfigured);
         }
 
         EntityFactory<?> factory = getFactory();
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicFabricImpl.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicFabricImpl.java
index 67c4c79..34051c5 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicFabricImpl.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicFabricImpl.java
@@ -41,6 +41,7 @@
 import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
 import org.apache.brooklyn.core.entity.trait.Changeable;
 import org.apache.brooklyn.core.entity.trait.Startable;
+import org.apache.brooklyn.core.location.Locations;
 import org.apache.brooklyn.enricher.stock.Enrichers;
 import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.exceptions.Exceptions;
@@ -109,15 +110,11 @@
     
     @Override
     public void start(Collection<? extends Location> locsO) {
-        if (locsO!=null) {
-            addLocations(locsO);
-        }
+        addLocations(locsO);
+        List<Location> locationsToStart = MutableList.copyOf(Locations.getLocationsCheckingAncestors(locsO, this));
         
-        List<Location> newLocations = MutableList.copyOf(locsO);
-        if (newLocations.isEmpty()) newLocations.addAll(getLocations());
-        
-        Preconditions.checkNotNull(newLocations, "locations must be supplied");
-        Preconditions.checkArgument(newLocations.size() >= 1, "One or more locations must be supplied");
+        Preconditions.checkNotNull(locationsToStart, "locations must be supplied");
+        Preconditions.checkArgument(locationsToStart.size() >= 1, "One or more locations must be supplied");
         
         int locIndex = 0;
         
@@ -133,8 +130,8 @@
                     Location it = null;
                     if (child.getLocations().isEmpty())
                         // give him any of these locations if he has none, allowing round robin here
-                        if (!newLocations.isEmpty()) {
-                            it = newLocations.get(locIndex++ % newLocations.size());
+                        if (!locationsToStart.isEmpty()) {
+                            it = locationsToStart.get(locIndex++ % locationsToStart.size());
                             ((EntityInternal)child).addLocations(Arrays.asList(it));
                         }
                     
@@ -144,12 +141,12 @@
                 }
             }
             // remove all the locations we applied to existing nodes
-            while (locIndex-->0 && !newLocations.isEmpty())
-                newLocations.remove(0);
+            while (locIndex-->0 && !locationsToStart.isEmpty())
+                locationsToStart.remove(0);
 
             // finally (and usually) we create new entities for locations passed in
             // (unless they were consumed by pre-existing children which didn't have locations)
-            for (Location it : newLocations) {
+            for (Location it : locationsToStart) {
                 Entity e = addCluster(it);
                 
                 ((EntityInternal)e).addLocations(Arrays.asList(it));
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartableImpl.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartableImpl.java
index b5a8616..54be703 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartableImpl.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartableImpl.java
@@ -48,11 +48,12 @@
 
     @Override
     public void start(Collection<? extends Location> locations) {
-        log.info("Starting entity "+this+" at "+locations);
         try {
             ServiceStateLogic.setExpectedState(this, Lifecycle.STARTING);
 
             addLocations(locations);
+            locations = Locations.getLocationsCheckingAncestors(locations, this);
+            log.info("Starting entity "+this+" at "+locations);
 
             // essentially does StartableMethods.start(this, locations),
             // but optionally filters locations for each child
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/stock/DataEntityImpl.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/stock/DataEntityImpl.java
index 9ee3b28..cc6d8d7 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/stock/DataEntityImpl.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/stock/DataEntityImpl.java
@@ -38,6 +38,7 @@
 
     @Override
     public void start(Collection<? extends Location> locations) {
+        addLocations(locations);
         connectSensors();
         sensors().set(SERVICE_UP, Boolean.TRUE);
     }
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/entity/group/DynamicClusterTest.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/entity/group/DynamicClusterTest.java
index f58ac90..b58c630 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/entity/group/DynamicClusterTest.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/entity/group/DynamicClusterTest.java
@@ -110,9 +110,9 @@
         try {
             app.createAndManageChild(EntitySpec.create(DynamicCluster.class)
                     .configure("factory", "error"));
-            fail();
+            Asserts.shouldHaveFailedPreviously();
         } catch (Exception e) {
-            if (Exceptions.getFirstThrowableOfType(e, IllegalArgumentException.class) == null) throw e;
+            Asserts.expectedFailure(e);
         }
     }
 
@@ -121,9 +121,9 @@
         DynamicCluster c = app.createAndManageChild(EntitySpec.create(DynamicCluster.class));
         try {
             c.start(ImmutableList.of(loc));
-            fail();
+            Asserts.shouldHaveFailedPreviously();
         } catch (Exception e) {
-            if (Exceptions.getFirstThrowableOfType(e, IllegalStateException.class) == null) throw e;
+            Asserts.expectedFailureOfType(e, IllegalStateException.class);
         }
     }
     
@@ -135,9 +135,9 @@
             cluster.start(ImmutableList.of(loc));
             cluster.stop();
             cluster.start(ImmutableList.of(loc2));
-            fail();
+            Asserts.shouldHaveFailedPreviously();
         } catch (Exception e) {
-            if (Exceptions.getFirstThrowableOfType(e, IllegalStateException.class) == null) throw e;
+            Asserts.expectedFailureOfType(e, IllegalStateException.class);
         }
     }
 
@@ -147,9 +147,9 @@
                 .configure(DynamicCluster.MEMBER_SPEC, EntitySpec.create(TestEntity.class)));
         try {
             cluster.start(ImmutableList.of(loc, loc2));
-            fail();
+            Asserts.shouldHaveFailedPreviously();
         } catch (Exception e) {
-            if (Exceptions.getFirstThrowableOfType(e, IllegalArgumentException.class) == null) throw e;
+            Asserts.expectedFailureContainsIgnoreCase(e, "ambiguous");
         }
     }
 
diff --git a/brooklyn-server/pom.xml b/brooklyn-server/pom.xml
index b8d58ee..4e6012b 100644
--- a/brooklyn-server/pom.xml
+++ b/brooklyn-server/pom.xml
@@ -90,7 +90,7 @@
         <coverage.target>${working.dir}</coverage.target>
 
 		<!-- Dependency Versions -->
-        <jclouds.version>1.9.1</jclouds.version> <!-- JCLOUDS_VERSION -->
+        <jclouds.version>1.9.2</jclouds.version> <!-- JCLOUDS_VERSION -->
         <logback.version>1.0.7</logback.version>
         <slf4j.version>1.6.6</slf4j.version>  <!-- used for java.util.logging jul-to-slf4j interception -->
         <guava.version>17.0</guava.version>
diff --git a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessDriver.java b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessDriver.java
index 59dac4f..d243833 100644
--- a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessDriver.java
+++ b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessDriver.java
@@ -107,23 +107,22 @@
             skipStart = entityStarted.or(false);
         }
         if (!skipStart) {
-            DynamicTasks.queue("copy-pre-install-resources", new Runnable() { public void run() {
-                waitForConfigKey(BrooklynConfigKeys.PRE_INSTALL_RESOURCES_LATCH);
-                copyPreInstallResources();
-            }});
-
-            DynamicTasks.queue("pre-install", new Runnable() { public void run() {
-                preInstall();
-            }});
-
-            DynamicTasks.queue("pre-install-command", new Runnable() { public void run() {
-                runPreInstallCommand();
-            }});
-
             Optional<Boolean> locationInstalled = Optional.fromNullable(getLocation().getConfig(BrooklynConfigKeys.SKIP_ENTITY_INSTALLATION));
             Optional<Boolean> entityInstalled = Optional.fromNullable(entity.getConfig(BrooklynConfigKeys.SKIP_ENTITY_INSTALLATION));
             boolean skipInstall = locationInstalled.or(entityInstalled).or(false);
             if (!skipInstall) {
+                DynamicTasks.queue("copy-pre-install-resources", new Runnable() { public void run() {
+                    waitForConfigKey(BrooklynConfigKeys.PRE_INSTALL_RESOURCES_LATCH);
+                    copyPreInstallResources();
+                }});
+
+                DynamicTasks.queue("pre-install", new Runnable() { public void run() {
+                    preInstall();
+                }});
+
+                DynamicTasks.queue("pre-install-command", new Runnable() { public void run() {
+                    runPreInstallCommand();
+                }});
                 DynamicTasks.queue("setup", new Runnable() { public void run() {
                     waitForConfigKey(BrooklynConfigKeys.SETUP_LATCH);
                     setup();
diff --git a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SameServerEntityImpl.java b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SameServerEntityImpl.java
index c24201a..8da252f 100644
--- a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SameServerEntityImpl.java
+++ b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SameServerEntityImpl.java
@@ -21,6 +21,7 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import java.util.Collection;
+import java.util.List;
 
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.mgmt.Task;
@@ -28,6 +29,7 @@
 import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
 import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic.ComputeServiceIndicatorsFromChildrenAndMembers;
+import org.apache.brooklyn.core.location.Locations;
 import org.apache.brooklyn.entity.software.base.lifecycle.MachineLifecycleEffectorTasks;
 import org.apache.brooklyn.util.collections.QuorumCheck;
 import org.apache.brooklyn.util.core.config.ConfigBag;
@@ -68,7 +70,10 @@
      * Subclasses should override {@link #doStart} to customise behaviour.
      */
     @Override
-    public final void start(final Collection<? extends Location> locations) {
+    public final void start(Collection<? extends Location> locsO) {
+        addLocations(locsO);
+        final Collection<? extends Location> locations = Locations.getLocationsCheckingAncestors(locsO, this);
+        
         checkNotNull(locations, "locations");
         if (DynamicTasks.getTaskQueuingContext() != null) {
             doStart(locations);
diff --git a/brooklyn-server/test-framework/src/main/java/org/apache/brooklyn/test/framework/ParallelTestCaseImpl.java b/brooklyn-server/test-framework/src/main/java/org/apache/brooklyn/test/framework/ParallelTestCaseImpl.java
index 469bc3d..3ded474 100644
--- a/brooklyn-server/test-framework/src/main/java/org/apache/brooklyn/test/framework/ParallelTestCaseImpl.java
+++ b/brooklyn-server/test-framework/src/main/java/org/apache/brooklyn/test/framework/ParallelTestCaseImpl.java
@@ -49,7 +49,7 @@
         try {
             // Get an unsubmitted task for starting all the children of this entity in parallel,
             // at the same location as this entity.
-            final TaskAdaptable<?> taskAdaptable = StartableMethods.startingChildren(this);
+            final TaskAdaptable<?> taskAdaptable = StartableMethods.startingChildren(this, locations);
             logger.trace("{}, TaskAdaptable: {}", this, taskAdaptable);
 
             // Submit the task to the ExecutionManager so that they actually get started