Convert SaltEntity to a SoftwareProcess entity

Project: http://git-wip-us.apache.org/repos/asf/brooklyn-library/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-library/commit/ac837bca
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-library/tree/ac837bca
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-library/diff/ac837bca

Branch: refs/heads/master
Commit: ac837bcaba844ffe203edb957630896a48094d54
Parents: 38a9f78
Author: Thomas Bouron <[email protected]>
Authored: Tue Aug 29 17:07:30 2017 +0100
Committer: Thomas Bouron <[email protected]>
Committed: Tue Aug 29 17:07:30 2017 +0100

----------------------------------------------------------------------
 .../brooklyn/entity/cm/salt/SaltEntity.java     |   1 -
 .../entity/cm/salt/SaltEntityDriver.java        |   3 +-
 .../brooklyn/entity/cm/salt/SaltEntityImpl.java |  86 ++++++
 .../entity/cm/salt/SaltEntitySshDriver.java     | 269 ++++++++++++++++++
 .../brooklyn/entity/cm/salt/SaltHighstate.java  | 144 ++++++++++
 .../brooklyn/entity/cm/salt/SaltSshTasks.java   | 237 ++++++++++++++++
 .../brooklyn/entity/cm/salt/SaltUtils.java      |  42 +++
 .../entity/cm/salt/impl/SaltEntityImpl.java     |  81 ------
 .../cm/salt/impl/SaltEntitySshDriver.java       |  58 ----
 .../entity/cm/salt/impl/SaltHighstate.java      | 144 ----------
 .../salt/impl/SaltLifecycleEffectorTasks.java   | 275 -------------------
 .../entity/cm/salt/impl/SaltSshDriver.java      |  28 --
 .../entity/cm/salt/impl/SaltSshTasks.java       | 237 ----------------
 .../brooklyn/entity/cm/salt/impl/SaltUtils.java |  42 ---
 .../brooklyn/entity/cm/salt/HighstateTest.java  |   1 -
 15 files changed, 780 insertions(+), 868 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/ac837bca/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntity.java
----------------------------------------------------------------------
diff --git 
a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntity.java
 
b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntity.java
index 4428658..e308a20 100644
--- 
a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntity.java
+++ 
b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntity.java
@@ -23,7 +23,6 @@ import com.google.common.reflect.TypeToken;
 import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.ImplementedBy;
 import org.apache.brooklyn.api.sensor.AttributeSensor;
-import org.apache.brooklyn.entity.cm.salt.impl.SaltEntityImpl;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.annotation.Effector;
 import org.apache.brooklyn.core.annotation.EffectorParam;

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/ac837bca/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntityDriver.java
----------------------------------------------------------------------
diff --git 
a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntityDriver.java
 
b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntityDriver.java
index 19a88ed..a435991 100644
--- 
a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntityDriver.java
+++ 
b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntityDriver.java
@@ -19,7 +19,8 @@
 package org.apache.brooklyn.entity.cm.salt;
 
 import org.apache.brooklyn.entity.software.base.SoftwareProcessDriver;
+import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper;
 
 public interface SaltEntityDriver extends SoftwareProcessDriver {
-
+    ProcessTaskWrapper<Integer> saltCall(String spec);
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/ac837bca/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntityImpl.java
----------------------------------------------------------------------
diff --git 
a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntityImpl.java
 
b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntityImpl.java
new file mode 100644
index 0000000..e93ebe7
--- /dev/null
+++ 
b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntityImpl.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.brooklyn.entity.cm.salt;
+
+import java.util.Set;
+
+import org.apache.brooklyn.entity.software.base.SoftwareProcessImpl;
+import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.Beta;
+
+@Beta
+public class SaltEntityImpl extends SoftwareProcessImpl implements SaltEntity {
+    private static final Logger LOG = 
LoggerFactory.getLogger(SaltEntityImpl.class);
+
+    @Override
+    public Class getDriverInterface() {
+        return SaltEntityDriver.class;
+    }
+
+    @Override
+    public SaltEntityDriver getDriver() {
+        return (SaltEntityDriver) super.getDriver();
+    }
+
+    @Override
+    public void init() {
+        super.init();
+
+        final Set<? extends String> runList = 
getConfig(SaltConfig.START_STATES);
+        if (0 == runList.size()) {
+            throw new IllegalArgumentException("Must have configuration values 
for 'start_states' ("
+                + SaltConfig.START_STATES + ")");
+        }
+
+        LOG.debug("Run list size is {}", runList.size());
+        for (String state : runList) {
+            LOG.debug("Runlist state: {} ", state);
+        }
+
+        final Set<? extends String> formulas = 
getConfig(SaltConfig.SALT_FORMULAS);
+        LOG.debug("Formulas size: {}", formulas.size());
+        for (String formula : formulas) {
+            LOG.debug("Formula configured:  {}", formula);
+        }
+
+        SaltConfig.SaltMode mode = getConfig(SaltConfig.SALT_MODE);
+        LOG.debug("Initialize SaltStack {} mode", mode.name());
+    }
+
+    @Override
+    public void populateServiceNotUpDiagnostics() {
+        // TODO: noop for now;
+    }
+
+    @Override
+    public String saltCall(String spec) {
+        final ProcessTaskWrapper<Integer> command = getDriver().saltCall(spec);
+
+        command.asTask().blockUntilEnded();
+
+        if (0 == command.getExitCode()) {
+            return command.getStdout();
+        } else {
+            throw new RuntimeException("Command (" + spec + ")  failed with 
stderr:\n" + command.getStderr() + "\n");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/ac837bca/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntitySshDriver.java
----------------------------------------------------------------------
diff --git 
a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntitySshDriver.java
 
b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntitySshDriver.java
new file mode 100644
index 0000000..dac9756
--- /dev/null
+++ 
b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntitySshDriver.java
@@ -0,0 +1,269 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.brooklyn.entity.cm.salt;
+
+import static java.util.regex.Pattern.DOTALL;
+import static java.util.regex.Pattern.MULTILINE;
+import static 
org.apache.brooklyn.entity.software.base.SoftwareProcess.StopSoftwareParameters.StopMode.ALWAYS;
+import static 
org.apache.brooklyn.entity.software.base.SoftwareProcess.StopSoftwareParameters.StopMode.NEVER;
+
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.brooklyn.api.effector.Effector;
+import org.apache.brooklyn.api.mgmt.TaskAdaptable;
+import org.apache.brooklyn.camp.brooklyn.BrooklynCampConstants;
+import org.apache.brooklyn.core.effector.Effectors;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
+import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
+import org.apache.brooklyn.core.entity.trait.Startable;
+import 
org.apache.brooklyn.entity.software.base.AbstractSoftwareProcessSshDriver;
+import org.apache.brooklyn.entity.software.base.SoftwareProcess;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.apache.brooklyn.util.collections.MutableSet;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.core.task.DynamicTasks;
+import org.apache.brooklyn.util.core.task.TaskBuilder;
+import org.apache.brooklyn.util.core.task.system.ProcessTaskFactory;
+import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper;
+import org.apache.brooklyn.util.text.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+
+// TODO: does this belong to the _.impl package?
+public class SaltEntitySshDriver extends AbstractSoftwareProcessSshDriver 
implements SaltEntityDriver {
+    private static final Logger LOG = 
LoggerFactory.getLogger(SaltEntitySshDriver.class);
+    private static final Pattern FAILURES = 
Pattern.compile(".*^Failed:\\s+(\\d+)$.*", MULTILINE | DOTALL);
+    private static final String ZERO = "0";
+
+    public SaltEntitySshDriver(SaltEntityImpl entity, SshMachineLocation 
machine) {
+        super(entity, machine);
+    }
+
+    @Override
+    public void install() {
+        SaltConfig.SaltMode mode = 
getEntity().config().get(SaltConfig.SALT_MODE);
+        Preconditions.checkNotNull(mode, "Required config " + 
SaltConfig.SALT_MODE + " not provided for entity: " + entity);
+        LOG.info("Starting salt in '{}' mode at '{}'", mode, 
getMachine().getDisplayName());
+
+        if (mode != SaltConfig.SaltMode.MASTERLESS) {
+            // TODO: implement MASTER and MINION
+            throw new IllegalStateException("Unknown salt mode: " + 
mode.name());
+        }
+
+        final Set<? extends String> startStates = 
getEntity().config().get(SaltConfig.START_STATES);
+        final Set<? extends String> formulas = 
getEntity().config().get(SaltConfig.SALT_FORMULAS);
+        final Set<? extends String> pillars = 
getEntity().config().get(SaltConfig.SALT_PILLARS);
+        final Set<? extends String> pillarUrls = 
getEntity().config().get(SaltConfig.SALT_PILLAR_URLS);
+        final String entityId = 
getEntity().config().get(BrooklynCampConstants.PLAN_ID);
+
+        final ProcessTaskWrapper<Integer> installedAlready = 
queueAndBlock(SaltSshTasks.isSaltInstalled(false));
+
+        if (0 != installedAlready.getExitCode()) {
+            DynamicTasks.queue("install", new Runnable() {
+                @Override
+                public void run() {
+                    DynamicTasks.queue(
+                            SaltSshTasks.installSalt(false),
+                            SaltSshTasks.installSaltUtilities(false),
+                            
SaltSshTasks.configureForMasterlessOperation(false),
+                            SaltSshTasks.installTopFile(startStates, false));
+
+                    if (Strings.isNonBlank(entityId)) {
+                        DynamicTasks.queue(SaltSshTasks.setMinionId(entityId));
+                    }
+                    installFormulas(formulas);
+                    installPillars(pillars, pillarUrls);
+                }
+            });
+        }
+    }
+
+    @Override
+    public void customize() {
+        newScript(CUSTOMIZING).execute();
+    }
+
+    @Override
+    public void launch() {
+        String name = "apply top states";
+
+        final ProcessTaskWrapper<Integer> topStates = 
queueAndBlock(SaltSshTasks.applyTopStates(false).summary(name));
+
+        // Salt apply returns exit code 0 even upon failure so check the 
stdout.
+        final Matcher failCount = FAILURES.matcher(topStates.getStdout());
+        if (!failCount.matches() || !ZERO.equals(failCount.group(1))) {
+            LOG.warn("Encountered error in applying Salt top states: {}", 
topStates.getStdout());
+            throw new RuntimeException(
+                    "Encountered error in applying Salt top states, see '" + 
name + "' in activities for details");
+        }
+    }
+
+    @Override
+    public void runPostLaunchCommand() {
+        super.runPostLaunchCommand();
+
+        final ProcessTaskWrapper<String> retrieveHighstate = 
SaltSshTasks.retrieveHighstate();
+        final ProcessTaskWrapper<String> highstate = 
DynamicTasks.queue(retrieveHighstate).block();
+        String stateDescription = highstate.get();
+
+        SaltHighstate.applyHighstate(stateDescription, getEntity());
+
+        getEntity().sensors().set(SoftwareProcess.SERVICE_UP, true);
+    }
+
+    @Override
+    public boolean isRunning() {
+        return true;
+    }
+
+    @Override
+    public void stop() {
+        final Set<? extends String> stopStates = 
getEntity().config().get(SaltConfig.STOP_STATES);
+        LOG.debug("Executing Salt stopProcessesAtMachine with states {}", 
stopStates);
+        if (stopStates.isEmpty()) {
+            stopBasedOnStartStates();
+        } else {
+            applyStates(stopStates);
+        }
+    }
+
+    @Override
+    public void restart() {
+        ServiceStateLogic.setExpectedState(getEntity(), Lifecycle.STOPPING);
+
+        try {
+            final Set<? extends String> restartStates = 
getEntity().config().get(SaltConfig.RESTART_STATES);
+            LOG.debug("Executing Salt restart with states {}", restartStates);
+            if (restartStates.isEmpty()) {
+                restartBasedOnStartStates();
+            } else {
+                applyStates(restartStates);
+            }
+            ServiceStateLogic.setExpectedState(getEntity(), Lifecycle.RUNNING);
+        } catch (Exception e) {
+            
getEntity().sensors().set(ServiceStateLogic.SERVICE_NOT_UP_DIAGNOSTICS,
+                    ImmutableMap.<String, Object>of("restart", 
e.getMessage()));
+            ServiceStateLogic.setExpectedState(getEntity(), Lifecycle.ON_FIRE);
+        }
+    }
+
+    @Override
+    public ProcessTaskWrapper<Integer> saltCall(String spec) {
+        return DynamicTasks.queue(SaltSshTasks.saltCall(spec));
+    }
+
+    private ProcessTaskWrapper<Integer> 
queueAndBlock(ProcessTaskFactory<Integer> taskFactory) {
+        final ProcessTaskWrapper<Integer> queued = 
DynamicTasks.queue(taskFactory);
+        queued.asTask().blockUntilEnded();
+        return queued;
+    }
+
+    private void installFormulas(Set<? extends String> formulas) {
+        if (formulas.size() > 0) {
+            DynamicTasks.queue(SaltSshTasks.enableFileRoots(false));
+
+            final TaskBuilder<Object> formulaTasks = 
TaskBuilder.builder().displayName("install formulas");
+            for (String url : formulas) {
+                formulaTasks.add(SaltSshTasks.installSaltFormula(url, 
false).newTask());
+            }
+            DynamicTasks.queue(formulaTasks.build());
+        }
+    }
+
+    private void installPillars(Set<? extends String> pillars, Set<? extends 
String> pillarUrls) {
+        if (pillarUrls.size() > 0) {
+            final TaskBuilder<Object> pillarTasks = 
TaskBuilder.builder().displayName("install pillars");
+            
pillarTasks.add(SaltSshTasks.invokeSaltUtility("init_pillar_config", null, 
false)
+                    .summary("init pillar config").newTask());
+            for (String pillar : pillars) {
+                pillarTasks.add(SaltSshTasks.addPillarToTop(pillar, 
false).newTask());
+            }
+            for (String url : pillarUrls) {
+                pillarTasks.add(SaltSshTasks.installSaltPillar(url, 
false).newTask());
+            }
+            DynamicTasks.queue(pillarTasks.build());
+        }
+    }
+
+    private MutableSet<String> addSuffix(Set<? extends String> names, String 
suffix) {
+        final MutableSet<String> suffixed = MutableSet.of();
+        for (String name : names) {
+            suffixed.add(name + suffix);
+        }
+        return suffixed;
+    }
+
+    private void applyStates(Set<? extends String> states) {
+        for (String state : states) {
+            DynamicTasks.queue(SaltSshTasks.applyState(state, 
false).summary("apply state " + state));
+        }
+    }
+
+    private void stopBasedOnStartStates() {
+        final Set<? extends String> startStates = 
getEntity().config().get(SaltConfig.START_STATES);
+        final MutableSet<String> stopStates = addSuffix(startStates, ".stop");
+        final ProcessTaskWrapper<Integer> checkStops =
+                queueAndBlock(SaltSshTasks.verifyStates(stopStates, 
false).summary("check stop states"));
+        if (0 != checkStops.getExitCode()) {
+            throw new RuntimeException("No stop_states configured and not all 
start_states have matching stop states");
+        } else {
+            applyStates(stopStates);
+        }
+    }
+
+    private void restartBasedOnStartStates() {
+        final Set<? extends String> startStates = 
getEntity().config().get(SaltConfig.START_STATES);
+        final MutableSet<String> restartStates = addSuffix(startStates, 
".restart");
+        final ProcessTaskWrapper<Integer> queued =
+                queueAndBlock(SaltSshTasks.findStates(restartStates, 
false).summary("check restart states"));
+        final String stdout = queued.getStdout();
+        String[] foundStates = Strings.isNonBlank(stdout) ? 
stdout.split("\\n") : null;
+
+        if (restartStates.size() > 0 && foundStates != null && 
(restartStates.size() == foundStates.length)) {
+            // each state X listed in start_states has a matching state of the 
form X.restart;  we apply them.
+            LOG.debug("All start_states have matching restart states, applying 
these");
+            applyStates(restartStates);
+
+        } else if (foundStates != null && foundStates.length > 0) {
+            // only *some* of the states have a matching restart; we treat 
this as a fail
+            LOG.debug("Only some start_states have matching restart states, 
treating as restart failure") ;
+            throw new RuntimeException("unable to find restart state for all 
applied states");
+
+        } else {
+            // else we apply "stop" effector (with parameters to stop 
processes not machine) then "start"
+            // (and in that effector we'd fail if stop was not well-defined)
+            LOG.debug("No stop states available, invoking stop and start 
effectors");
+            invokeEffector(Startable.STOP, ConfigBag.newInstance()
+                    
.configure(SoftwareProcess.StopSoftwareParameters.STOP_PROCESS_MODE, ALWAYS)
+                    
.configure(SoftwareProcess.StopSoftwareParameters.STOP_MACHINE_MODE, NEVER));
+            invokeEffector(Startable.START, ConfigBag.EMPTY);
+        }
+    }
+
+    private void invokeEffector(Effector<Void> effector, ConfigBag config) {
+        final TaskAdaptable<Void> stop = Entities.submit(getEntity(), 
Effectors.invocation(getEntity(), effector, config));
+        stop.asTask().blockUntilEnded();
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/ac837bca/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltHighstate.java
----------------------------------------------------------------------
diff --git 
a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltHighstate.java
 
b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltHighstate.java
new file mode 100644
index 0000000..16a4224
--- /dev/null
+++ 
b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltHighstate.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.brooklyn.entity.cm.salt;
+
+import com.google.common.reflect.TypeToken;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.core.sensor.Sensors;
+import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.text.Strings;
+import org.apache.brooklyn.util.yaml.Yamls;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Utility for setting a Salt highstate description on entity sensors.
+ */
+public class SaltHighstate {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(SaltHighstate.class);
+
+    public static final String HIGHSTATE_SENSOR_PREFIX = "salt.state";
+
+    @SuppressWarnings("serial")
+    public static TypeToken<Map<String, Object>> STATE_FUNCTION_TYPE =
+        new TypeToken<Map<String, Object>>() {};
+
+    private SaltHighstate() {}
+
+    public static void applyHighstate(String contents, Entity entity) {
+
+        final String adaptedYaml = adaptForSaltYamlTypes(contents);
+        LOG.debug("Parsing Salt highstate yaml:\n{}", adaptedYaml);
+        final Iterable<Object> objects = Yamls.parseAll(adaptedYaml);
+
+        for (Object entry: objects) {
+            @SuppressWarnings("unchecked")
+            final Map<String, Object> scopeMap = Yamls.getAs(entry, Map.class);
+            applyStatesInScope(entity, scopeMap);
+        }
+    }
+
+    private static void applyStatesInScope(Entity entity, Map<String, Object> 
scopeMap) {
+        for (String scope: scopeMap.keySet()) {
+            @SuppressWarnings("unchecked")
+            final Map<String, Object> stateMap = 
Yamls.getAs(scopeMap.get(scope), Map.class);
+            for (String id: stateMap.keySet()) {
+                applyStateSensor(id, stateMap.get(id), entity);
+            }
+        }
+    }
+
+
+    private static String adaptForSaltYamlTypes(String description) {
+        return description.replaceAll("!!python/unicode\\s+", "");
+    }
+
+    @SuppressWarnings("unchecked")
+    private static void applyStateSensor(String id, Object stateData, Entity 
entity) {
+        if (isSaltInternal(id)) {
+            return;
+        }
+        addStateSensor(id, entity);
+        try {
+            Map<String, List<Object>> stateInfo = (Map<String, 
List<Object>>)stateData;
+            for (String stateModule : stateInfo.keySet()) {
+                addStateModuleValue(id, entity, stateInfo, stateModule);
+            }
+        } catch (ClassCastException e) {
+            LOG.info("Unexpected structure for {} state, skipping ({})", id, 
e.getMessage());
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private static void addStateModuleValue(String id, Entity entity, 
Map<String, List<Object>> stateInfo,
+        String stateModule) {
+
+        if (isSaltInternal(stateModule)) {
+            return;
+        }
+        try {
+            final List<Object> stateEntries = stateInfo.get(stateModule);
+            String stateFunction = "";
+            Map<String, Object> moduleSettings = MutableMap.of();
+            for (Object entry : stateEntries) {
+                if (entry instanceof Map) {
+                    moduleSettings.putAll((Map<String, Object>)entry);
+                } else {
+                    stateFunction = entry.toString();
+                }
+            }
+
+            final String name = sensorName(id, stateModule, stateFunction);
+            final AttributeSensor<Map<String, Object>> newSensor =
+                Sensors.newSensor(STATE_FUNCTION_TYPE, HIGHSTATE_SENSOR_PREFIX 
+ "." + name, name);
+            entity.sensors().set(newSensor, moduleSettings);
+
+            LOG.debug("Sensor set for: {}", moduleSettings);
+        } catch (ClassCastException e) {
+            LOG.info("Unexpected structure for state module {}, skipping 
({})", id + "." + stateModule, e.getMessage());
+        }
+    }
+
+    private static String sensorName(String... parts) {
+        return Strings.join(parts, ".");
+    }
+
+
+    private static void addStateSensor(String state, Entity entity) {
+        List<String> states = entity.sensors().get(SaltEntityImpl.STATES);
+        if (null == states || !states.contains(state)) {
+            if (null == states) {
+                states = MutableList.of();
+            }
+            states.add(state);
+            entity.sensors().set(SaltEntityImpl.STATES, states);
+        }
+    }
+
+    private static boolean isSaltInternal(String module) {
+        return module.startsWith("__");
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/ac837bca/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltSshTasks.java
----------------------------------------------------------------------
diff --git 
a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltSshTasks.java
 
b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltSshTasks.java
new file mode 100644
index 0000000..03daf8e
--- /dev/null
+++ 
b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltSshTasks.java
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.brooklyn.entity.cm.salt;
+
+import org.apache.brooklyn.api.mgmt.TaskAdaptable;
+import org.apache.brooklyn.api.mgmt.TaskFactory;
+import org.apache.brooklyn.core.effector.EffectorTasks;
+import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks;
+import 
org.apache.brooklyn.core.effector.ssh.SshEffectorTasks.SshEffectorTaskFactory;
+import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.core.ResourceUtils;
+import org.apache.brooklyn.util.core.file.ArchiveTasks;
+import org.apache.brooklyn.util.core.task.TaskBuilder;
+import org.apache.brooklyn.util.core.task.Tasks;
+import org.apache.brooklyn.util.core.task.ssh.SshPutTaskFactory;
+import org.apache.brooklyn.util.core.task.system.ProcessTaskFactory;
+import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper;
+import org.apache.brooklyn.util.ssh.BashCommands;
+import org.apache.brooklyn.util.text.Identifiers;
+import org.apache.brooklyn.util.text.Strings;
+
+import java.util.List;
+import java.util.Set;
+
+import static org.apache.brooklyn.core.effector.ssh.SshEffectorTasks.ssh;
+import static org.apache.brooklyn.util.ssh.BashCommands.sudo;
+
+
+public class SaltSshTasks {
+
+    private static final String UTILITY_SCRIPT = "salt_utilities.sh";
+
+    private SaltSshTasks() {
+        // Utility class
+    }
+
+    public static TaskFactory<?> installSalt(boolean force) {
+        // TODO: ignore force?
+        return sshCommands(
+            
BashCommands.commandToDownloadUrlAs("https://bootstrap.saltstack.com";, 
"install_salt.sh"),
+            sudo("sh install_salt.sh")
+        )
+        .summary("install salt");
+    }
+
+    public static SshEffectorTaskFactory<Integer> isSaltInstalled(boolean 
force) {
+        return invokeSaltUtility("salt_installed", null, true).summary("check 
installed");
+    }
+
+    public static TaskFactory<?> configureForMasterlessOperation(boolean 
force) {
+        // TODO: ignore force?
+        return ssh(sudo("sed -i '/^#file_client/c file_client: local' 
/etc/salt/minion"))
+            .summary("configure masterless");
+    }
+
+
+    public static TaskFactory<?> enableFileRoots(boolean force) {
+        return sshCommands(
+                "grep ^file_roots /etc/salt/minion || {",
+                "cat /etc/salt/minion > /tmp/minion.update",
+                "cat >> /tmp/minion.update  << BROOKLYN_EOF",
+                "file_roots:",
+                "  base:",
+                "    - /srv/salt/",
+                "BROOKLYN_EOF",
+                sudo("mv /tmp/minion.update /etc/salt/minion"),
+                "}"
+            )
+            .requiringExitCodeZero()
+            .summary("enable file_roots");
+    }
+
+
+    public static TaskFactory<?>  addPillarToTop(String pillar, boolean b) {
+        return invokeSaltUtility("add_pillar_to_top", pillar, false)
+            .summary("Add pillar " + pillar + " to top");
+    }
+
+    public static TaskFactory<?>  installSaltPillar(final String pillarUrl, 
boolean force) {
+        return new TaskFactory<TaskAdaptable<?>>() {
+            @Override
+            public TaskAdaptable<?> newTask() {
+                TaskBuilder<Void> tb = 
Tasks.<Void>builder().displayName(pillarUrl);
+                String tempDownloadDir = "/tmp/download-" + 
Identifiers.makeRandomId(12);
+
+                
tb.add(ArchiveTasks.deploy(null,null,pillarUrl,EffectorTasks.findSshMachine(),
+                    tempDownloadDir,false,null,null).newTask());
+
+                tb.add(ssh("cd " + tempDownloadDir + " ; " + sudo("mv * 
/srv/pillar")).newTask());
+
+                return tb.build();
+            }
+        };
+    }
+
+    public static TaskFactory<?> installSaltFormula(final String formulaUrl, 
boolean force) {
+        return new TaskFactory<TaskAdaptable<?>>() {
+            @Override
+            public TaskAdaptable<?> newTask() {
+                TaskBuilder<Void> tb = 
Tasks.<Void>builder().displayName(formulaUrl);
+
+                String tempDirectoryForUnpack = "/tmp/download-" + 
Identifiers.makeRandomId(12);
+
+                tb.add(ArchiveTasks.deploy(null, null, formulaUrl, 
EffectorTasks.findSshMachine(),
+                    tempDirectoryForUnpack, false, null, null).newTask());
+
+                // TODO move this into salt_utilities.sh
+                String installCmd = BashCommands.chain(
+                    "cd "+tempDirectoryForUnpack,
+                    "EXPANDED_DIR=`ls`",
+                    BashCommands.requireTest("`ls | wc -w` -eq 1",
+                        "The deployed archive "+ formulaUrl +" must contain 
exactly one directory"),
+                    "sudo mkdir -p /srv/formula",
+                    "sudo mv $EXPANDED_DIR /srv/formula/",
+                    // sed command below relies on enableFileRoots behaviour 
of append file_roots to end of config file
+                    "sudo sed -i \"$ a\\    - /srv/formula/$EXPANDED_DIR\" 
/etc/salt/minion",
+                    "cd ..",
+                    "rm -rf '"+tempDirectoryForUnpack+"'");
+                tb.add(ssh(installCmd).summary("installing " + formulaUrl + " 
states to /srv/formula")
+                    .requiringExitCodeZero().newTask());
+
+                return tb.build();
+            }
+        };
+    }
+
+    public static TaskFactory<?> installTopFile(final Set<? extends String> 
runList, boolean force) {
+        // TODO: ignore force?
+        // TODO: move this into salt_utilities.sh
+        final MutableList.Builder<String> topBuilder = 
MutableList.<String>builder()
+            .add("cat > /tmp/top.sls << BROOKLYN_EOF")
+            .add("base:")
+            .add("  '*':");
+        for (String stateName: runList) {
+            topBuilder.add("    - " + stateName);
+        }
+        topBuilder.add("BROOKLYN_EOF");
+        List<String> createTempTopFile = topBuilder.build();
+
+        List<String> commands = MutableList.<String>builder()
+            .add(sudo("mkdir -p /srv/salt"))
+            .add(Strings.join(createTempTopFile, "\n"))
+            .add(sudo("mv /tmp/top.sls /srv/salt"))
+            .build();
+        return ssh(commands).summary("create top.sls file");
+    }
+
+    public static ProcessTaskFactory<Integer> applyTopStates(boolean force) {
+        return  saltCall("state.apply");
+    }
+
+    public static SshEffectorTaskFactory<Integer> applyState(String state, 
boolean force) {
+        return saltCall("state.apply " + state);
+    }
+
+    public static SshEffectorTaskFactory<Integer> saltCall(String command) {
+        return ssh(sudo("salt-call --local " + 
command)).allowingNonZeroExitCode();
+    }
+
+    public static ProcessTaskWrapper<String> retrieveHighstate() {
+        return saltCall("state.show_highstate --out=yaml")
+            .summary("retrieve highstate")
+            .requiringZeroAndReturningStdout()
+            .newTask();
+    }
+
+    public static TaskFactory<?> installSaltUtilities(boolean force) {
+        return new TaskFactory<TaskAdaptable<?>>() {
+            @Override
+            public TaskAdaptable<?> newTask() {
+                final TaskBuilder<Void> builder = Tasks.<Void>builder()
+                    .displayName("install salt utilities")
+                    .add(installScript(UTILITY_SCRIPT, "install salt shell 
utils").newTask())
+                    .add(ssh(sudo("mv /tmp/" + UTILITY_SCRIPT + " 
/etc/salt")).newTask());
+                return builder.build();
+            }
+        };
+    }
+
+    private static SshPutTaskFactory installScript(String name, String 
description) {
+        return SshEffectorTasks.put("/tmp/" + name)
+                
.contents(ResourceUtils.create().getResourceFromUrl("classpath:" + name))
+                .summary(description);
+    }
+
+    public static SshEffectorTaskFactory<Integer> verifyStates(Set<String> 
states, boolean force) {
+        return invokeSaltUtility("verify_states", Strings.join(states, " "), 
true);
+    }
+
+    public static SshEffectorTaskFactory<Integer> findStates(Set<String> 
states, boolean force) {
+        return invokeSaltUtility("find_states", Strings.join(states, " "), 
true);
+    }
+
+    // Simple invocation of a function from salt_utilities.sh, optionally 
allowing it to fail.
+    // Uses single quoted bash command, so args mustn't contain single quotes.
+    public static SshEffectorTaskFactory<Integer> invokeSaltUtility(String 
functionName, String args, boolean permitFailure) {
+
+        final SshEffectorTaskFactory<Integer> taskFactory =
+            ssh(sudo("/bin/bash -c '. /etc/salt/salt_utilities.sh ; " + 
functionName + " " + args + "'"))
+            .summary(functionName);
+
+        if (permitFailure) {
+            taskFactory.allowingNonZeroExitCode();
+        } else {
+            taskFactory.requiringExitCodeZero();
+        }
+        return taskFactory;
+
+    }
+
+    public static SshEffectorTaskFactory<Integer> sshCommands(String line, 
String... lines) {
+        final MutableList.Builder<String> builder = 
MutableList.<String>builder()
+            .add(line);
+        builder.addAll(lines);
+        return ssh(Strings.join(builder.build(), "\n"));
+    }
+
+    public static SshEffectorTaskFactory<Integer> setMinionId(final String 
entityId) {
+        return invokeSaltUtility("set_minion_id", entityId, true);
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/ac837bca/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltUtils.java
----------------------------------------------------------------------
diff --git 
a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltUtils.java
 
b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltUtils.java
new file mode 100644
index 0000000..fc18a86
--- /dev/null
+++ 
b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltUtils.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.brooklyn.entity.cm.salt;
+
+import org.apache.brooklyn.api.location.LocationDefinition;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+
+public class SaltUtils {
+
+    private SaltUtils() {
+        // Utility class
+    }
+
+    public static ManagementContext.PropertiesReloadListener 
propertiesReloadListener(
+            ManagementContext mc, LocationDefinition definition) {
+
+        return new ManagementContext.PropertiesReloadListener() {
+            private static final long serialVersionUID = 1L;
+            @Override
+            public void reloaded() {
+                // TODO: implement properties reload logic
+            }
+        };
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/ac837bca/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltEntityImpl.java
----------------------------------------------------------------------
diff --git 
a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltEntityImpl.java
 
b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltEntityImpl.java
deleted file mode 100644
index bea2581..0000000
--- 
a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltEntityImpl.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.brooklyn.entity.cm.salt.impl;
-
-import com.google.common.annotations.Beta;
-import org.apache.brooklyn.entity.cm.salt.SaltConfig;
-import org.apache.brooklyn.entity.cm.salt.SaltEntity;
-import org.apache.brooklyn.entity.stock.EffectorStartableImpl;
-import org.apache.brooklyn.util.core.task.DynamicTasks;
-import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Set;
-
-@Beta
-public class SaltEntityImpl extends EffectorStartableImpl implements 
SaltEntity {
-    private static final Logger LOG = 
LoggerFactory.getLogger(SaltEntityImpl.class);
-
-    public SaltEntityImpl() {
-        super();
-    }
-
-    @Override
-    public void init() {
-        super.init();
-
-        final Set<? extends String> runList = 
getConfig(SaltConfig.START_STATES);
-        if (0 == runList.size()) {
-            throw new IllegalArgumentException("Must have configuration values 
for 'start_states' ("
-                + SaltConfig.START_STATES + ")");
-        }
-
-        LOG.debug("Run list size is {}", runList.size());
-        for (String state : runList) {
-            LOG.debug("Runlist state: {} ", state);
-        }
-
-        final Set<? extends String> formulas = 
getConfig(SaltConfig.SALT_FORMULAS);
-        LOG.debug("Formulas size: {}", formulas.size());
-        for (String formula : formulas) {
-            LOG.debug("Formula configured:  {}", formula);
-        }
-
-        SaltConfig.SaltMode mode = getConfig(SaltConfig.SALT_MODE);
-        LOG.debug("Initialize SaltStack {} mode", mode.name());
-        new SaltLifecycleEffectorTasks().attachLifecycleEffectors(this);
-    }
-
-    @Override
-    public void populateServiceNotUpDiagnostics() {
-        // TODO: noop for now;
-    }
-
-    @Override
-    public String saltCall(String spec) {
-        final ProcessTaskWrapper<Integer> command = 
DynamicTasks.queue(SaltSshTasks.saltCall(spec));
-        command.asTask().blockUntilEnded();
-        if (0 == command.getExitCode()) {
-            return command.getStdout();
-        } else {
-            throw new RuntimeException("Command (" + spec + ")  failed with 
stderr:\n" + command.getStderr() + "\n");
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/ac837bca/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltEntitySshDriver.java
----------------------------------------------------------------------
diff --git 
a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltEntitySshDriver.java
 
b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltEntitySshDriver.java
deleted file mode 100644
index 7531c27..0000000
--- 
a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltEntitySshDriver.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.brooklyn.entity.cm.salt.impl;
-
-import org.apache.brooklyn.entity.cm.salt.SaltEntityDriver;
-import 
org.apache.brooklyn.entity.software.base.AbstractSoftwareProcessSshDriver;
-import org.apache.brooklyn.location.ssh.SshMachineLocation;
-
-// TODO: does this belong to the _.impl package?
-public class SaltEntitySshDriver extends AbstractSoftwareProcessSshDriver 
implements SaltEntityDriver {
-
-    public SaltEntitySshDriver(SaltEntityImpl entity, SshMachineLocation 
machine) {
-        super(entity, machine);
-    }
-
-    @Override
-    public SaltEntityImpl getEntity() {
-        return (SaltEntityImpl) super.getEntity();
-    }
-
-    @Override
-    public void install() {
-    }
-
-    @Override
-    public void customize() {
-    }
-
-    @Override
-    public void launch() {
-    }
-
-    @Override
-    public boolean isRunning() {
-        return true;
-    }
-
-    @Override
-    public void stop() {
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/ac837bca/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltHighstate.java
----------------------------------------------------------------------
diff --git 
a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltHighstate.java
 
b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltHighstate.java
deleted file mode 100644
index fdae93b..0000000
--- 
a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltHighstate.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.brooklyn.entity.cm.salt.impl;
-
-import com.google.common.reflect.TypeToken;
-
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.sensor.AttributeSensor;
-import org.apache.brooklyn.core.sensor.Sensors;
-import org.apache.brooklyn.util.collections.MutableList;
-import org.apache.brooklyn.util.collections.MutableMap;
-import org.apache.brooklyn.util.text.Strings;
-import org.apache.brooklyn.util.yaml.Yamls;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * Utility for setting a Salt highstate description on entity sensors.
- */
-public class SaltHighstate {
-
-    private static final Logger LOG = 
LoggerFactory.getLogger(SaltHighstate.class);
-
-    public static final String HIGHSTATE_SENSOR_PREFIX = "salt.state";
-
-    @SuppressWarnings("serial")
-    public static TypeToken<Map<String, Object>> STATE_FUNCTION_TYPE =
-        new TypeToken<Map<String, Object>>() {};
-
-    private SaltHighstate() {}
-
-    public static void applyHighstate(String contents, Entity entity) {
-
-        final String adaptedYaml = adaptForSaltYamlTypes(contents);
-        LOG.debug("Parsing Salt highstate yaml:\n{}", adaptedYaml);
-        final Iterable<Object> objects = Yamls.parseAll(adaptedYaml);
-
-        for (Object entry: objects) {
-            @SuppressWarnings("unchecked")
-            final Map<String, Object> scopeMap = Yamls.getAs(entry, Map.class);
-            applyStatesInScope(entity, scopeMap);
-        }
-    }
-
-    private static void applyStatesInScope(Entity entity, Map<String, Object> 
scopeMap) {
-        for (String scope: scopeMap.keySet()) {
-            @SuppressWarnings("unchecked")
-            final Map<String, Object> stateMap = 
Yamls.getAs(scopeMap.get(scope), Map.class);
-            for (String id: stateMap.keySet()) {
-                applyStateSensor(id, stateMap.get(id), entity);
-            }
-        }
-    }
-
-
-    private static String adaptForSaltYamlTypes(String description) {
-        return description.replaceAll("!!python/unicode\\s+", "");
-    }
-
-    @SuppressWarnings("unchecked")
-    private static void applyStateSensor(String id, Object stateData, Entity 
entity) {
-        if (isSaltInternal(id)) {
-            return;
-        }
-        addStateSensor(id, entity);
-        try {
-            Map<String, List<Object>> stateInfo = (Map<String, 
List<Object>>)stateData;
-            for (String stateModule : stateInfo.keySet()) {
-                addStateModuleValue(id, entity, stateInfo, stateModule);
-            }
-        } catch (ClassCastException e) {
-            LOG.info("Unexpected structure for {} state, skipping ({})", id, 
e.getMessage());
-        }
-    }
-
-    @SuppressWarnings("unchecked")
-    private static void addStateModuleValue(String id, Entity entity, 
Map<String, List<Object>> stateInfo,
-        String stateModule) {
-
-        if (isSaltInternal(stateModule)) {
-            return;
-        }
-        try {
-            final List<Object> stateEntries = stateInfo.get(stateModule);
-            String stateFunction = "";
-            Map<String, Object> moduleSettings = MutableMap.of();
-            for (Object entry : stateEntries) {
-                if (entry instanceof Map) {
-                    moduleSettings.putAll((Map<String, Object>)entry);
-                } else {
-                    stateFunction = entry.toString();
-                }
-            }
-
-            final String name = sensorName(id, stateModule, stateFunction);
-            final AttributeSensor<Map<String, Object>> newSensor =
-                Sensors.newSensor(STATE_FUNCTION_TYPE, HIGHSTATE_SENSOR_PREFIX 
+ "." + name, name);
-            entity.sensors().set(newSensor, moduleSettings);
-
-            LOG.debug("Sensor set for: {}", moduleSettings);
-        } catch (ClassCastException e) {
-            LOG.info("Unexpected structure for state module {}, skipping 
({})", id + "." + stateModule, e.getMessage());
-        }
-    }
-
-    private static String sensorName(String... parts) {
-        return Strings.join(parts, ".");
-    }
-
-
-    private static void addStateSensor(String state, Entity entity) {
-        List<String> states = entity.sensors().get(SaltEntityImpl.STATES);
-        if (null == states || !states.contains(state)) {
-            if (null == states) {
-                states = MutableList.of();
-            }
-            states.add(state);
-            entity.sensors().set(SaltEntityImpl.STATES, states);
-        }
-    }
-
-    private static boolean isSaltInternal(String module) {
-        return module.startsWith("__");
-    }
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/ac837bca/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltLifecycleEffectorTasks.java
----------------------------------------------------------------------
diff --git 
a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltLifecycleEffectorTasks.java
 
b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltLifecycleEffectorTasks.java
deleted file mode 100644
index 2051ed6..0000000
--- 
a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltLifecycleEffectorTasks.java
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.brooklyn.entity.cm.salt.impl;
-
-import static java.util.regex.Pattern.DOTALL;
-import static java.util.regex.Pattern.MULTILINE;
-import static 
org.apache.brooklyn.entity.software.base.SoftwareProcess.StopSoftwareParameters.StopMode.ALWAYS;
-import static 
org.apache.brooklyn.entity.software.base.SoftwareProcess.StopSoftwareParameters.StopMode.NEVER;
-
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.brooklyn.api.effector.Effector;
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.location.MachineLocation;
-import org.apache.brooklyn.api.mgmt.TaskAdaptable;
-import org.apache.brooklyn.camp.brooklyn.BrooklynCampConstants;
-import org.apache.brooklyn.core.effector.Effectors;
-import org.apache.brooklyn.core.entity.Entities;
-import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
-import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
-import org.apache.brooklyn.core.entity.trait.Startable;
-import org.apache.brooklyn.entity.cm.salt.SaltConfig;
-import org.apache.brooklyn.entity.software.base.SoftwareProcess;
-import 
org.apache.brooklyn.entity.software.base.SoftwareProcess.StopSoftwareParameters;
-import 
org.apache.brooklyn.entity.software.base.lifecycle.MachineLifecycleEffectorTasks;
-import org.apache.brooklyn.util.collections.MutableSet;
-import org.apache.brooklyn.util.core.config.ConfigBag;
-import org.apache.brooklyn.util.core.task.DynamicTasks;
-import org.apache.brooklyn.util.core.task.TaskBuilder;
-import org.apache.brooklyn.util.core.task.system.ProcessTaskFactory;
-import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper;
-import org.apache.brooklyn.util.text.Strings;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.annotations.Beta;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Supplier;
-import com.google.common.collect.ImmutableMap;
-
-@Beta
-public class SaltLifecycleEffectorTasks extends MachineLifecycleEffectorTasks 
implements SaltConfig {
-    private static final Logger LOG = 
LoggerFactory.getLogger(SaltLifecycleEffectorTasks.class);
-
-    @Override
-    protected String startProcessesAtMachine(Supplier<MachineLocation> 
machineS) {
-        SaltMode mode = detectSaltMode(entity());
-        final MachineLocation machine = machineS.get();
-        LOG.info("Starting salt in '{}' mode at '{}'", mode, 
machine.getDisplayName());
-        if (mode == SaltMode.MASTERLESS) {
-            startWithSshAsync();
-        } else {
-            // TODO: implement MASTER and MINION
-            throw new IllegalStateException("Unknown salt mode: " + 
mode.name());
-        }
-        return "salt tasks submitted (" + mode + ")";
-    }
-
-
-    protected static SaltMode detectSaltMode(Entity entity) {
-        SaltMode mode = entity.getConfig(SaltConfig.SALT_MODE);
-        Preconditions.checkNotNull(mode, "Required config " + 
SaltConfig.SALT_MODE + " not provided for entity: " + entity);
-        return mode;
-    }
-
-    protected void startWithSshAsync() {
-
-        final Set<? extends String> startStates = 
entity().getConfig(SaltConfig.START_STATES);
-        final Set<? extends String> formulas = 
entity().getConfig(SaltConfig.SALT_FORMULAS);
-        final Set<? extends String> pillars = 
entity().getConfig(SaltConfig.SALT_PILLARS);
-        final Set<? extends String> pillarUrls = 
entity().getConfig(SaltConfig.SALT_PILLAR_URLS);
-        final String entityId = 
entity().getConfig(BrooklynCampConstants.PLAN_ID);
-
-        final ProcessTaskWrapper<Integer> installedAlready = 
queueAndBlock(SaltSshTasks.isSaltInstalled(false));
-
-        if (0 != installedAlready.getExitCode()) {
-            DynamicTasks.queue("install", new Runnable() {
-                @Override
-                public void run() {
-                    DynamicTasks.queue(
-                        SaltSshTasks.installSalt(false),
-                        SaltSshTasks.installSaltUtilities(false),
-                        SaltSshTasks.configureForMasterlessOperation(false),
-                        SaltSshTasks.installTopFile(startStates, false));
-
-                    if (Strings.isNonBlank(entityId)) {
-                        DynamicTasks.queue(SaltSshTasks.setMinionId(entityId));
-                    }
-                    installFormulas(formulas);
-                    installPillars(pillars, pillarUrls);
-                }
-            });
-        }
-
-        startSalt();
-
-        connectSensors();
-    }
-
-
-    private static final Pattern FAILURES = 
Pattern.compile(".*^Failed:\\s+(\\d+)$.*", MULTILINE | DOTALL);
-    private static final String ZERO = "0";
-
-    private void startSalt() {
-        String name = "apply top states";
-        final ProcessTaskWrapper<Integer> topStates = 
queueAndBlock(SaltSshTasks.applyTopStates(false).summary(name));
-
-        // Salt apply returns exit code 0 even upon failure so check the 
stdout.
-        final Matcher failCount = FAILURES.matcher(topStates.getStdout());
-        if (!failCount.matches() || !ZERO.equals(failCount.group(1))) {
-            LOG.warn("Encountered error in applying Salt top states: {}", 
topStates.getStdout());
-            throw new RuntimeException(
-                "Encountered error in applying Salt top states, see '" + name 
+ "' in activities for details");
-        }
-    }
-
-    private void installFormulas(Set<? extends String> formulas) {
-        if (formulas.size() > 0) {
-            DynamicTasks.queue(SaltSshTasks.enableFileRoots(false));
-
-            final TaskBuilder<Object> formulaTasks = 
TaskBuilder.builder().displayName("install formulas");
-            for (String url : formulas) {
-                formulaTasks.add(SaltSshTasks.installSaltFormula(url, 
false).newTask());
-            }
-            DynamicTasks.queue(formulaTasks.build());
-        }
-    }
-
-    private void installPillars(Set<? extends String> pillars, Set<? extends 
String> pillarUrls) {
-        if (pillarUrls.size() > 0) {
-            final TaskBuilder<Object> pillarTasks = 
TaskBuilder.builder().displayName("install pillars");
-            
pillarTasks.add(SaltSshTasks.invokeSaltUtility("init_pillar_config", null, 
false)
-                .summary("init pillar config").newTask());
-            for (String pillar : pillars) {
-                pillarTasks.add(SaltSshTasks.addPillarToTop(pillar, 
false).newTask());
-            }
-            for (String url : pillarUrls) {
-                pillarTasks.add(SaltSshTasks.installSaltPillar(url, 
false).newTask());
-            }
-            DynamicTasks.queue(pillarTasks.build());
-        }
-    }
-
-    private void connectSensors() {
-        final ProcessTaskWrapper<String> retrieveHighstate = 
SaltSshTasks.retrieveHighstate();
-        final ProcessTaskWrapper<String> highstate = 
DynamicTasks.queue(retrieveHighstate).block();
-        String stateDescription = highstate.get();
-
-        SaltHighstate.applyHighstate(stateDescription, entity());
-    }
-
-    @Override
-    protected void postStartCustom() {
-        // TODO: check for package installed?
-        entity().sensors().set(SoftwareProcess.SERVICE_UP, true);
-        super.postStartCustom();
-    }
-
-
-    @Override
-    protected String stopProcessesAtMachine() {
-        final Set<? extends String> stopStates = 
entity().getConfig(SaltConfig.STOP_STATES);
-        LOG.debug("Executing Salt stopProcessesAtMachine with states {}", 
stopStates);
-        if (stopStates.isEmpty()) {
-            stopBasedOnStartStates();
-        } else {
-            applyStates(stopStates);
-        }
-        return null;
-    }
-
-    private void applyStates(Set<? extends String> states) {
-        for (String state : states) {
-            DynamicTasks.queue(SaltSshTasks.applyState(state, 
false).summary("apply state " + state));
-        }
-    }
-
-    private void stopBasedOnStartStates() {
-        final Set<? extends String> startStates = 
entity().getConfig(SaltConfig.START_STATES);
-        final MutableSet<String> stopStates = addSuffix(startStates, ".stop");
-        final ProcessTaskWrapper<Integer> checkStops =
-            queueAndBlock(SaltSshTasks.verifyStates(stopStates, 
false).summary("check stop states"));
-        if (0 != checkStops.getExitCode()) {
-            throw new RuntimeException("No stop_states configured and not all 
start_states have matching stop states");
-        } else {
-            applyStates(stopStates);
-        }
-    }
-
-    @Override
-    public void restart(ConfigBag parameters) {
-        ServiceStateLogic.setExpectedState(entity(), Lifecycle.STOPPING);
-
-        try {
-            final Set<? extends String> restartStates = 
entity().getConfig(SaltConfig.RESTART_STATES);
-            LOG.debug("Executing Salt restart with states {}", restartStates);
-            if (restartStates.isEmpty()) {
-                restartBasedOnStartStates();
-            } else {
-                applyStates(restartStates);
-            }
-            ServiceStateLogic.setExpectedState(entity(), Lifecycle.RUNNING);
-        } catch (Exception e) {
-            
entity().sensors().set(ServiceStateLogic.SERVICE_NOT_UP_DIAGNOSTICS,
-                ImmutableMap.<String, Object>of("restart", e.getMessage()));
-            ServiceStateLogic.setExpectedState(entity(), Lifecycle.ON_FIRE);
-        }
-    }
-
-    private void restartBasedOnStartStates() {
-        final Set<? extends String> startStates = 
entity().getConfig(SaltConfig.START_STATES);
-        final MutableSet<String> restartStates = addSuffix(startStates, 
".restart");
-        final ProcessTaskWrapper<Integer> queued =
-            queueAndBlock(SaltSshTasks.findStates(restartStates, 
false).summary("check restart states"));
-        final String stdout = queued.getStdout();
-        String[] foundStates = Strings.isNonBlank(stdout) ? 
stdout.split("\\n") : null;
-
-        if (restartStates.size() > 0 && foundStates != null && 
(restartStates.size() == foundStates.length)) {
-            // each state X listed in start_states has a matching state of the 
form X.restart;  we apply them.
-            LOG.debug("All start_states have matching restart states, applying 
these");
-            applyStates(restartStates);
-
-        } else if (foundStates != null && foundStates.length > 0) {
-            // only *some* of the states have a matching restart; we treat 
this as a fail
-            LOG.debug("Only some start_states have matching restart states, 
treating as restart failure") ;
-            throw new RuntimeException("unable to find restart state for all 
applied states");
-
-        } else {
-            // else we apply "stop" effector (with parameters to stop 
processes not machine) then "start"
-            // (and in that effector we'd fail if stop was not well-defined)
-            LOG.debug("No stop states available, invoking stop and start 
effectors");
-            invokeEffector(Startable.STOP, ConfigBag.newInstance()
-                .configure(StopSoftwareParameters.STOP_PROCESS_MODE, ALWAYS)
-                .configure(StopSoftwareParameters.STOP_MACHINE_MODE, NEVER));
-            invokeEffector(Startable.START, ConfigBag.EMPTY);
-        }
-    }
-
-    private ProcessTaskWrapper<Integer> 
queueAndBlock(ProcessTaskFactory<Integer> taskFactory) {
-        final ProcessTaskWrapper<Integer> queued = 
DynamicTasks.queue(taskFactory);
-        queued.asTask().blockUntilEnded();
-        return queued;
-    }
-
-    private void invokeEffector(Effector<Void> effector, ConfigBag config) {
-        final TaskAdaptable<Void> stop = Entities.submit(entity(), 
Effectors.invocation(entity(), effector, config));
-        stop.asTask().blockUntilEnded();
-    }
-
-    private MutableSet<String> addSuffix(Set<? extends String> names, String 
suffix) {
-        final MutableSet<String> suffixed = MutableSet.of();
-        for (String name : names) {
-            suffixed.add(name + suffix);
-        }
-        return suffixed;
-    }
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/ac837bca/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltSshDriver.java
----------------------------------------------------------------------
diff --git 
a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltSshDriver.java
 
b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltSshDriver.java
deleted file mode 100644
index cd9cb38..0000000
--- 
a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltSshDriver.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.brooklyn.entity.cm.salt.impl;
-
-
-public class SaltSshDriver {
-
-    private SaltSshDriver() {
-        // Utility class
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/ac837bca/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltSshTasks.java
----------------------------------------------------------------------
diff --git 
a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltSshTasks.java
 
b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltSshTasks.java
deleted file mode 100644
index a8e1af6..0000000
--- 
a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltSshTasks.java
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.brooklyn.entity.cm.salt.impl;
-
-import org.apache.brooklyn.api.mgmt.TaskAdaptable;
-import org.apache.brooklyn.api.mgmt.TaskFactory;
-import org.apache.brooklyn.core.effector.EffectorTasks;
-import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks;
-import 
org.apache.brooklyn.core.effector.ssh.SshEffectorTasks.SshEffectorTaskFactory;
-import org.apache.brooklyn.util.collections.MutableList;
-import org.apache.brooklyn.util.core.ResourceUtils;
-import org.apache.brooklyn.util.core.file.ArchiveTasks;
-import org.apache.brooklyn.util.core.task.TaskBuilder;
-import org.apache.brooklyn.util.core.task.Tasks;
-import org.apache.brooklyn.util.core.task.ssh.SshPutTaskFactory;
-import org.apache.brooklyn.util.core.task.system.ProcessTaskFactory;
-import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper;
-import org.apache.brooklyn.util.ssh.BashCommands;
-import org.apache.brooklyn.util.text.Identifiers;
-import org.apache.brooklyn.util.text.Strings;
-
-import java.util.List;
-import java.util.Set;
-
-import static org.apache.brooklyn.core.effector.ssh.SshEffectorTasks.ssh;
-import static org.apache.brooklyn.util.ssh.BashCommands.sudo;
-
-
-public class SaltSshTasks {
-
-    private static final String UTILITY_SCRIPT = "salt_utilities.sh";
-
-    private SaltSshTasks() {
-        // Utility class
-    }
-
-    public static TaskFactory<?> installSalt(boolean force) {
-        // TODO: ignore force?
-        return sshCommands(
-            
BashCommands.commandToDownloadUrlAs("https://bootstrap.saltstack.com";, 
"install_salt.sh"),
-            sudo("sh install_salt.sh")
-        )
-        .summary("install salt");
-    }
-
-    public static SshEffectorTaskFactory<Integer> isSaltInstalled(boolean 
force) {
-        return invokeSaltUtility("salt_installed", null, true).summary("check 
installed");
-    }
-
-    public static TaskFactory<?> configureForMasterlessOperation(boolean 
force) {
-        // TODO: ignore force?
-        return ssh(sudo("sed -i '/^#file_client/c file_client: local' 
/etc/salt/minion"))
-            .summary("configure masterless");
-    }
-
-
-    public static TaskFactory<?> enableFileRoots(boolean force) {
-        return sshCommands(
-                "grep ^file_roots /etc/salt/minion || {",
-                "cat /etc/salt/minion > /tmp/minion.update",
-                "cat >> /tmp/minion.update  << BROOKLYN_EOF",
-                "file_roots:",
-                "  base:",
-                "    - /srv/salt/",
-                "BROOKLYN_EOF",
-                sudo("mv /tmp/minion.update /etc/salt/minion"),
-                "}"
-            )
-            .requiringExitCodeZero()
-            .summary("enable file_roots");
-    }
-
-
-    public static TaskFactory<?>  addPillarToTop(String pillar, boolean b) {
-        return invokeSaltUtility("add_pillar_to_top", pillar, false)
-            .summary("Add pillar " + pillar + " to top");
-    }
-
-    public static TaskFactory<?>  installSaltPillar(final String pillarUrl, 
boolean force) {
-        return new TaskFactory<TaskAdaptable<?>>() {
-            @Override
-            public TaskAdaptable<?> newTask() {
-                TaskBuilder<Void> tb = 
Tasks.<Void>builder().displayName(pillarUrl);
-                String tempDownloadDir = "/tmp/download-" + 
Identifiers.makeRandomId(12);
-
-                
tb.add(ArchiveTasks.deploy(null,null,pillarUrl,EffectorTasks.findSshMachine(),
-                    tempDownloadDir,false,null,null).newTask());
-
-                tb.add(ssh("cd " + tempDownloadDir + " ; " + sudo("mv * 
/srv/pillar")).newTask());
-
-                return tb.build();
-            }
-        };
-    }
-
-    public static TaskFactory<?> installSaltFormula(final String formulaUrl, 
boolean force) {
-        return new TaskFactory<TaskAdaptable<?>>() {
-            @Override
-            public TaskAdaptable<?> newTask() {
-                TaskBuilder<Void> tb = 
Tasks.<Void>builder().displayName(formulaUrl);
-
-                String tempDirectoryForUnpack = "/tmp/download-" + 
Identifiers.makeRandomId(12);
-
-                tb.add(ArchiveTasks.deploy(null, null, formulaUrl, 
EffectorTasks.findSshMachine(),
-                    tempDirectoryForUnpack, false, null, null).newTask());
-
-                // TODO move this into salt_utilities.sh
-                String installCmd = BashCommands.chain(
-                    "cd "+tempDirectoryForUnpack,
-                    "EXPANDED_DIR=`ls`",
-                    BashCommands.requireTest("`ls | wc -w` -eq 1",
-                        "The deployed archive "+ formulaUrl +" must contain 
exactly one directory"),
-                    "sudo mkdir -p /srv/formula",
-                    "sudo mv $EXPANDED_DIR /srv/formula/",
-                    // sed command below relies on enableFileRoots behaviour 
of append file_roots to end of config file
-                    "sudo sed -i \"$ a\\    - /srv/formula/$EXPANDED_DIR\" 
/etc/salt/minion",
-                    "cd ..",
-                    "rm -rf '"+tempDirectoryForUnpack+"'");
-                tb.add(ssh(installCmd).summary("installing " + formulaUrl + " 
states to /srv/formula")
-                    .requiringExitCodeZero().newTask());
-
-                return tb.build();
-            }
-        };
-    }
-
-    public static TaskFactory<?> installTopFile(final Set<? extends String> 
runList, boolean force) {
-        // TODO: ignore force?
-        // TODO: move this into salt_utilities.sh
-        final MutableList.Builder<String> topBuilder = 
MutableList.<String>builder()
-            .add("cat > /tmp/top.sls << BROOKLYN_EOF")
-            .add("base:")
-            .add("  '*':");
-        for (String stateName: runList) {
-            topBuilder.add("    - " + stateName);
-        }
-        topBuilder.add("BROOKLYN_EOF");
-        List<String> createTempTopFile = topBuilder.build();
-
-        List<String> commands = MutableList.<String>builder()
-            .add(sudo("mkdir -p /srv/salt"))
-            .add(Strings.join(createTempTopFile, "\n"))
-            .add(sudo("mv /tmp/top.sls /srv/salt"))
-            .build();
-        return ssh(commands).summary("create top.sls file");
-    }
-
-    public static ProcessTaskFactory<Integer> applyTopStates(boolean force) {
-        return  saltCall("state.apply");
-    }
-
-    public static SshEffectorTaskFactory<Integer> applyState(String state, 
boolean force) {
-        return saltCall("state.apply " + state);
-    }
-
-    public static SshEffectorTaskFactory<Integer> saltCall(String command) {
-        return ssh(sudo("salt-call --local " + 
command)).allowingNonZeroExitCode();
-    }
-
-    public static ProcessTaskWrapper<String> retrieveHighstate() {
-        return saltCall("state.show_highstate --out=yaml")
-            .summary("retrieve highstate")
-            .requiringZeroAndReturningStdout()
-            .newTask();
-    }
-
-    public static TaskFactory<?> installSaltUtilities(boolean force) {
-        return new TaskFactory<TaskAdaptable<?>>() {
-            @Override
-            public TaskAdaptable<?> newTask() {
-                final TaskBuilder<Void> builder = Tasks.<Void>builder()
-                    .displayName("install salt utilities")
-                    .add(installScript(UTILITY_SCRIPT, "install salt shell 
utils").newTask())
-                    .add(ssh(sudo("mv /tmp/" + UTILITY_SCRIPT + " 
/etc/salt")).newTask());
-                return builder.build();
-            }
-        };
-    }
-
-    private static SshPutTaskFactory installScript(String name, String 
description) {
-        return SshEffectorTasks.put("/tmp/" + name)
-                
.contents(ResourceUtils.create().getResourceFromUrl("classpath:" + name))
-                .summary(description);
-    }
-
-    public static SshEffectorTaskFactory<Integer> verifyStates(Set<String> 
states, boolean force) {
-        return invokeSaltUtility("verify_states", Strings.join(states, " "), 
true);
-    }
-
-    public static SshEffectorTaskFactory<Integer> findStates(Set<String> 
states, boolean force) {
-        return invokeSaltUtility("find_states", Strings.join(states, " "), 
true);
-    }
-
-    // Simple invocation of a function from salt_utilities.sh, optionally 
allowing it to fail.
-    // Uses single quoted bash command, so args mustn't contain single quotes.
-    public static SshEffectorTaskFactory<Integer> invokeSaltUtility(String 
functionName, String args, boolean permitFailure) {
-
-        final SshEffectorTaskFactory<Integer> taskFactory =
-            ssh(sudo("/bin/bash -c '. /etc/salt/salt_utilities.sh ; " + 
functionName + " " + args + "'"))
-            .summary(functionName);
-
-        if (permitFailure) {
-            taskFactory.allowingNonZeroExitCode();
-        } else {
-            taskFactory.requiringExitCodeZero();
-        }
-        return taskFactory;
-
-    }
-
-    public static SshEffectorTaskFactory<Integer> sshCommands(String line, 
String... lines) {
-        final MutableList.Builder<String> builder = 
MutableList.<String>builder()
-            .add(line);
-        builder.addAll(lines);
-        return ssh(Strings.join(builder.build(), "\n"));
-    }
-
-    public static SshEffectorTaskFactory<Integer> setMinionId(final String 
entityId) {
-        return invokeSaltUtility("set_minion_id", entityId, true);
-    }
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/ac837bca/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltUtils.java
----------------------------------------------------------------------
diff --git 
a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltUtils.java
 
b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltUtils.java
deleted file mode 100644
index b11c06e..0000000
--- 
a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltUtils.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.brooklyn.entity.cm.salt.impl;
-
-import org.apache.brooklyn.api.location.LocationDefinition;
-import org.apache.brooklyn.api.mgmt.ManagementContext;
-
-public class SaltUtils {
-
-    private SaltUtils() {
-        // Utility class
-    }
-
-    public static ManagementContext.PropertiesReloadListener 
propertiesReloadListener(
-            ManagementContext mc, LocationDefinition definition) {
-
-        return new ManagementContext.PropertiesReloadListener() {
-            private static final long serialVersionUID = 1L;
-            @Override
-            public void reloaded() {
-                // TODO: implement properties reload logic
-            }
-        };
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/ac837bca/software/cm/salt/src/test/java/org/apache/brooklyn/entity/cm/salt/HighstateTest.java
----------------------------------------------------------------------
diff --git 
a/software/cm/salt/src/test/java/org/apache/brooklyn/entity/cm/salt/HighstateTest.java
 
b/software/cm/salt/src/test/java/org/apache/brooklyn/entity/cm/salt/HighstateTest.java
index 50205e0..e5d130a 100644
--- 
a/software/cm/salt/src/test/java/org/apache/brooklyn/entity/cm/salt/HighstateTest.java
+++ 
b/software/cm/salt/src/test/java/org/apache/brooklyn/entity/cm/salt/HighstateTest.java
@@ -20,7 +20,6 @@ package org.apache.brooklyn.entity.cm.salt;
 
 import com.google.common.collect.ImmutableSet;
 import org.apache.brooklyn.api.entity.EntitySpec;
-import org.apache.brooklyn.entity.cm.salt.impl.SaltHighstate;
 import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.core.sensor.Sensors;
 import org.apache.brooklyn.core.test.entity.TestApplication;

Reply via email to