You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2016/02/19 11:07:38 UTC

[1/7] brooklyn-library git commit: Moving Ansible as a module so it matches the new CM structure

Repository: brooklyn-library
Updated Branches:
  refs/heads/master 9fdf1dbf6 -> b8395ff30


Moving Ansible as a module so it matches the new CM structure

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

Branch: refs/heads/master
Commit: c1a87838197f172ec5179831ba3824f0ead117a4
Parents: 121db32
Author: Yavor Yanchev <ya...@yanchev.com>
Authored: Tue Feb 16 11:10:07 2016 +0200
Committer: Yavor Yanchev <ya...@yanchev.com>
Committed: Wed Feb 17 11:43:03 2016 +0200

----------------------------------------------------------------------
 software/cm/ansible/pom.xml                     |  84 ++++++++
 .../entity/cm/ansible/AnsibleBashCommands.java  |  38 ++++
 .../entity/cm/ansible/AnsibleConfig.java        |  66 ++++++
 .../entity/cm/ansible/AnsibleEntity.java        |  40 ++++
 .../entity/cm/ansible/AnsibleEntityImpl.java    |  61 ++++++
 .../ansible/AnsibleLifecycleEffectorTasks.java  | 203 +++++++++++++++++++
 .../entity/cm/ansible/AnsiblePlaybookTasks.java | 109 ++++++++++
 .../ansible/AnsibleEntityIntegrationTest.java   | 116 +++++++++++
 software/cm/pom.xml                             |  53 +----
 .../entity/cm/ansible/AnsibleBashCommands.java  |  38 ----
 .../entity/cm/ansible/AnsibleConfig.java        |  66 ------
 .../entity/cm/ansible/AnsibleEntity.java        |  40 ----
 .../entity/cm/ansible/AnsibleEntityImpl.java    |  61 ------
 .../ansible/AnsibleLifecycleEffectorTasks.java  | 203 -------------------
 .../entity/cm/ansible/AnsiblePlaybookTasks.java | 109 ----------
 .../ansible/AnsibleEntityIntegrationTest.java   | 116 -----------
 16 files changed, 718 insertions(+), 685 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/c1a87838/software/cm/ansible/pom.xml
----------------------------------------------------------------------
diff --git a/software/cm/ansible/pom.xml b/software/cm/ansible/pom.xml
new file mode 100644
index 0000000..028d73d
--- /dev/null
+++ b/software/cm/ansible/pom.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>brooklyn-software-cm-ansible</artifactId>
+  <packaging>jar</packaging>
+  <name>Brooklyn CM Ansible</name>
+  <description>Brooklyn entities for Configuration Management using Ansible</description>
+
+
+    <parent>
+        <groupId>org.apache.brooklyn</groupId>
+        <artifactId>brooklyn-library</artifactId>
+        <version>0.9.0-SNAPSHOT</version>  <!-- BROOKLYN_VERSION -->
+        <relativePath>../../../pom.xml</relativePath>
+    </parent>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.brooklyn</groupId>
+      <artifactId>brooklyn-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.brooklyn</groupId>
+      <artifactId>brooklyn-software-base</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+         <groupId>org.apache.brooklyn</groupId>
+         <artifactId>brooklyn-camp</artifactId>
+        <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.brooklyn</groupId>
+      <artifactId>brooklyn-test-support</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.brooklyn</groupId>
+      <artifactId>brooklyn-core</artifactId>
+      <version>${project.version}</version>
+      <classifier>tests</classifier>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.brooklyn</groupId>
+      <artifactId>brooklyn-software-base</artifactId>
+      <version>${project.version}</version>
+      <classifier>tests</classifier>
+      <scope>test</scope>
+    </dependency>
+
+      <dependency>
+          <groupId>org.assertj</groupId>
+          <artifactId>assertj-core</artifactId>
+          <version>${assertj.version}</version>
+          <scope>test</scope>
+      </dependency>
+
+  </dependencies>
+
+</project>

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/c1a87838/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleBashCommands.java
----------------------------------------------------------------------
diff --git a/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleBashCommands.java b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleBashCommands.java
new file mode 100644
index 0000000..e22ee34
--- /dev/null
+++ b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleBashCommands.java
@@ -0,0 +1,38 @@
+/*
+ * 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.ansible;
+
+import static org.apache.brooklyn.util.ssh.BashCommands.INSTALL_CURL;
+import static org.apache.brooklyn.util.ssh.BashCommands.INSTALL_TAR;
+import static org.apache.brooklyn.util.ssh.BashCommands.INSTALL_UNZIP;
+import static org.apache.brooklyn.util.ssh.BashCommands.installExecutable;
+import static org.apache.brooklyn.util.ssh.BashCommands.sudo;
+
+import org.apache.brooklyn.util.ssh.BashCommands;
+
+public class AnsibleBashCommands {
+
+    public static final String INSTALL_ANSIBLE =
+            BashCommands.chain(
+                    sudo("apt-add-repository -y ppa:ansible/ansible"),
+                    INSTALL_CURL,
+                    INSTALL_TAR,
+                    INSTALL_UNZIP,
+                    installExecutable("ansible"));
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/c1a87838/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleConfig.java
----------------------------------------------------------------------
diff --git a/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleConfig.java b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleConfig.java
new file mode 100644
index 0000000..d63c2c6
--- /dev/null
+++ b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleConfig.java
@@ -0,0 +1,66 @@
+/*
+ * 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.ansible;
+
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.util.core.flags.SetFromFlag;
+
+import com.google.common.annotations.Beta;
+
+/** {@link ConfigKey}s used to configure Ansible */
+@Beta
+public interface AnsibleConfig {
+
+    enum AnsibleModes {
+        PLAYBOOK
+    };
+
+    @SetFromFlag("playbook")
+    ConfigKey<String> ANSIBLE_PLAYBOOK = ConfigKeys.newStringConfigKey("brooklyn.ansible.playbook",
+        "Playbook to be execute by Ansible");
+
+    @SetFromFlag("playbook.yaml")
+    ConfigKey<String> ANSIBLE_PLAYBOOK_YAML = ConfigKeys.newStringConfigKey("brooklyn.ansible.playbookYaml",
+        "Playbook to be execute by Ansible");
+
+    @SetFromFlag("playbook.url")
+    ConfigKey<String> ANSIBLE_PLAYBOOK_URL = ConfigKeys.newStringConfigKey("brooklyn.ansible.playbookUrl");
+
+    @SetFromFlag("ansible.service.start")
+    ConfigKey<String> ANSIBLE_SERVICE_START = ConfigKeys.newStringConfigKey("ansible.service.start",
+            "Default start command used with conjunction with the Ansible's service module",
+            "sudo ansible localhost -c local -m service -a \"name=%s state=started\"");
+
+    @SetFromFlag("ansible.service.stop")
+    ConfigKey<String> ANSIBLE_SERVICE_STOP = ConfigKeys.newStringConfigKey("ansible.service.stop",
+            "Default stop command used with conjunction with the Ansible's service module",
+            "sudo ansible localhost -c local -m service -a \"name=%s state=stopped\"");
+
+    @SetFromFlag("ansible.service.checkPort")
+    ConfigKey<Integer> ANSIBLE_SERVICE_CHECK_PORT = ConfigKeys.newIntegerConfigKey("ansible.service.check.port");
+
+    @SetFromFlag("service.name")
+    ConfigKey<String> SERVICE_NAME = ConfigKeys.newStringConfigKey("brooklyn.ansible.serviceName",
+        "Name of OS service this will run as, for use in checking running and stopping");
+
+    @SetFromFlag("ansible.vars")
+    ConfigKey<Object> ANSIBLE_VARS = ConfigKeys.newConfigKey(Object.class, "brooklyn.ansible.vars",
+        "Ansible 'extra-vars' variable configuration values");
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/c1a87838/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntity.java
----------------------------------------------------------------------
diff --git a/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntity.java b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntity.java
new file mode 100644
index 0000000..a2c9676
--- /dev/null
+++ b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntity.java
@@ -0,0 +1,40 @@
+/*
+ * 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.ansible;
+
+import org.apache.brooklyn.api.entity.ImplementedBy;
+import org.apache.brooklyn.core.annotation.Effector;
+import org.apache.brooklyn.core.annotation.EffectorParam;
+import org.apache.brooklyn.core.effector.MethodEffector;
+import org.apache.brooklyn.entity.software.base.SoftwareProcess;
+
+@ImplementedBy(AnsibleEntityImpl.class)
+public interface AnsibleEntity extends SoftwareProcess, AnsibleConfig {
+
+    MethodEffector<String> ANSIBLE_COMMAND = new MethodEffector<>(AnsibleEntity.class, "ansibleCommand");
+
+    @Effector(description = "Invoke an arbitrary Ansible command, optionally specifying the module (default is 'command')")
+    String ansibleCommand(
+        @EffectorParam(name="module", description = "Name of the Ansible module to invoke", defaultValue = "command")
+        String module,
+        @EffectorParam(name="args", description = "Arguments for the ansible command")
+        String args
+    );
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/c1a87838/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityImpl.java
----------------------------------------------------------------------
diff --git a/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityImpl.java b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityImpl.java
new file mode 100644
index 0000000..c2635ca
--- /dev/null
+++ b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityImpl.java
@@ -0,0 +1,61 @@
+/*
+ * 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.ansible;
+
+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.apache.brooklyn.util.text.Strings;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class AnsibleEntityImpl extends EffectorStartableImpl implements AnsibleEntity {
+
+    private AnsibleLifecycleEffectorTasks lifecycleTasks;
+
+    public void init() {
+        checkNotNull(getConfig(SERVICE_NAME), "service name is missing. it has to be provided by the user");
+        String playbookName = getConfig(ANSIBLE_PLAYBOOK);
+        if (!Strings.isBlank(playbookName)) setDefaultDisplayName(playbookName + " (ansible)");
+
+        super.init();
+
+        lifecycleTasks = new AnsibleLifecycleEffectorTasks();
+
+        lifecycleTasks.attachLifecycleEffectors(this);
+    }
+
+    @Override
+    public void populateServiceNotUpDiagnostics() {
+        // TODO no-op currently; should check ssh'able etc
+    }
+
+    @Override
+    public String ansibleCommand(String module, String args) {
+        final ProcessTaskWrapper<Integer> command = DynamicTasks.queue(
+            AnsiblePlaybookTasks.moduleCommand(module, config().get(ANSIBLE_VARS), lifecycleTasks.getRunDir(), args));
+        command.asTask().blockUntilEnded();
+        if (0 == command.getExitCode()) {
+            return command.getStdout();
+        } else {
+            throw new RuntimeException("Command (" + args + ") in module " + module
+                + " failed with stderr:\n" + command.getStderr() + "\n");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/c1a87838/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleLifecycleEffectorTasks.java
----------------------------------------------------------------------
diff --git a/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleLifecycleEffectorTasks.java b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleLifecycleEffectorTasks.java
new file mode 100644
index 0000000..e1a8622
--- /dev/null
+++ b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleLifecycleEffectorTasks.java
@@ -0,0 +1,203 @@
+/*
+ * 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.ansible;
+
+import com.google.common.base.Supplier;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.location.MachineLocation;
+import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks;
+import org.apache.brooklyn.core.entity.Attributes;
+import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
+import org.apache.brooklyn.core.entity.trait.Startable;
+import org.apache.brooklyn.core.location.Locations;
+import org.apache.brooklyn.core.location.Machines;
+import org.apache.brooklyn.entity.software.base.SoftwareProcess;
+import org.apache.brooklyn.entity.software.base.lifecycle.MachineLifecycleEffectorTasks;
+import org.apache.brooklyn.feed.ssh.SshFeed;
+import org.apache.brooklyn.feed.ssh.SshPollConfig;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.apache.brooklyn.util.core.task.DynamicTasks;
+import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.net.Urls;
+import org.apache.brooklyn.util.text.Strings;
+import org.apache.brooklyn.util.time.Duration;
+import org.apache.brooklyn.util.time.Time;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AnsibleLifecycleEffectorTasks extends MachineLifecycleEffectorTasks implements AnsibleConfig {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AnsibleLifecycleEffectorTasks.class);
+
+    protected String serviceName;
+    protected SshFeed serviceSshFeed;
+
+    protected Object extraVars;
+    protected String baseDir;
+    protected String runDir;
+
+    public AnsibleLifecycleEffectorTasks() {
+    }
+
+    public String getServiceName() {
+        if (serviceName!=null) return serviceName;
+        return serviceName = entity().config().get(AnsibleConfig.SERVICE_NAME);
+    }
+
+    public Object getExtraVars() {
+        if (extraVars != null) return extraVars;
+        return extraVars = entity().config().get(ANSIBLE_VARS);
+    }
+
+    public String getBaseDir() {
+        if (null != baseDir) return baseDir;
+        return baseDir = MachineLifecycleEffectorTasks.resolveOnBoxDir(entity(),
+                Machines.findUniqueMachineLocation(entity().getLocations(), SshMachineLocation.class).get());
+    }
+
+    public String getRunDir() {
+        if (null != runDir) return runDir;
+        return runDir = Urls.mergePaths(getBaseDir(), "apps/"+entity().getApplicationId()+"/ansible/playbooks/"
+            +entity().getEntityType().getSimpleName()+"_"+entity().getId());
+    }
+
+    @Override
+    public void attachLifecycleEffectors(Entity entity) {
+        if (getServiceName()==null && getClass().equals(AnsibleLifecycleEffectorTasks.class)) {
+            // warn on incorrect usage
+            LOG.warn("Uses of "+getClass()+" must define a PID file or a service name (or subclass and override {start,stop} methods as per javadoc) " +
+                    "in order for check-running and stop to work");
+        }
+        super.attachLifecycleEffectors(entity);
+    }
+
+    @Override
+    protected String startProcessesAtMachine(Supplier<MachineLocation> machineS) {
+        startWithAnsibleAsync();
+
+        return "ansible start tasks submitted";
+    }
+
+    protected String getPlaybookName() {
+        return entity().config().get(ANSIBLE_PLAYBOOK);
+    }
+
+    protected void startWithAnsibleAsync() {
+        String installDir = Urls.mergePaths(getBaseDir(), "installs/ansible");
+
+        String playbookUrl = entity().config().get(ANSIBLE_PLAYBOOK_URL);
+        String playbookYaml = entity().config().get(ANSIBLE_PLAYBOOK_YAML);
+
+        if (Strings.isNonBlank(playbookUrl) && Strings.isNonBlank(playbookYaml)) {
+            throw new IllegalArgumentException("You can specify " +  AnsibleConfig.ANSIBLE_PLAYBOOK_URL.getName()
+                +  " or " + AnsibleConfig.ANSIBLE_PLAYBOOK_YAML.getName() + " but not both of them!");
+        }
+
+        DynamicTasks.queue(AnsiblePlaybookTasks.installAnsible(installDir, false));
+
+        if (getExtraVars() != null) {
+            DynamicTasks.queue(AnsiblePlaybookTasks.configureExtraVars(getRunDir(), extraVars, false));
+        }
+
+        if (Strings.isNonBlank(playbookUrl)) {
+            DynamicTasks.queue(AnsiblePlaybookTasks.installPlaybook(getRunDir(), getPlaybookName(), playbookUrl));
+        }
+
+        if (Strings.isNonBlank(playbookYaml)) {
+            DynamicTasks.queue(AnsiblePlaybookTasks.buildPlaybookFile(getRunDir(), getPlaybookName()));
+        }
+        DynamicTasks.queue(AnsiblePlaybookTasks.runAnsible(getRunDir(), getExtraVars(), getPlaybookName()));
+    }
+
+
+    protected void postStartCustom() {
+        boolean result = false;
+        result |= tryCheckStartService();
+
+        if (!result) {
+            LOG.warn("No way to check whether "+entity()+" is running; assuming yes");
+        }
+        entity().sensors().set(SoftwareProcess.SERVICE_UP, true);
+        
+        Maybe<SshMachineLocation> machine = Locations.findUniqueSshMachineLocation(entity().getLocations());
+
+        if (machine.isPresent()) {
+            
+            String serviceName = String.format("[%s]%s", entity().config().get(SERVICE_NAME).substring(0, 1),
+                    entity().config().get(SERVICE_NAME).substring(1));
+            String checkCmd = String.format("ps -ef | grep %s", serviceName);
+
+            Integer serviceCheckPort = entity().config().get(ANSIBLE_SERVICE_CHECK_PORT);
+
+            if (serviceCheckPort != null) {
+                checkCmd = String.format("sudo ansible localhost -c local -m wait_for -a \"host=0.0.0.0 port=%d\"", serviceCheckPort);
+            }
+            serviceSshFeed = SshFeed.builder()
+                    .entity(entity())
+                    .period(Duration.ONE_MINUTE)
+                    .machine(machine.get())
+                    .poll(new SshPollConfig<Boolean>(Startable.SERVICE_UP)
+                            .command(checkCmd)
+                            .setOnSuccess(true)
+                            .setOnFailureOrException(false))
+                    .build();
+                    
+             entity().feeds().addFeed(serviceSshFeed);
+        } else {
+            LOG.warn("Location(s) {} not an ssh-machine location, so not polling for status; setting serviceUp immediately", entity().getLocations());
+        }
+    }
+
+    protected boolean tryCheckStartService() {
+        if (getServiceName()==null) return false;
+
+        // if it's still up after 5s assume we are good (default behaviour)
+        Time.sleep(Duration.FIVE_SECONDS);
+        if (!((Integer)0).equals(DynamicTasks.queue(SshEffectorTasks.ssh(String.format(entity().config().get(AnsibleConfig.ANSIBLE_SERVICE_START), getServiceName()))).get())) {
+            throw new IllegalStateException("The process for "+entity()+" appears not to be running (service "+getServiceName()+")");
+        }
+
+        return true;
+    }
+
+    @Override
+    protected String stopProcessesAtMachine() {
+        boolean result = false;
+        result |= tryStopService();
+        if (!result) {
+            throw new IllegalStateException("The process for "+entity()+" could not be stopped (no impl!)");
+        }
+        return "stopped";
+    }
+
+    @Override
+    protected StopMachineDetails<Integer> stopAnyProvisionedMachines() {
+        return super.stopAnyProvisionedMachines();
+    }
+
+    protected boolean tryStopService() {
+        if (getServiceName()==null) return false;
+        int result = DynamicTasks.queue(SshEffectorTasks.ssh(String.format(entity().config().get(AnsibleConfig.ANSIBLE_SERVICE_STOP), getServiceName()))).get();
+        if (0 == result) return true;
+        if (entity().getAttribute(Attributes.SERVICE_STATE_ACTUAL) != Lifecycle.RUNNING)
+            return true;
+        
+        throw new IllegalStateException("The process for "+entity()+" appears could not be stopped (exit code "+result+" to service stop)");
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/c1a87838/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java
----------------------------------------------------------------------
diff --git a/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java
new file mode 100644
index 0000000..36076a1
--- /dev/null
+++ b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java
@@ -0,0 +1,109 @@
+/*
+ * 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.ansible;
+
+import org.apache.brooklyn.api.entity.Entity;
+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.util.core.ResourceUtils;
+import org.apache.brooklyn.util.core.task.Tasks;
+import org.apache.brooklyn.util.core.task.system.ProcessTaskFactory;
+import org.apache.brooklyn.util.net.Urls;
+import org.apache.brooklyn.util.ssh.BashCommands;
+import org.apache.brooklyn.util.text.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+
+import static org.apache.brooklyn.util.ssh.BashCommands.sudo;
+
+public class AnsiblePlaybookTasks {
+    private static final Logger LOG = LoggerFactory.getLogger(AnsiblePlaybookTasks.class);
+    private static final String EXTRA_VARS_FILENAME = "extra_vars.yaml";
+
+    public static TaskFactory<?> installAnsible(String ansibleDirectory, boolean force) {
+        String installCmd = cdAndRun(ansibleDirectory, AnsibleBashCommands.INSTALL_ANSIBLE);
+        if (!force) installCmd = BashCommands.alternatives("which ansible", installCmd);
+        return SshEffectorTasks.ssh(installCmd).summary("install ansible");
+    }
+
+    public static TaskFactory<?> installPlaybook(final String ansibleDirectory, final String playbookName, final String playbookUrl) {
+        return Tasks.sequential("build ansible playbook file for "+playbookName,
+                SshEffectorTasks.put(ansibleDirectory + "/" + playbookName + ".yaml")
+                    .contents(ResourceUtils.create().getResourceFromUrl(playbookUrl))
+                    .createDirectory());
+    }
+    
+    protected static String cdAndRun(String targetDirectory, String command) {
+        return BashCommands.chain("mkdir -p "+targetDirectory,
+                "cd "+targetDirectory,
+                command);
+    }
+
+    public static TaskFactory<?> buildPlaybookFile(final String ansibleDirectory, String playbook) {
+        Entity entity = EffectorTasks.findEntity();
+        String yaml = entity.config().get(AnsibleConfig.ANSIBLE_PLAYBOOK_YAML);
+
+        return Tasks.sequential("build ansible playbook file for "+ playbook,
+            SshEffectorTasks.put(Urls.mergePaths(ansibleDirectory) + "/" + playbook + ".yaml")
+                .contents(yaml).createDirectory());
+    }
+
+    public static TaskFactory<?> runAnsible(final String dir, Object extraVars, String playbookName) {
+        String cmd = String.format("sudo ansible-playbook -i \"localhost,\" -c local "
+            + optionalExtraVarsParameter(extraVars)
+            + " -s %s.yaml", playbookName);
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Ansible command: {}", cmd);
+        }
+
+        return SshEffectorTasks.ssh(cdAndRun(dir, cmd)).
+                summary("run ansible playbook for " + playbookName).requiringExitCodeZero();
+    }
+
+    public static ProcessTaskFactory<Integer> moduleCommand(String module, Object extraVars, String root, String args) {
+        final String command = "ansible localhost "
+            + optionalExtraVarsParameter(extraVars)
+            + " -m '" + module + "' -a '" + args + "'";
+        return SshEffectorTasks.ssh(sudo(BashCommands.chain("cd " + root, command)))
+            .summary("ad-hoc: " + command).requiringExitCodeZero();
+    }
+
+    public static TaskFactory<?> configureExtraVars(String dir, Object extraVars, boolean force) {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(DumperOptions.ScalarStyle.DOUBLE_QUOTED);
+        Yaml asYaml = new Yaml(options);
+        final String varsYaml = asYaml.dump(extraVars);
+        return SshEffectorTasks.put(Urls.mergePaths(dir, EXTRA_VARS_FILENAME))
+            .contents(varsYaml)
+            .summary("install extra vars")
+            .createDirectory();
+    }
+
+    private static String optionalExtraVarsParameter(Object extraVars) {
+        if (null == extraVars || Strings.isBlank(extraVars.toString())) {
+            return "";
+        }
+        return " --extra-vars \"@" + EXTRA_VARS_FILENAME + "\" ";
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/c1a87838/software/cm/ansible/src/test/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/cm/ansible/src/test/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityIntegrationTest.java b/software/cm/ansible/src/test/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityIntegrationTest.java
new file mode 100644
index 0000000..76e993d
--- /dev/null
+++ b/software/cm/ansible/src/test/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityIntegrationTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.ansible;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.MachineDetails;
+import org.apache.brooklyn.api.location.OsDetails;
+import org.apache.brooklyn.api.mgmt.Task;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.EntityAsserts;
+import org.apache.brooklyn.core.entity.trait.Startable;
+import org.apache.brooklyn.core.location.BasicMachineDetails;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+
+public class AnsibleEntityIntegrationTest {
+    protected TestApplication app;
+    protected LocalhostMachineProvisioningLocation testLocation;
+    protected AnsibleEntity ansible;
+    protected SshMachineLocation sshHost;
+
+    String playbookYaml = Joiner.on("\n").join(
+            "---",
+            "- hosts: localhost",
+            "  sudo: True",
+            "",
+            "  tasks:",
+            "  - apt: name=apache2 state=latest",
+            "    when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu'",
+            "",
+            "  - yum: name=httpd state=latest",
+            "    when: ansible_distribution == 'CentOS' or ansible_distribution == 'Red Hat Enterprise Linux'",
+            "",
+            "  - service: name=apache2 state=started enabled=no",
+            "    when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu'",
+            "",
+            "  - service: name=httpd state=started enabled=no",
+            "    when: ansible_distribution == 'CentOS' or ansible_distribution == 'Red Hat Enterprise Linux'");
+
+    @BeforeMethod(alwaysRun = true)
+    public void setup() throws Exception {
+        app = TestApplication.Factory.newManagedInstanceForTests();;
+        testLocation = new LocalhostMachineProvisioningLocation();
+        sshHost = testLocation.obtain();
+    }
+
+    @AfterMethod(alwaysRun = true)
+    public void shutdown() {
+        Entities.destroyAll(app.getManagementContext());
+    }
+    
+    @Test(groups = {"Integration"})
+    public void testAnsible() {
+        Task<BasicMachineDetails> detailsTask = app.getExecutionContext().submit(
+                BasicMachineDetails.taskForSshMachineLocation(sshHost));
+        MachineDetails machine = detailsTask.getUnchecked();
+
+
+        OsDetails details = machine.getOsDetails();
+        
+        String osName = details.getName();
+        String playbookServiceName = getPlaybookServiceName(osName);
+        ansible = app.createAndManageChild(EntitySpec.create(AnsibleEntity.class)
+                .configure("playbook.yaml", playbookYaml)
+                .configure("playbook", playbookServiceName)
+                .configure("service.name", playbookServiceName));
+
+        app.start(ImmutableList.of(testLocation));
+        EntityAsserts.assertAttributeEqualsEventually(ansible, Startable.SERVICE_UP, true);
+
+        ansible.stop();
+        EntityAsserts.assertAttributeEqualsEventually(ansible, Startable.SERVICE_UP, false);
+    }
+
+    private String getPlaybookServiceName(String os) {
+        String name;
+
+        switch (os.toLowerCase()) {
+            case "fedora":
+                name = "httpd";
+                break;
+            case "centos":
+                name = "httpd";
+                break;
+            default:
+                name = "apache2";
+        }
+        return name;
+    }
+}
+
+

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/c1a87838/software/cm/pom.xml
----------------------------------------------------------------------
diff --git a/software/cm/pom.xml b/software/cm/pom.xml
index ea17774..8a86fb7 100644
--- a/software/cm/pom.xml
+++ b/software/cm/pom.xml
@@ -70,61 +70,10 @@
         </mailingList>
     </mailingLists>
 
-
-  <dependencies>
-    <dependency>
-      <groupId>org.apache.brooklyn</groupId>
-      <artifactId>brooklyn-api</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.brooklyn</groupId>
-      <artifactId>brooklyn-software-base</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.brooklyn</groupId>
-      <artifactId>brooklyn-software-database</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-    <dependency>
-         <groupId>org.apache.brooklyn</groupId>
-         <artifactId>brooklyn-camp</artifactId>
-        <version>${project.version}</version>
-    </dependency>
-
-    <dependency>
-      <groupId>org.apache.brooklyn</groupId>
-      <artifactId>brooklyn-test-support</artifactId>
-      <version>${project.version}</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.brooklyn</groupId>
-      <artifactId>brooklyn-core</artifactId>
-      <version>${project.version}</version>
-      <classifier>tests</classifier>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.brooklyn</groupId>
-      <artifactId>brooklyn-software-base</artifactId>
-      <version>${project.version}</version>
-      <classifier>tests</classifier>
-      <scope>test</scope>
-    </dependency>
-
-      <dependency>
-          <groupId>org.assertj</groupId>
-          <artifactId>assertj-core</artifactId>
-          <version>${assertj.version}</version>
-          <scope>test</scope>
-      </dependency>
-  </dependencies>
-
     <modules>
 
         <module>salt</module>
+        <module>ansible</module>
 
     </modules>
 

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/c1a87838/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleBashCommands.java
----------------------------------------------------------------------
diff --git a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleBashCommands.java b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleBashCommands.java
deleted file mode 100644
index e22ee34..0000000
--- a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleBashCommands.java
+++ /dev/null
@@ -1,38 +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.ansible;
-
-import static org.apache.brooklyn.util.ssh.BashCommands.INSTALL_CURL;
-import static org.apache.brooklyn.util.ssh.BashCommands.INSTALL_TAR;
-import static org.apache.brooklyn.util.ssh.BashCommands.INSTALL_UNZIP;
-import static org.apache.brooklyn.util.ssh.BashCommands.installExecutable;
-import static org.apache.brooklyn.util.ssh.BashCommands.sudo;
-
-import org.apache.brooklyn.util.ssh.BashCommands;
-
-public class AnsibleBashCommands {
-
-    public static final String INSTALL_ANSIBLE =
-            BashCommands.chain(
-                    sudo("apt-add-repository -y ppa:ansible/ansible"),
-                    INSTALL_CURL,
-                    INSTALL_TAR,
-                    INSTALL_UNZIP,
-                    installExecutable("ansible"));
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/c1a87838/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleConfig.java
----------------------------------------------------------------------
diff --git a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleConfig.java b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleConfig.java
deleted file mode 100644
index d63c2c6..0000000
--- a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleConfig.java
+++ /dev/null
@@ -1,66 +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.ansible;
-
-import org.apache.brooklyn.config.ConfigKey;
-import org.apache.brooklyn.core.config.ConfigKeys;
-import org.apache.brooklyn.util.core.flags.SetFromFlag;
-
-import com.google.common.annotations.Beta;
-
-/** {@link ConfigKey}s used to configure Ansible */
-@Beta
-public interface AnsibleConfig {
-
-    enum AnsibleModes {
-        PLAYBOOK
-    };
-
-    @SetFromFlag("playbook")
-    ConfigKey<String> ANSIBLE_PLAYBOOK = ConfigKeys.newStringConfigKey("brooklyn.ansible.playbook",
-        "Playbook to be execute by Ansible");
-
-    @SetFromFlag("playbook.yaml")
-    ConfigKey<String> ANSIBLE_PLAYBOOK_YAML = ConfigKeys.newStringConfigKey("brooklyn.ansible.playbookYaml",
-        "Playbook to be execute by Ansible");
-
-    @SetFromFlag("playbook.url")
-    ConfigKey<String> ANSIBLE_PLAYBOOK_URL = ConfigKeys.newStringConfigKey("brooklyn.ansible.playbookUrl");
-
-    @SetFromFlag("ansible.service.start")
-    ConfigKey<String> ANSIBLE_SERVICE_START = ConfigKeys.newStringConfigKey("ansible.service.start",
-            "Default start command used with conjunction with the Ansible's service module",
-            "sudo ansible localhost -c local -m service -a \"name=%s state=started\"");
-
-    @SetFromFlag("ansible.service.stop")
-    ConfigKey<String> ANSIBLE_SERVICE_STOP = ConfigKeys.newStringConfigKey("ansible.service.stop",
-            "Default stop command used with conjunction with the Ansible's service module",
-            "sudo ansible localhost -c local -m service -a \"name=%s state=stopped\"");
-
-    @SetFromFlag("ansible.service.checkPort")
-    ConfigKey<Integer> ANSIBLE_SERVICE_CHECK_PORT = ConfigKeys.newIntegerConfigKey("ansible.service.check.port");
-
-    @SetFromFlag("service.name")
-    ConfigKey<String> SERVICE_NAME = ConfigKeys.newStringConfigKey("brooklyn.ansible.serviceName",
-        "Name of OS service this will run as, for use in checking running and stopping");
-
-    @SetFromFlag("ansible.vars")
-    ConfigKey<Object> ANSIBLE_VARS = ConfigKeys.newConfigKey(Object.class, "brooklyn.ansible.vars",
-        "Ansible 'extra-vars' variable configuration values");
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/c1a87838/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntity.java
----------------------------------------------------------------------
diff --git a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntity.java b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntity.java
deleted file mode 100644
index a2c9676..0000000
--- a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntity.java
+++ /dev/null
@@ -1,40 +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.ansible;
-
-import org.apache.brooklyn.api.entity.ImplementedBy;
-import org.apache.brooklyn.core.annotation.Effector;
-import org.apache.brooklyn.core.annotation.EffectorParam;
-import org.apache.brooklyn.core.effector.MethodEffector;
-import org.apache.brooklyn.entity.software.base.SoftwareProcess;
-
-@ImplementedBy(AnsibleEntityImpl.class)
-public interface AnsibleEntity extends SoftwareProcess, AnsibleConfig {
-
-    MethodEffector<String> ANSIBLE_COMMAND = new MethodEffector<>(AnsibleEntity.class, "ansibleCommand");
-
-    @Effector(description = "Invoke an arbitrary Ansible command, optionally specifying the module (default is 'command')")
-    String ansibleCommand(
-        @EffectorParam(name="module", description = "Name of the Ansible module to invoke", defaultValue = "command")
-        String module,
-        @EffectorParam(name="args", description = "Arguments for the ansible command")
-        String args
-    );
-
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/c1a87838/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityImpl.java
----------------------------------------------------------------------
diff --git a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityImpl.java b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityImpl.java
deleted file mode 100644
index c2635ca..0000000
--- a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityImpl.java
+++ /dev/null
@@ -1,61 +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.ansible;
-
-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.apache.brooklyn.util.text.Strings;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-public class AnsibleEntityImpl extends EffectorStartableImpl implements AnsibleEntity {
-
-    private AnsibleLifecycleEffectorTasks lifecycleTasks;
-
-    public void init() {
-        checkNotNull(getConfig(SERVICE_NAME), "service name is missing. it has to be provided by the user");
-        String playbookName = getConfig(ANSIBLE_PLAYBOOK);
-        if (!Strings.isBlank(playbookName)) setDefaultDisplayName(playbookName + " (ansible)");
-
-        super.init();
-
-        lifecycleTasks = new AnsibleLifecycleEffectorTasks();
-
-        lifecycleTasks.attachLifecycleEffectors(this);
-    }
-
-    @Override
-    public void populateServiceNotUpDiagnostics() {
-        // TODO no-op currently; should check ssh'able etc
-    }
-
-    @Override
-    public String ansibleCommand(String module, String args) {
-        final ProcessTaskWrapper<Integer> command = DynamicTasks.queue(
-            AnsiblePlaybookTasks.moduleCommand(module, config().get(ANSIBLE_VARS), lifecycleTasks.getRunDir(), args));
-        command.asTask().blockUntilEnded();
-        if (0 == command.getExitCode()) {
-            return command.getStdout();
-        } else {
-            throw new RuntimeException("Command (" + args + ") in module " + module
-                + " failed with stderr:\n" + command.getStderr() + "\n");
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/c1a87838/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleLifecycleEffectorTasks.java
----------------------------------------------------------------------
diff --git a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleLifecycleEffectorTasks.java b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleLifecycleEffectorTasks.java
deleted file mode 100644
index e1a8622..0000000
--- a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleLifecycleEffectorTasks.java
+++ /dev/null
@@ -1,203 +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.ansible;
-
-import com.google.common.base.Supplier;
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.location.MachineLocation;
-import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks;
-import org.apache.brooklyn.core.entity.Attributes;
-import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
-import org.apache.brooklyn.core.entity.trait.Startable;
-import org.apache.brooklyn.core.location.Locations;
-import org.apache.brooklyn.core.location.Machines;
-import org.apache.brooklyn.entity.software.base.SoftwareProcess;
-import org.apache.brooklyn.entity.software.base.lifecycle.MachineLifecycleEffectorTasks;
-import org.apache.brooklyn.feed.ssh.SshFeed;
-import org.apache.brooklyn.feed.ssh.SshPollConfig;
-import org.apache.brooklyn.location.ssh.SshMachineLocation;
-import org.apache.brooklyn.util.core.task.DynamicTasks;
-import org.apache.brooklyn.util.guava.Maybe;
-import org.apache.brooklyn.util.net.Urls;
-import org.apache.brooklyn.util.text.Strings;
-import org.apache.brooklyn.util.time.Duration;
-import org.apache.brooklyn.util.time.Time;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class AnsibleLifecycleEffectorTasks extends MachineLifecycleEffectorTasks implements AnsibleConfig {
-
-    private static final Logger LOG = LoggerFactory.getLogger(AnsibleLifecycleEffectorTasks.class);
-
-    protected String serviceName;
-    protected SshFeed serviceSshFeed;
-
-    protected Object extraVars;
-    protected String baseDir;
-    protected String runDir;
-
-    public AnsibleLifecycleEffectorTasks() {
-    }
-
-    public String getServiceName() {
-        if (serviceName!=null) return serviceName;
-        return serviceName = entity().config().get(AnsibleConfig.SERVICE_NAME);
-    }
-
-    public Object getExtraVars() {
-        if (extraVars != null) return extraVars;
-        return extraVars = entity().config().get(ANSIBLE_VARS);
-    }
-
-    public String getBaseDir() {
-        if (null != baseDir) return baseDir;
-        return baseDir = MachineLifecycleEffectorTasks.resolveOnBoxDir(entity(),
-                Machines.findUniqueMachineLocation(entity().getLocations(), SshMachineLocation.class).get());
-    }
-
-    public String getRunDir() {
-        if (null != runDir) return runDir;
-        return runDir = Urls.mergePaths(getBaseDir(), "apps/"+entity().getApplicationId()+"/ansible/playbooks/"
-            +entity().getEntityType().getSimpleName()+"_"+entity().getId());
-    }
-
-    @Override
-    public void attachLifecycleEffectors(Entity entity) {
-        if (getServiceName()==null && getClass().equals(AnsibleLifecycleEffectorTasks.class)) {
-            // warn on incorrect usage
-            LOG.warn("Uses of "+getClass()+" must define a PID file or a service name (or subclass and override {start,stop} methods as per javadoc) " +
-                    "in order for check-running and stop to work");
-        }
-        super.attachLifecycleEffectors(entity);
-    }
-
-    @Override
-    protected String startProcessesAtMachine(Supplier<MachineLocation> machineS) {
-        startWithAnsibleAsync();
-
-        return "ansible start tasks submitted";
-    }
-
-    protected String getPlaybookName() {
-        return entity().config().get(ANSIBLE_PLAYBOOK);
-    }
-
-    protected void startWithAnsibleAsync() {
-        String installDir = Urls.mergePaths(getBaseDir(), "installs/ansible");
-
-        String playbookUrl = entity().config().get(ANSIBLE_PLAYBOOK_URL);
-        String playbookYaml = entity().config().get(ANSIBLE_PLAYBOOK_YAML);
-
-        if (Strings.isNonBlank(playbookUrl) && Strings.isNonBlank(playbookYaml)) {
-            throw new IllegalArgumentException("You can specify " +  AnsibleConfig.ANSIBLE_PLAYBOOK_URL.getName()
-                +  " or " + AnsibleConfig.ANSIBLE_PLAYBOOK_YAML.getName() + " but not both of them!");
-        }
-
-        DynamicTasks.queue(AnsiblePlaybookTasks.installAnsible(installDir, false));
-
-        if (getExtraVars() != null) {
-            DynamicTasks.queue(AnsiblePlaybookTasks.configureExtraVars(getRunDir(), extraVars, false));
-        }
-
-        if (Strings.isNonBlank(playbookUrl)) {
-            DynamicTasks.queue(AnsiblePlaybookTasks.installPlaybook(getRunDir(), getPlaybookName(), playbookUrl));
-        }
-
-        if (Strings.isNonBlank(playbookYaml)) {
-            DynamicTasks.queue(AnsiblePlaybookTasks.buildPlaybookFile(getRunDir(), getPlaybookName()));
-        }
-        DynamicTasks.queue(AnsiblePlaybookTasks.runAnsible(getRunDir(), getExtraVars(), getPlaybookName()));
-    }
-
-
-    protected void postStartCustom() {
-        boolean result = false;
-        result |= tryCheckStartService();
-
-        if (!result) {
-            LOG.warn("No way to check whether "+entity()+" is running; assuming yes");
-        }
-        entity().sensors().set(SoftwareProcess.SERVICE_UP, true);
-        
-        Maybe<SshMachineLocation> machine = Locations.findUniqueSshMachineLocation(entity().getLocations());
-
-        if (machine.isPresent()) {
-            
-            String serviceName = String.format("[%s]%s", entity().config().get(SERVICE_NAME).substring(0, 1),
-                    entity().config().get(SERVICE_NAME).substring(1));
-            String checkCmd = String.format("ps -ef | grep %s", serviceName);
-
-            Integer serviceCheckPort = entity().config().get(ANSIBLE_SERVICE_CHECK_PORT);
-
-            if (serviceCheckPort != null) {
-                checkCmd = String.format("sudo ansible localhost -c local -m wait_for -a \"host=0.0.0.0 port=%d\"", serviceCheckPort);
-            }
-            serviceSshFeed = SshFeed.builder()
-                    .entity(entity())
-                    .period(Duration.ONE_MINUTE)
-                    .machine(machine.get())
-                    .poll(new SshPollConfig<Boolean>(Startable.SERVICE_UP)
-                            .command(checkCmd)
-                            .setOnSuccess(true)
-                            .setOnFailureOrException(false))
-                    .build();
-                    
-             entity().feeds().addFeed(serviceSshFeed);
-        } else {
-            LOG.warn("Location(s) {} not an ssh-machine location, so not polling for status; setting serviceUp immediately", entity().getLocations());
-        }
-    }
-
-    protected boolean tryCheckStartService() {
-        if (getServiceName()==null) return false;
-
-        // if it's still up after 5s assume we are good (default behaviour)
-        Time.sleep(Duration.FIVE_SECONDS);
-        if (!((Integer)0).equals(DynamicTasks.queue(SshEffectorTasks.ssh(String.format(entity().config().get(AnsibleConfig.ANSIBLE_SERVICE_START), getServiceName()))).get())) {
-            throw new IllegalStateException("The process for "+entity()+" appears not to be running (service "+getServiceName()+")");
-        }
-
-        return true;
-    }
-
-    @Override
-    protected String stopProcessesAtMachine() {
-        boolean result = false;
-        result |= tryStopService();
-        if (!result) {
-            throw new IllegalStateException("The process for "+entity()+" could not be stopped (no impl!)");
-        }
-        return "stopped";
-    }
-
-    @Override
-    protected StopMachineDetails<Integer> stopAnyProvisionedMachines() {
-        return super.stopAnyProvisionedMachines();
-    }
-
-    protected boolean tryStopService() {
-        if (getServiceName()==null) return false;
-        int result = DynamicTasks.queue(SshEffectorTasks.ssh(String.format(entity().config().get(AnsibleConfig.ANSIBLE_SERVICE_STOP), getServiceName()))).get();
-        if (0 == result) return true;
-        if (entity().getAttribute(Attributes.SERVICE_STATE_ACTUAL) != Lifecycle.RUNNING)
-            return true;
-        
-        throw new IllegalStateException("The process for "+entity()+" appears could not be stopped (exit code "+result+" to service stop)");
-    }
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/c1a87838/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java
----------------------------------------------------------------------
diff --git a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java
deleted file mode 100644
index 36076a1..0000000
--- a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java
+++ /dev/null
@@ -1,109 +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.ansible;
-
-import org.apache.brooklyn.api.entity.Entity;
-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.util.core.ResourceUtils;
-import org.apache.brooklyn.util.core.task.Tasks;
-import org.apache.brooklyn.util.core.task.system.ProcessTaskFactory;
-import org.apache.brooklyn.util.net.Urls;
-import org.apache.brooklyn.util.ssh.BashCommands;
-import org.apache.brooklyn.util.text.Strings;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.yaml.snakeyaml.DumperOptions;
-import org.yaml.snakeyaml.Yaml;
-
-import static org.apache.brooklyn.util.ssh.BashCommands.sudo;
-
-public class AnsiblePlaybookTasks {
-    private static final Logger LOG = LoggerFactory.getLogger(AnsiblePlaybookTasks.class);
-    private static final String EXTRA_VARS_FILENAME = "extra_vars.yaml";
-
-    public static TaskFactory<?> installAnsible(String ansibleDirectory, boolean force) {
-        String installCmd = cdAndRun(ansibleDirectory, AnsibleBashCommands.INSTALL_ANSIBLE);
-        if (!force) installCmd = BashCommands.alternatives("which ansible", installCmd);
-        return SshEffectorTasks.ssh(installCmd).summary("install ansible");
-    }
-
-    public static TaskFactory<?> installPlaybook(final String ansibleDirectory, final String playbookName, final String playbookUrl) {
-        return Tasks.sequential("build ansible playbook file for "+playbookName,
-                SshEffectorTasks.put(ansibleDirectory + "/" + playbookName + ".yaml")
-                    .contents(ResourceUtils.create().getResourceFromUrl(playbookUrl))
-                    .createDirectory());
-    }
-    
-    protected static String cdAndRun(String targetDirectory, String command) {
-        return BashCommands.chain("mkdir -p "+targetDirectory,
-                "cd "+targetDirectory,
-                command);
-    }
-
-    public static TaskFactory<?> buildPlaybookFile(final String ansibleDirectory, String playbook) {
-        Entity entity = EffectorTasks.findEntity();
-        String yaml = entity.config().get(AnsibleConfig.ANSIBLE_PLAYBOOK_YAML);
-
-        return Tasks.sequential("build ansible playbook file for "+ playbook,
-            SshEffectorTasks.put(Urls.mergePaths(ansibleDirectory) + "/" + playbook + ".yaml")
-                .contents(yaml).createDirectory());
-    }
-
-    public static TaskFactory<?> runAnsible(final String dir, Object extraVars, String playbookName) {
-        String cmd = String.format("sudo ansible-playbook -i \"localhost,\" -c local "
-            + optionalExtraVarsParameter(extraVars)
-            + " -s %s.yaml", playbookName);
-
-        if (LOG.isDebugEnabled()) {
-            LOG.debug("Ansible command: {}", cmd);
-        }
-
-        return SshEffectorTasks.ssh(cdAndRun(dir, cmd)).
-                summary("run ansible playbook for " + playbookName).requiringExitCodeZero();
-    }
-
-    public static ProcessTaskFactory<Integer> moduleCommand(String module, Object extraVars, String root, String args) {
-        final String command = "ansible localhost "
-            + optionalExtraVarsParameter(extraVars)
-            + " -m '" + module + "' -a '" + args + "'";
-        return SshEffectorTasks.ssh(sudo(BashCommands.chain("cd " + root, command)))
-            .summary("ad-hoc: " + command).requiringExitCodeZero();
-    }
-
-    public static TaskFactory<?> configureExtraVars(String dir, Object extraVars, boolean force) {
-        DumperOptions options = new DumperOptions();
-        options.setDefaultScalarStyle(DumperOptions.ScalarStyle.DOUBLE_QUOTED);
-        Yaml asYaml = new Yaml(options);
-        final String varsYaml = asYaml.dump(extraVars);
-        return SshEffectorTasks.put(Urls.mergePaths(dir, EXTRA_VARS_FILENAME))
-            .contents(varsYaml)
-            .summary("install extra vars")
-            .createDirectory();
-    }
-
-    private static String optionalExtraVarsParameter(Object extraVars) {
-        if (null == extraVars || Strings.isBlank(extraVars.toString())) {
-            return "";
-        }
-        return " --extra-vars \"@" + EXTRA_VARS_FILENAME + "\" ";
-    }
-}
-

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/c1a87838/software/cm/src/test/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/cm/src/test/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityIntegrationTest.java b/software/cm/src/test/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityIntegrationTest.java
deleted file mode 100644
index 76e993d..0000000
--- a/software/cm/src/test/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityIntegrationTest.java
+++ /dev/null
@@ -1,116 +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.ansible;
-
-import org.apache.brooklyn.api.entity.EntitySpec;
-import org.apache.brooklyn.api.location.MachineDetails;
-import org.apache.brooklyn.api.location.OsDetails;
-import org.apache.brooklyn.api.mgmt.Task;
-import org.apache.brooklyn.core.entity.Entities;
-import org.apache.brooklyn.core.entity.EntityAsserts;
-import org.apache.brooklyn.core.entity.trait.Startable;
-import org.apache.brooklyn.core.location.BasicMachineDetails;
-import org.apache.brooklyn.core.test.entity.TestApplication;
-
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation;
-import org.apache.brooklyn.location.ssh.SshMachineLocation;
-
-import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableList;
-
-public class AnsibleEntityIntegrationTest {
-    protected TestApplication app;
-    protected LocalhostMachineProvisioningLocation testLocation;
-    protected AnsibleEntity ansible;
-    protected SshMachineLocation sshHost;
-
-    String playbookYaml = Joiner.on("\n").join(
-            "---",
-            "- hosts: localhost",
-            "  sudo: True",
-            "",
-            "  tasks:",
-            "  - apt: name=apache2 state=latest",
-            "    when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu'",
-            "",
-            "  - yum: name=httpd state=latest",
-            "    when: ansible_distribution == 'CentOS' or ansible_distribution == 'Red Hat Enterprise Linux'",
-            "",
-            "  - service: name=apache2 state=started enabled=no",
-            "    when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu'",
-            "",
-            "  - service: name=httpd state=started enabled=no",
-            "    when: ansible_distribution == 'CentOS' or ansible_distribution == 'Red Hat Enterprise Linux'");
-
-    @BeforeMethod(alwaysRun = true)
-    public void setup() throws Exception {
-        app = TestApplication.Factory.newManagedInstanceForTests();;
-        testLocation = new LocalhostMachineProvisioningLocation();
-        sshHost = testLocation.obtain();
-    }
-
-    @AfterMethod(alwaysRun = true)
-    public void shutdown() {
-        Entities.destroyAll(app.getManagementContext());
-    }
-    
-    @Test(groups = {"Integration"})
-    public void testAnsible() {
-        Task<BasicMachineDetails> detailsTask = app.getExecutionContext().submit(
-                BasicMachineDetails.taskForSshMachineLocation(sshHost));
-        MachineDetails machine = detailsTask.getUnchecked();
-
-
-        OsDetails details = machine.getOsDetails();
-        
-        String osName = details.getName();
-        String playbookServiceName = getPlaybookServiceName(osName);
-        ansible = app.createAndManageChild(EntitySpec.create(AnsibleEntity.class)
-                .configure("playbook.yaml", playbookYaml)
-                .configure("playbook", playbookServiceName)
-                .configure("service.name", playbookServiceName));
-
-        app.start(ImmutableList.of(testLocation));
-        EntityAsserts.assertAttributeEqualsEventually(ansible, Startable.SERVICE_UP, true);
-
-        ansible.stop();
-        EntityAsserts.assertAttributeEqualsEventually(ansible, Startable.SERVICE_UP, false);
-    }
-
-    private String getPlaybookServiceName(String os) {
-        String name;
-
-        switch (os.toLowerCase()) {
-            case "fedora":
-                name = "httpd";
-                break;
-            case "centos":
-                name = "httpd";
-                break;
-            default:
-                name = "apache2";
-        }
-        return name;
-    }
-}
-
-


[3/7] brooklyn-library git commit: Initial Ansible support

Posted by he...@apache.org.
Initial Ansible support

- Ansible install for Ubuntu and CentOS
- Playbooks can be specified via URL or directly as a YAML
- Support for custom start/stop commands
- Support for command to support setting a useful value for the
service.isUp sensor
- Generic check for a running service based on the provided serviceName


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

Branch: refs/heads/master
Commit: 373c598afe05a81adbc317088b166b734a04472a
Parents: 9fdf1db
Author: Yavor Yanchev <ya...@yanchev.com>
Authored: Fri Feb 5 10:05:41 2016 +0200
Committer: Yavor Yanchev <ya...@yanchev.com>
Committed: Wed Feb 17 11:43:03 2016 +0200

----------------------------------------------------------------------
 software/cm/pom.xml                             |  51 ++++++
 .../entity/cm/ansible/AnsibleBashCommands.java  |  38 ++++
 .../entity/cm/ansible/AnsibleConfig.java        |  62 +++++++
 .../entity/cm/ansible/AnsibleEntity.java        |  26 +++
 .../entity/cm/ansible/AnsibleEntityImpl.java    |  41 +++++
 .../ansible/AnsibleLifecycleEffectorTasks.java  | 180 +++++++++++++++++++
 .../entity/cm/ansible/AnsiblePlaybookTasks.java |  72 ++++++++
 7 files changed, 470 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/373c598a/software/cm/pom.xml
----------------------------------------------------------------------
diff --git a/software/cm/pom.xml b/software/cm/pom.xml
index 90af5b4..ea17774 100644
--- a/software/cm/pom.xml
+++ b/software/cm/pom.xml
@@ -71,6 +71,57 @@
     </mailingLists>
 
 
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.brooklyn</groupId>
+      <artifactId>brooklyn-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.brooklyn</groupId>
+      <artifactId>brooklyn-software-base</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.brooklyn</groupId>
+      <artifactId>brooklyn-software-database</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+         <groupId>org.apache.brooklyn</groupId>
+         <artifactId>brooklyn-camp</artifactId>
+        <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.brooklyn</groupId>
+      <artifactId>brooklyn-test-support</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.brooklyn</groupId>
+      <artifactId>brooklyn-core</artifactId>
+      <version>${project.version}</version>
+      <classifier>tests</classifier>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.brooklyn</groupId>
+      <artifactId>brooklyn-software-base</artifactId>
+      <version>${project.version}</version>
+      <classifier>tests</classifier>
+      <scope>test</scope>
+    </dependency>
+
+      <dependency>
+          <groupId>org.assertj</groupId>
+          <artifactId>assertj-core</artifactId>
+          <version>${assertj.version}</version>
+          <scope>test</scope>
+      </dependency>
+  </dependencies>
+
     <modules>
 
         <module>salt</module>

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/373c598a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleBashCommands.java
----------------------------------------------------------------------
diff --git a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleBashCommands.java b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleBashCommands.java
new file mode 100644
index 0000000..e22ee34
--- /dev/null
+++ b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleBashCommands.java
@@ -0,0 +1,38 @@
+/*
+ * 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.ansible;
+
+import static org.apache.brooklyn.util.ssh.BashCommands.INSTALL_CURL;
+import static org.apache.brooklyn.util.ssh.BashCommands.INSTALL_TAR;
+import static org.apache.brooklyn.util.ssh.BashCommands.INSTALL_UNZIP;
+import static org.apache.brooklyn.util.ssh.BashCommands.installExecutable;
+import static org.apache.brooklyn.util.ssh.BashCommands.sudo;
+
+import org.apache.brooklyn.util.ssh.BashCommands;
+
+public class AnsibleBashCommands {
+
+    public static final String INSTALL_ANSIBLE =
+            BashCommands.chain(
+                    sudo("apt-add-repository -y ppa:ansible/ansible"),
+                    INSTALL_CURL,
+                    INSTALL_TAR,
+                    INSTALL_UNZIP,
+                    installExecutable("ansible"));
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/373c598a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleConfig.java
----------------------------------------------------------------------
diff --git a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleConfig.java b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleConfig.java
new file mode 100644
index 0000000..b201294
--- /dev/null
+++ b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleConfig.java
@@ -0,0 +1,62 @@
+/*
+ * 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.ansible;
+
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.util.core.flags.SetFromFlag;
+
+import com.google.common.annotations.Beta;
+
+/** {@link ConfigKey}s used to configure Ansible */
+@Beta
+public interface AnsibleConfig {
+
+    public static enum AnsibleModes {
+        PLAYBOOK
+    };
+
+    @SetFromFlag("playbook")
+    public static final ConfigKey<String> ANSIBLE_PLAYBOOK = ConfigKeys.newStringConfigKey("brooklyn.ansible.playbook",
+        "Playbook to be execute by Ansible");
+
+    @SetFromFlag("playbook.yaml")
+    public static final ConfigKey<String> ANSIBLE_PLAYBOOK_YAML = ConfigKeys.newStringConfigKey("brooklyn.ansible.playbookYaml",
+        "Playbook to be execute by Ansible");
+
+    @SetFromFlag("playbook.url")
+    public static final ConfigKey<String> ANSIBLE_PLAYBOOK_URL = ConfigKeys.newStringConfigKey("brooklyn.ansible.playbookUrl");
+
+    @SetFromFlag("ansible.service.start")
+    public static final ConfigKey<String> ANSIBLE_SERVICE_START = ConfigKeys.newStringConfigKey("ansible.service.start",
+            "Default start command used with conjunction with the Ansible's service module",
+            "sudo ansible localhost -c local -m service -a \"name=%s state=started\"");
+
+    @SetFromFlag("ansible.service.stop")
+    public static final ConfigKey<String> ANSIBLE_SERVICE_STOP = ConfigKeys.newStringConfigKey("ansible.service.stop",
+            "Default stop command used with conjunction with the Ansible's service module",
+            "sudo ansible localhost -c local -m service -a \"name=%s state=stopped\"");
+
+    @SetFromFlag("ansible.service.checkPort")
+    public static final ConfigKey<Integer> ANSIBLE_SERVICE_CHECK_PORT = ConfigKeys.newIntegerConfigKey("ansible.service.check.port");
+
+    @SetFromFlag("service.name")
+    public static final ConfigKey<String> SERVICE_NAME = ConfigKeys.newStringConfigKey("brooklyn.ansible.serviceName",
+        "Name of OS service this will run as, for use in checking running and stopping");
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/373c598a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntity.java
----------------------------------------------------------------------
diff --git a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntity.java b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntity.java
new file mode 100644
index 0000000..0124854
--- /dev/null
+++ b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntity.java
@@ -0,0 +1,26 @@
+/*
+ * 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.ansible;
+
+import org.apache.brooklyn.api.entity.ImplementedBy;
+import org.apache.brooklyn.entity.software.base.SoftwareProcess;
+
+@ImplementedBy(AnsibleEntityImpl.class)
+public interface AnsibleEntity extends SoftwareProcess, AnsibleConfig {
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/373c598a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityImpl.java
----------------------------------------------------------------------
diff --git a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityImpl.java b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityImpl.java
new file mode 100644
index 0000000..bd797e6
--- /dev/null
+++ b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityImpl.java
@@ -0,0 +1,41 @@
+/*
+ * 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.ansible;
+
+import org.apache.brooklyn.entity.stock.EffectorStartableImpl;
+import org.apache.brooklyn.util.text.Strings;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class AnsibleEntityImpl extends EffectorStartableImpl implements AnsibleEntity {
+
+    public void init() {
+        checkNotNull(getConfig(SERVICE_NAME), "service name is missing. it has to be provided by the user");
+        String playbookName = getConfig(ANSIBLE_PLAYBOOK);
+        if (!Strings.isBlank(playbookName)) setDefaultDisplayName(playbookName + " (ansible)");
+
+        super.init();
+        new AnsibleLifecycleEffectorTasks().attachLifecycleEffectors(this);
+    }
+
+    @Override
+    public void populateServiceNotUpDiagnostics() {
+        // TODO no-op currently; should check ssh'able etc
+    }    
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/373c598a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleLifecycleEffectorTasks.java
----------------------------------------------------------------------
diff --git a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleLifecycleEffectorTasks.java b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleLifecycleEffectorTasks.java
new file mode 100644
index 0000000..0262af3
--- /dev/null
+++ b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleLifecycleEffectorTasks.java
@@ -0,0 +1,180 @@
+/*
+ * 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.ansible;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.location.MachineLocation;
+import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks;
+import org.apache.brooklyn.core.entity.Attributes;
+import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
+import org.apache.brooklyn.core.entity.trait.Startable;
+import org.apache.brooklyn.core.location.Locations;
+import org.apache.brooklyn.core.location.Machines;
+import org.apache.brooklyn.entity.software.base.SoftwareProcess;
+import org.apache.brooklyn.entity.software.base.lifecycle.MachineLifecycleEffectorTasks;
+import org.apache.brooklyn.feed.ssh.SshFeed;
+import org.apache.brooklyn.feed.ssh.SshPollConfig;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.brooklyn.util.core.task.DynamicTasks;
+import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.net.Urls;
+import org.apache.brooklyn.util.text.Strings;
+import org.apache.brooklyn.util.time.Duration;
+import org.apache.brooklyn.util.time.Time;
+
+import com.google.common.base.Supplier;
+
+public class AnsibleLifecycleEffectorTasks extends MachineLifecycleEffectorTasks implements AnsibleConfig {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AnsibleLifecycleEffectorTasks.class);
+
+    protected String serviceName;
+    protected SshFeed serviceSshFeed;
+    
+    public AnsibleLifecycleEffectorTasks() {
+    }
+
+    public String getServiceName() {
+        if (serviceName!=null) return serviceName;
+        return serviceName = entity().config().get(AnsibleConfig.SERVICE_NAME);
+    }
+
+    @Override
+    public void attachLifecycleEffectors(Entity entity) {
+        if (getServiceName()==null && getClass().equals(AnsibleLifecycleEffectorTasks.class)) {
+            // warn on incorrect usage
+            LOG.warn("Uses of "+getClass()+" must define a PID file or a service name (or subclass and override {start,stop} methods as per javadoc) " +
+                    "in order for check-running and stop to work");
+        }
+        super.attachLifecycleEffectors(entity);
+    }
+
+    @Override
+    protected String startProcessesAtMachine(Supplier<MachineLocation> machineS) {
+        startWithAnsibleAsync();
+
+        return "ansible start tasks submitted";
+    }
+
+    protected String getPlaybookName() {
+        return entity().config().get(ANSIBLE_PLAYBOOK);
+    }
+
+    protected void startWithAnsibleAsync() {
+        String baseDir = MachineLifecycleEffectorTasks.resolveOnBoxDir(entity(), Machines.findUniqueMachineLocation(entity().getLocations(), SshMachineLocation.class).get());
+        String installDir = Urls.mergePaths(baseDir, "installs/ansible");
+
+        String playbookUrl = entity().config().get(ANSIBLE_PLAYBOOK_URL);
+        String playbookYaml = entity().config().get(ANSIBLE_PLAYBOOK_YAML);
+
+        if (Strings.isNonBlank(playbookUrl) && Strings.isNonBlank(playbookYaml)) {
+            throw new IllegalArgumentException("You can specify " +  AnsibleConfig.ANSIBLE_PLAYBOOK_URL.getName() +  " or " + AnsibleConfig.ANSIBLE_PLAYBOOK_YAML.getName() + " but not both of them!");
+        }
+
+        DynamicTasks.queue(AnsiblePlaybookTasks.installAnsible(installDir, false));
+
+        String runDir = Urls.mergePaths(baseDir, "apps/"+entity().getApplicationId()+"/ansible/playbooks/"+entity().getEntityType().getSimpleName()+"_"+entity().getId());
+        
+        if (Strings.isNonBlank(playbookUrl)) {
+            DynamicTasks.queue(AnsiblePlaybookTasks.installPlaybook(runDir, getPlaybookName(), playbookUrl));
+        }
+
+        if (Strings.isNonBlank(playbookYaml)) {
+            DynamicTasks.queue(AnsiblePlaybookTasks.buildPlaybookFile(runDir, getPlaybookName()));
+        }
+        DynamicTasks.queue(AnsiblePlaybookTasks.runAnsible(runDir, getPlaybookName()));
+    }
+
+    protected void postStartCustom() {
+        boolean result = false;
+        result |= tryCheckStartService();
+
+        if (!result) {
+            LOG.warn("No way to check whether "+entity()+" is running; assuming yes");
+        }
+        entity().sensors().set(SoftwareProcess.SERVICE_UP, true);
+        
+        Maybe<SshMachineLocation> machine = Locations.findUniqueSshMachineLocation(entity().getLocations());
+
+        if (machine.isPresent()) {
+            
+            String serviceName = String.format("[%s]%s", entity().config().get(SERVICE_NAME).substring(0, 1),
+                    entity().config().get(SERVICE_NAME).substring(1));
+            String checkCmd = String.format("ps -ef | grep %s", serviceName);
+
+            Integer serviceCheckPort = entity().config().get(ANSIBLE_SERVICE_CHECK_PORT);
+
+            if (serviceCheckPort != null) {
+                checkCmd = String.format("sudo ansible localhost -c local -m wait_for -a \"host=0.0.0.0 port=%d\"", serviceCheckPort);
+            }
+            serviceSshFeed = SshFeed.builder()
+                    .entity(entity())
+                    .period(Duration.ONE_MINUTE)
+                    .machine(machine.get())
+                    .poll(new SshPollConfig<Boolean>(Startable.SERVICE_UP)
+                            .command(checkCmd)
+                            .setOnSuccess(true)
+                            .setOnFailureOrException(false))
+                    .build();
+                    
+             entity().feeds().addFeed(serviceSshFeed);
+        } else {
+            LOG.warn("Location(s) {} not an ssh-machine location, so not polling for status; setting serviceUp immediately", entity().getLocations());
+        }
+    }
+
+    protected boolean tryCheckStartService() {
+        if (getServiceName()==null) return false;
+
+        // if it's still up after 5s assume we are good (default behaviour)
+        Time.sleep(Duration.FIVE_SECONDS);
+        if (!((Integer)0).equals(DynamicTasks.queue(SshEffectorTasks.ssh(String.format(entity().config().get(AnsibleConfig.ANSIBLE_SERVICE_START), getServiceName()))).get())) {
+            throw new IllegalStateException("The process for "+entity()+" appears not to be running (service "+getServiceName()+")");
+        }
+
+        return true;
+    }
+
+    @Override
+    protected String stopProcessesAtMachine() {
+        boolean result = false;
+        result |= tryStopService();
+        if (!result) {
+            throw new IllegalStateException("The process for "+entity()+" could not be stopped (no impl!)");
+        }
+        return "stopped";
+    }
+
+    @Override
+    protected StopMachineDetails<Integer> stopAnyProvisionedMachines() {
+        return super.stopAnyProvisionedMachines();
+    }
+
+    protected boolean tryStopService() {
+        if (getServiceName()==null) return false;
+        int result = DynamicTasks.queue(SshEffectorTasks.ssh(String.format(entity().config().get(AnsibleConfig.ANSIBLE_SERVICE_STOP), getServiceName()))).get();
+        if (0 == result) return true;
+        if (entity().getAttribute(Attributes.SERVICE_STATE_ACTUAL) != Lifecycle.RUNNING)
+            return true;
+        
+        throw new IllegalStateException("The process for "+entity()+" appears could not be stopped (exit code "+result+" to service stop)");
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/373c598a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java
----------------------------------------------------------------------
diff --git a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java
new file mode 100644
index 0000000..1579e2d
--- /dev/null
+++ b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java
@@ -0,0 +1,72 @@
+/*
+ * 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.ansible;
+
+import org.apache.brooklyn.api.entity.Entity;
+
+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.util.core.ResourceUtils;
+import org.apache.brooklyn.util.core.task.Tasks;
+import org.apache.brooklyn.util.net.Urls;
+import org.apache.brooklyn.util.ssh.BashCommands;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AnsiblePlaybookTasks {
+    private static final Logger LOG = LoggerFactory.getLogger(AnsiblePlaybookTasks.class);
+
+    public static TaskFactory<?> installAnsible(String ansibleDirectory, boolean force) {
+        String installCmd = cdAndRun(ansibleDirectory, AnsibleBashCommands.INSTALL_ANSIBLE);
+        if (!force) installCmd = BashCommands.alternatives("which ansible", installCmd);
+        return SshEffectorTasks.ssh(installCmd).summary("install ansible");
+    }
+
+    public static TaskFactory<?> installPlaybook(final String ansibleDirectory, final String playbookName, final String playbookUrl) {
+        return Tasks.sequential("build ansible playbook file for "+playbookName,
+                SshEffectorTasks.put(ansibleDirectory + "/" + playbookName + ".yaml").contents(ResourceUtils.create().getResourceFromUrl(playbookUrl)).createDirectory());
+    }
+    
+    protected static String cdAndRun(String targetDirectory, String command) {
+        return BashCommands.chain("mkdir -p "+targetDirectory,
+                "cd "+targetDirectory,
+                command);
+    }
+
+    public static TaskFactory<?> buildPlaybookFile(final String ansibleDirectory, String playbook) {
+        Entity entity = EffectorTasks.findEntity();
+        String yaml = entity.config().get(AnsibleConfig.ANSIBLE_PLAYBOOK_YAML);
+
+        return Tasks.sequential("build ansible playbook file for "+ playbook,
+                    SshEffectorTasks.put(Urls.mergePaths(ansibleDirectory) + "/" + playbook + ".yaml").contents(yaml).createDirectory());
+    }
+
+    public static TaskFactory<?> runAnsible(final String ansibleDirectory, String playbookName) {
+        String cmd = String.format("sudo ansible-playbook -i \"localhost,\" -c local -s %s.yaml", playbookName);
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Ansible command: {}", cmd);
+        }
+
+        return SshEffectorTasks.ssh(cdAndRun(ansibleDirectory, cmd)).
+                summary("run ansible playbook for " + playbookName).requiringExitCodeZero();
+    }
+    
+}


[5/7] brooklyn-library git commit: Add effector for ad-hoc ansible commands.

Posted by he...@apache.org.
Add effector for ad-hoc ansible commands.

For example (using the command line):

    br app utest ent apc effector ansibleCommand invoke -P module=shell -P args='curl http://myhost:8080/additional.html > /var/www/html/additional.html'

This invokes the ad-hoc ansible command to add a file to an Apache documents folder.


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

Branch: refs/heads/master
Commit: 8de0e9fdf1c894da6c949ccfd75c77e9d24badf4
Parents: d5026df
Author: Geoff Macartney <ge...@cloudsoftcorp.com>
Authored: Tue Feb 9 17:28:44 2016 +0000
Committer: Yavor Yanchev <ya...@yanchev.com>
Committed: Wed Feb 17 11:43:03 2016 +0200

----------------------------------------------------------------------
 .../brooklyn/entity/cm/ansible/AnsibleEntity.java   | 14 ++++++++++++++
 .../entity/cm/ansible/AnsibleEntityImpl.java        | 16 +++++++++++++++-
 .../entity/cm/ansible/AnsiblePlaybookTasks.java     | 14 ++++++++++++--
 3 files changed, 41 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/8de0e9fd/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntity.java
----------------------------------------------------------------------
diff --git a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntity.java b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntity.java
index 0124854..a2c9676 100644
--- a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntity.java
+++ b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntity.java
@@ -19,8 +19,22 @@
 package org.apache.brooklyn.entity.cm.ansible;
 
 import org.apache.brooklyn.api.entity.ImplementedBy;
+import org.apache.brooklyn.core.annotation.Effector;
+import org.apache.brooklyn.core.annotation.EffectorParam;
+import org.apache.brooklyn.core.effector.MethodEffector;
 import org.apache.brooklyn.entity.software.base.SoftwareProcess;
 
 @ImplementedBy(AnsibleEntityImpl.class)
 public interface AnsibleEntity extends SoftwareProcess, AnsibleConfig {
+
+    MethodEffector<String> ANSIBLE_COMMAND = new MethodEffector<>(AnsibleEntity.class, "ansibleCommand");
+
+    @Effector(description = "Invoke an arbitrary Ansible command, optionally specifying the module (default is 'command')")
+    String ansibleCommand(
+        @EffectorParam(name="module", description = "Name of the Ansible module to invoke", defaultValue = "command")
+        String module,
+        @EffectorParam(name="args", description = "Arguments for the ansible command")
+        String args
+    );
+
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/8de0e9fd/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityImpl.java
----------------------------------------------------------------------
diff --git a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityImpl.java b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityImpl.java
index bd797e6..b34603e 100644
--- a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityImpl.java
+++ b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityImpl.java
@@ -19,6 +19,8 @@
 package org.apache.brooklyn.entity.cm.ansible;
 
 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.apache.brooklyn.util.text.Strings;
 
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -37,5 +39,17 @@ public class AnsibleEntityImpl extends EffectorStartableImpl implements AnsibleE
     @Override
     public void populateServiceNotUpDiagnostics() {
         // TODO no-op currently; should check ssh'able etc
-    }    
+    }
+
+    @Override
+    public String ansibleCommand(String module, String args) {
+        final ProcessTaskWrapper<Integer> command = DynamicTasks.queue(AnsiblePlaybookTasks.moduleCommand(module, args));
+                command.asTask().blockUntilEnded();
+                if (0 == command.getExitCode()) {
+                        return command.getStdout();
+                    } else {
+                        throw new RuntimeException("Command (" + args + ") in module " + module
+                            +  " failed with stderr:\n" + command.getStderr() + "\n");
+                    }
+    }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/8de0e9fd/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java
----------------------------------------------------------------------
diff --git a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java
index 1579e2d..29924d4 100644
--- a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java
+++ b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java
@@ -19,17 +19,21 @@
 package org.apache.brooklyn.entity.cm.ansible;
 
 import org.apache.brooklyn.api.entity.Entity;
-
 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.util.core.ResourceUtils;
 import org.apache.brooklyn.util.core.task.Tasks;
+import org.apache.brooklyn.util.core.task.system.ProcessTaskFactory;
 import org.apache.brooklyn.util.net.Urls;
 import org.apache.brooklyn.util.ssh.BashCommands;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.Map;
+
+import static org.apache.brooklyn.util.ssh.BashCommands.sudo;
+
 public class AnsiblePlaybookTasks {
     private static final Logger LOG = LoggerFactory.getLogger(AnsiblePlaybookTasks.class);
 
@@ -68,5 +72,11 @@ public class AnsiblePlaybookTasks {
         return SshEffectorTasks.ssh(cdAndRun(ansibleDirectory, cmd)).
                 summary("run ansible playbook for " + playbookName).requiringExitCodeZero();
     }
-    
+
+    public static ProcessTaskFactory<Integer> moduleCommand(String module, String args) {
+        final String command = "ansible localhost -m '" + module + "' -a '" + args + "'";
+        return SshEffectorTasks.ssh(sudo(command))
+            .summary("ad-hoc: " + command).requiringExitCodeZero();
+    }
 }
+


[7/7] brooklyn-library git commit: This closes #5

Posted by he...@apache.org.
This closes #5


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

Branch: refs/heads/master
Commit: b8395ff302a805fef161ea1079794ebaa9cd1810
Parents: 9fdf1db a01cba0
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Fri Feb 19 10:07:28 2016 +0000
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri Feb 19 10:07:28 2016 +0000

----------------------------------------------------------------------
 software/cm/ansible/pom.xml                     |  84 +++++++
 .../entity/cm/ansible/AnsibleBashCommands.java  |  39 ++++
 .../entity/cm/ansible/AnsibleConfig.java        |  70 ++++++
 .../entity/cm/ansible/AnsibleEntity.java        |  40 ++++
 .../entity/cm/ansible/AnsibleEntityImpl.java    |  59 +++++
 .../ansible/AnsibleLifecycleEffectorTasks.java  | 227 +++++++++++++++++++
 .../entity/cm/ansible/AnsiblePlaybookTasks.java | 109 +++++++++
 .../ansible/AnsibleEntityIntegrationTest.java   | 116 ++++++++++
 software/cm/pom.xml                             |   2 +-
 9 files changed, 745 insertions(+), 1 deletion(-)
----------------------------------------------------------------------



[2/7] brooklyn-library git commit: Ansible Integration test

Posted by he...@apache.org.
Ansible Integration test

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

Branch: refs/heads/master
Commit: d5026df1da2df5bb9d375a82babfed5d1f1b0c3f
Parents: 373c598
Author: Yavor Yanchev <ya...@yanchev.com>
Authored: Thu Feb 11 12:14:10 2016 +0200
Committer: Yavor Yanchev <ya...@yanchev.com>
Committed: Wed Feb 17 11:43:03 2016 +0200

----------------------------------------------------------------------
 .../ansible/AnsibleEntityIntegrationTest.java   | 116 +++++++++++++++++++
 1 file changed, 116 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/d5026df1/software/cm/src/test/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/cm/src/test/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityIntegrationTest.java b/software/cm/src/test/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityIntegrationTest.java
new file mode 100644
index 0000000..76e993d
--- /dev/null
+++ b/software/cm/src/test/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityIntegrationTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.ansible;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.MachineDetails;
+import org.apache.brooklyn.api.location.OsDetails;
+import org.apache.brooklyn.api.mgmt.Task;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.EntityAsserts;
+import org.apache.brooklyn.core.entity.trait.Startable;
+import org.apache.brooklyn.core.location.BasicMachineDetails;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+
+public class AnsibleEntityIntegrationTest {
+    protected TestApplication app;
+    protected LocalhostMachineProvisioningLocation testLocation;
+    protected AnsibleEntity ansible;
+    protected SshMachineLocation sshHost;
+
+    String playbookYaml = Joiner.on("\n").join(
+            "---",
+            "- hosts: localhost",
+            "  sudo: True",
+            "",
+            "  tasks:",
+            "  - apt: name=apache2 state=latest",
+            "    when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu'",
+            "",
+            "  - yum: name=httpd state=latest",
+            "    when: ansible_distribution == 'CentOS' or ansible_distribution == 'Red Hat Enterprise Linux'",
+            "",
+            "  - service: name=apache2 state=started enabled=no",
+            "    when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu'",
+            "",
+            "  - service: name=httpd state=started enabled=no",
+            "    when: ansible_distribution == 'CentOS' or ansible_distribution == 'Red Hat Enterprise Linux'");
+
+    @BeforeMethod(alwaysRun = true)
+    public void setup() throws Exception {
+        app = TestApplication.Factory.newManagedInstanceForTests();;
+        testLocation = new LocalhostMachineProvisioningLocation();
+        sshHost = testLocation.obtain();
+    }
+
+    @AfterMethod(alwaysRun = true)
+    public void shutdown() {
+        Entities.destroyAll(app.getManagementContext());
+    }
+    
+    @Test(groups = {"Integration"})
+    public void testAnsible() {
+        Task<BasicMachineDetails> detailsTask = app.getExecutionContext().submit(
+                BasicMachineDetails.taskForSshMachineLocation(sshHost));
+        MachineDetails machine = detailsTask.getUnchecked();
+
+
+        OsDetails details = machine.getOsDetails();
+        
+        String osName = details.getName();
+        String playbookServiceName = getPlaybookServiceName(osName);
+        ansible = app.createAndManageChild(EntitySpec.create(AnsibleEntity.class)
+                .configure("playbook.yaml", playbookYaml)
+                .configure("playbook", playbookServiceName)
+                .configure("service.name", playbookServiceName));
+
+        app.start(ImmutableList.of(testLocation));
+        EntityAsserts.assertAttributeEqualsEventually(ansible, Startable.SERVICE_UP, true);
+
+        ansible.stop();
+        EntityAsserts.assertAttributeEqualsEventually(ansible, Startable.SERVICE_UP, false);
+    }
+
+    private String getPlaybookServiceName(String os) {
+        String name;
+
+        switch (os.toLowerCase()) {
+            case "fedora":
+                name = "httpd";
+                break;
+            case "centos":
+                name = "httpd";
+                break;
+            default:
+                name = "apache2";
+        }
+        return name;
+    }
+}
+
+


[4/7] brooklyn-library git commit: Addition of Ansible extra vars from Brooklyn config.

Posted by he...@apache.org.
Addition of Ansible extra vars from Brooklyn config.


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

Branch: refs/heads/master
Commit: 121db3252a606aebdd9fc7a038f9920f082fdd8a
Parents: 8de0e9f
Author: Geoff Macartney <ge...@cloudsoftcorp.com>
Authored: Wed Feb 10 14:44:54 2016 +0000
Committer: Yavor Yanchev <ya...@yanchev.com>
Committed: Wed Feb 17 11:43:03 2016 +0200

----------------------------------------------------------------------
 .../entity/cm/ansible/AnsibleConfig.java        | 20 ++++----
 .../entity/cm/ansible/AnsibleEntityImpl.java    | 24 ++++++----
 .../ansible/AnsibleLifecycleEffectorTasks.java  | 49 ++++++++++++++------
 .../entity/cm/ansible/AnsiblePlaybookTasks.java | 47 +++++++++++++++----
 4 files changed, 100 insertions(+), 40 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/121db325/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleConfig.java
----------------------------------------------------------------------
diff --git a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleConfig.java b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleConfig.java
index b201294..d63c2c6 100644
--- a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleConfig.java
+++ b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleConfig.java
@@ -28,35 +28,39 @@ import com.google.common.annotations.Beta;
 @Beta
 public interface AnsibleConfig {
 
-    public static enum AnsibleModes {
+    enum AnsibleModes {
         PLAYBOOK
     };
 
     @SetFromFlag("playbook")
-    public static final ConfigKey<String> ANSIBLE_PLAYBOOK = ConfigKeys.newStringConfigKey("brooklyn.ansible.playbook",
+    ConfigKey<String> ANSIBLE_PLAYBOOK = ConfigKeys.newStringConfigKey("brooklyn.ansible.playbook",
         "Playbook to be execute by Ansible");
 
     @SetFromFlag("playbook.yaml")
-    public static final ConfigKey<String> ANSIBLE_PLAYBOOK_YAML = ConfigKeys.newStringConfigKey("brooklyn.ansible.playbookYaml",
+    ConfigKey<String> ANSIBLE_PLAYBOOK_YAML = ConfigKeys.newStringConfigKey("brooklyn.ansible.playbookYaml",
         "Playbook to be execute by Ansible");
 
     @SetFromFlag("playbook.url")
-    public static final ConfigKey<String> ANSIBLE_PLAYBOOK_URL = ConfigKeys.newStringConfigKey("brooklyn.ansible.playbookUrl");
+    ConfigKey<String> ANSIBLE_PLAYBOOK_URL = ConfigKeys.newStringConfigKey("brooklyn.ansible.playbookUrl");
 
     @SetFromFlag("ansible.service.start")
-    public static final ConfigKey<String> ANSIBLE_SERVICE_START = ConfigKeys.newStringConfigKey("ansible.service.start",
+    ConfigKey<String> ANSIBLE_SERVICE_START = ConfigKeys.newStringConfigKey("ansible.service.start",
             "Default start command used with conjunction with the Ansible's service module",
             "sudo ansible localhost -c local -m service -a \"name=%s state=started\"");
 
     @SetFromFlag("ansible.service.stop")
-    public static final ConfigKey<String> ANSIBLE_SERVICE_STOP = ConfigKeys.newStringConfigKey("ansible.service.stop",
+    ConfigKey<String> ANSIBLE_SERVICE_STOP = ConfigKeys.newStringConfigKey("ansible.service.stop",
             "Default stop command used with conjunction with the Ansible's service module",
             "sudo ansible localhost -c local -m service -a \"name=%s state=stopped\"");
 
     @SetFromFlag("ansible.service.checkPort")
-    public static final ConfigKey<Integer> ANSIBLE_SERVICE_CHECK_PORT = ConfigKeys.newIntegerConfigKey("ansible.service.check.port");
+    ConfigKey<Integer> ANSIBLE_SERVICE_CHECK_PORT = ConfigKeys.newIntegerConfigKey("ansible.service.check.port");
 
     @SetFromFlag("service.name")
-    public static final ConfigKey<String> SERVICE_NAME = ConfigKeys.newStringConfigKey("brooklyn.ansible.serviceName",
+    ConfigKey<String> SERVICE_NAME = ConfigKeys.newStringConfigKey("brooklyn.ansible.serviceName",
         "Name of OS service this will run as, for use in checking running and stopping");
+
+    @SetFromFlag("ansible.vars")
+    ConfigKey<Object> ANSIBLE_VARS = ConfigKeys.newConfigKey(Object.class, "brooklyn.ansible.vars",
+        "Ansible 'extra-vars' variable configuration values");
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/121db325/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityImpl.java
----------------------------------------------------------------------
diff --git a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityImpl.java b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityImpl.java
index b34603e..c2635ca 100644
--- a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityImpl.java
+++ b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityImpl.java
@@ -27,13 +27,18 @@ import static com.google.common.base.Preconditions.checkNotNull;
 
 public class AnsibleEntityImpl extends EffectorStartableImpl implements AnsibleEntity {
 
+    private AnsibleLifecycleEffectorTasks lifecycleTasks;
+
     public void init() {
         checkNotNull(getConfig(SERVICE_NAME), "service name is missing. it has to be provided by the user");
         String playbookName = getConfig(ANSIBLE_PLAYBOOK);
         if (!Strings.isBlank(playbookName)) setDefaultDisplayName(playbookName + " (ansible)");
 
         super.init();
-        new AnsibleLifecycleEffectorTasks().attachLifecycleEffectors(this);
+
+        lifecycleTasks = new AnsibleLifecycleEffectorTasks();
+
+        lifecycleTasks.attachLifecycleEffectors(this);
     }
 
     @Override
@@ -43,13 +48,14 @@ public class AnsibleEntityImpl extends EffectorStartableImpl implements AnsibleE
 
     @Override
     public String ansibleCommand(String module, String args) {
-        final ProcessTaskWrapper<Integer> command = DynamicTasks.queue(AnsiblePlaybookTasks.moduleCommand(module, args));
-                command.asTask().blockUntilEnded();
-                if (0 == command.getExitCode()) {
-                        return command.getStdout();
-                    } else {
-                        throw new RuntimeException("Command (" + args + ") in module " + module
-                            +  " failed with stderr:\n" + command.getStderr() + "\n");
-                    }
+        final ProcessTaskWrapper<Integer> command = DynamicTasks.queue(
+            AnsiblePlaybookTasks.moduleCommand(module, config().get(ANSIBLE_VARS), lifecycleTasks.getRunDir(), args));
+        command.asTask().blockUntilEnded();
+        if (0 == command.getExitCode()) {
+            return command.getStdout();
+        } else {
+            throw new RuntimeException("Command (" + args + ") in module " + module
+                + " failed with stderr:\n" + command.getStderr() + "\n");
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/121db325/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleLifecycleEffectorTasks.java
----------------------------------------------------------------------
diff --git a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleLifecycleEffectorTasks.java b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleLifecycleEffectorTasks.java
index 0262af3..e1a8622 100644
--- a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleLifecycleEffectorTasks.java
+++ b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleLifecycleEffectorTasks.java
@@ -18,6 +18,7 @@
  */
 package org.apache.brooklyn.entity.cm.ansible;
 
+import com.google.common.base.Supplier;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.location.MachineLocation;
 import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks;
@@ -31,16 +32,14 @@ import org.apache.brooklyn.entity.software.base.lifecycle.MachineLifecycleEffect
 import org.apache.brooklyn.feed.ssh.SshFeed;
 import org.apache.brooklyn.feed.ssh.SshPollConfig;
 import org.apache.brooklyn.location.ssh.SshMachineLocation;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.apache.brooklyn.util.core.task.DynamicTasks;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.net.Urls;
 import org.apache.brooklyn.util.text.Strings;
 import org.apache.brooklyn.util.time.Duration;
 import org.apache.brooklyn.util.time.Time;
-
-import com.google.common.base.Supplier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class AnsibleLifecycleEffectorTasks extends MachineLifecycleEffectorTasks implements AnsibleConfig {
 
@@ -48,7 +47,11 @@ public class AnsibleLifecycleEffectorTasks extends MachineLifecycleEffectorTasks
 
     protected String serviceName;
     protected SshFeed serviceSshFeed;
-    
+
+    protected Object extraVars;
+    protected String baseDir;
+    protected String runDir;
+
     public AnsibleLifecycleEffectorTasks() {
     }
 
@@ -57,6 +60,23 @@ public class AnsibleLifecycleEffectorTasks extends MachineLifecycleEffectorTasks
         return serviceName = entity().config().get(AnsibleConfig.SERVICE_NAME);
     }
 
+    public Object getExtraVars() {
+        if (extraVars != null) return extraVars;
+        return extraVars = entity().config().get(ANSIBLE_VARS);
+    }
+
+    public String getBaseDir() {
+        if (null != baseDir) return baseDir;
+        return baseDir = MachineLifecycleEffectorTasks.resolveOnBoxDir(entity(),
+                Machines.findUniqueMachineLocation(entity().getLocations(), SshMachineLocation.class).get());
+    }
+
+    public String getRunDir() {
+        if (null != runDir) return runDir;
+        return runDir = Urls.mergePaths(getBaseDir(), "apps/"+entity().getApplicationId()+"/ansible/playbooks/"
+            +entity().getEntityType().getSimpleName()+"_"+entity().getId());
+    }
+
     @Override
     public void attachLifecycleEffectors(Entity entity) {
         if (getServiceName()==null && getClass().equals(AnsibleLifecycleEffectorTasks.class)) {
@@ -79,30 +99,33 @@ public class AnsibleLifecycleEffectorTasks extends MachineLifecycleEffectorTasks
     }
 
     protected void startWithAnsibleAsync() {
-        String baseDir = MachineLifecycleEffectorTasks.resolveOnBoxDir(entity(), Machines.findUniqueMachineLocation(entity().getLocations(), SshMachineLocation.class).get());
-        String installDir = Urls.mergePaths(baseDir, "installs/ansible");
+        String installDir = Urls.mergePaths(getBaseDir(), "installs/ansible");
 
         String playbookUrl = entity().config().get(ANSIBLE_PLAYBOOK_URL);
         String playbookYaml = entity().config().get(ANSIBLE_PLAYBOOK_YAML);
 
         if (Strings.isNonBlank(playbookUrl) && Strings.isNonBlank(playbookYaml)) {
-            throw new IllegalArgumentException("You can specify " +  AnsibleConfig.ANSIBLE_PLAYBOOK_URL.getName() +  " or " + AnsibleConfig.ANSIBLE_PLAYBOOK_YAML.getName() + " but not both of them!");
+            throw new IllegalArgumentException("You can specify " +  AnsibleConfig.ANSIBLE_PLAYBOOK_URL.getName()
+                +  " or " + AnsibleConfig.ANSIBLE_PLAYBOOK_YAML.getName() + " but not both of them!");
         }
 
         DynamicTasks.queue(AnsiblePlaybookTasks.installAnsible(installDir, false));
 
-        String runDir = Urls.mergePaths(baseDir, "apps/"+entity().getApplicationId()+"/ansible/playbooks/"+entity().getEntityType().getSimpleName()+"_"+entity().getId());
-        
+        if (getExtraVars() != null) {
+            DynamicTasks.queue(AnsiblePlaybookTasks.configureExtraVars(getRunDir(), extraVars, false));
+        }
+
         if (Strings.isNonBlank(playbookUrl)) {
-            DynamicTasks.queue(AnsiblePlaybookTasks.installPlaybook(runDir, getPlaybookName(), playbookUrl));
+            DynamicTasks.queue(AnsiblePlaybookTasks.installPlaybook(getRunDir(), getPlaybookName(), playbookUrl));
         }
 
         if (Strings.isNonBlank(playbookYaml)) {
-            DynamicTasks.queue(AnsiblePlaybookTasks.buildPlaybookFile(runDir, getPlaybookName()));
+            DynamicTasks.queue(AnsiblePlaybookTasks.buildPlaybookFile(getRunDir(), getPlaybookName()));
         }
-        DynamicTasks.queue(AnsiblePlaybookTasks.runAnsible(runDir, getPlaybookName()));
+        DynamicTasks.queue(AnsiblePlaybookTasks.runAnsible(getRunDir(), getExtraVars(), getPlaybookName()));
     }
 
+
     protected void postStartCustom() {
         boolean result = false;
         result |= tryCheckStartService();

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/121db325/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java
----------------------------------------------------------------------
diff --git a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java
index 29924d4..36076a1 100644
--- a/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java
+++ b/software/cm/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java
@@ -27,15 +27,17 @@ import org.apache.brooklyn.util.core.task.Tasks;
 import org.apache.brooklyn.util.core.task.system.ProcessTaskFactory;
 import org.apache.brooklyn.util.net.Urls;
 import org.apache.brooklyn.util.ssh.BashCommands;
+import org.apache.brooklyn.util.text.Strings;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-
-import java.util.Map;
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
 
 import static org.apache.brooklyn.util.ssh.BashCommands.sudo;
 
 public class AnsiblePlaybookTasks {
     private static final Logger LOG = LoggerFactory.getLogger(AnsiblePlaybookTasks.class);
+    private static final String EXTRA_VARS_FILENAME = "extra_vars.yaml";
 
     public static TaskFactory<?> installAnsible(String ansibleDirectory, boolean force) {
         String installCmd = cdAndRun(ansibleDirectory, AnsibleBashCommands.INSTALL_ANSIBLE);
@@ -45,7 +47,9 @@ public class AnsiblePlaybookTasks {
 
     public static TaskFactory<?> installPlaybook(final String ansibleDirectory, final String playbookName, final String playbookUrl) {
         return Tasks.sequential("build ansible playbook file for "+playbookName,
-                SshEffectorTasks.put(ansibleDirectory + "/" + playbookName + ".yaml").contents(ResourceUtils.create().getResourceFromUrl(playbookUrl)).createDirectory());
+                SshEffectorTasks.put(ansibleDirectory + "/" + playbookName + ".yaml")
+                    .contents(ResourceUtils.create().getResourceFromUrl(playbookUrl))
+                    .createDirectory());
     }
     
     protected static String cdAndRun(String targetDirectory, String command) {
@@ -59,24 +63,47 @@ public class AnsiblePlaybookTasks {
         String yaml = entity.config().get(AnsibleConfig.ANSIBLE_PLAYBOOK_YAML);
 
         return Tasks.sequential("build ansible playbook file for "+ playbook,
-                    SshEffectorTasks.put(Urls.mergePaths(ansibleDirectory) + "/" + playbook + ".yaml").contents(yaml).createDirectory());
+            SshEffectorTasks.put(Urls.mergePaths(ansibleDirectory) + "/" + playbook + ".yaml")
+                .contents(yaml).createDirectory());
     }
 
-    public static TaskFactory<?> runAnsible(final String ansibleDirectory, String playbookName) {
-        String cmd = String.format("sudo ansible-playbook -i \"localhost,\" -c local -s %s.yaml", playbookName);
+    public static TaskFactory<?> runAnsible(final String dir, Object extraVars, String playbookName) {
+        String cmd = String.format("sudo ansible-playbook -i \"localhost,\" -c local "
+            + optionalExtraVarsParameter(extraVars)
+            + " -s %s.yaml", playbookName);
 
         if (LOG.isDebugEnabled()) {
             LOG.debug("Ansible command: {}", cmd);
         }
 
-        return SshEffectorTasks.ssh(cdAndRun(ansibleDirectory, cmd)).
+        return SshEffectorTasks.ssh(cdAndRun(dir, cmd)).
                 summary("run ansible playbook for " + playbookName).requiringExitCodeZero();
     }
 
-    public static ProcessTaskFactory<Integer> moduleCommand(String module, String args) {
-        final String command = "ansible localhost -m '" + module + "' -a '" + args + "'";
-        return SshEffectorTasks.ssh(sudo(command))
+    public static ProcessTaskFactory<Integer> moduleCommand(String module, Object extraVars, String root, String args) {
+        final String command = "ansible localhost "
+            + optionalExtraVarsParameter(extraVars)
+            + " -m '" + module + "' -a '" + args + "'";
+        return SshEffectorTasks.ssh(sudo(BashCommands.chain("cd " + root, command)))
             .summary("ad-hoc: " + command).requiringExitCodeZero();
     }
+
+    public static TaskFactory<?> configureExtraVars(String dir, Object extraVars, boolean force) {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(DumperOptions.ScalarStyle.DOUBLE_QUOTED);
+        Yaml asYaml = new Yaml(options);
+        final String varsYaml = asYaml.dump(extraVars);
+        return SshEffectorTasks.put(Urls.mergePaths(dir, EXTRA_VARS_FILENAME))
+            .contents(varsYaml)
+            .summary("install extra vars")
+            .createDirectory();
+    }
+
+    private static String optionalExtraVarsParameter(Object extraVars) {
+        if (null == extraVars || Strings.isBlank(extraVars.toString())) {
+            return "";
+        }
+        return " --extra-vars \"@" + EXTRA_VARS_FILENAME + "\" ";
+    }
 }
 


[6/7] brooklyn-library git commit: Addressing PR comments

Posted by he...@apache.org.
Addressing PR comments

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

Branch: refs/heads/master
Commit: a01cba0cabb8b3b980c51cd8b90bf9b2b715fe32
Parents: c1a8783
Author: Yavor Yanchev <ya...@yanchev.com>
Authored: Wed Feb 17 11:36:39 2016 +0200
Committer: Yavor Yanchev <ya...@yanchev.com>
Committed: Thu Feb 18 12:54:51 2016 +0200

----------------------------------------------------------------------
 .../entity/cm/ansible/AnsibleBashCommands.java  |  3 +-
 .../entity/cm/ansible/AnsibleConfig.java        |  8 +++-
 .../entity/cm/ansible/AnsibleEntityImpl.java    |  2 -
 .../ansible/AnsibleLifecycleEffectorTasks.java  | 48 +++++++++++++++-----
 .../entity/cm/ansible/AnsiblePlaybookTasks.java |  4 +-
 5 files changed, 46 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a01cba0c/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleBashCommands.java
----------------------------------------------------------------------
diff --git a/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleBashCommands.java b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleBashCommands.java
index e22ee34..ffe74e4 100644
--- a/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleBashCommands.java
+++ b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleBashCommands.java
@@ -22,6 +22,7 @@ import static org.apache.brooklyn.util.ssh.BashCommands.INSTALL_CURL;
 import static org.apache.brooklyn.util.ssh.BashCommands.INSTALL_TAR;
 import static org.apache.brooklyn.util.ssh.BashCommands.INSTALL_UNZIP;
 import static org.apache.brooklyn.util.ssh.BashCommands.installExecutable;
+import static org.apache.brooklyn.util.ssh.BashCommands.ifExecutableElse0;
 import static org.apache.brooklyn.util.ssh.BashCommands.sudo;
 
 import org.apache.brooklyn.util.ssh.BashCommands;
@@ -30,7 +31,7 @@ public class AnsibleBashCommands {
 
     public static final String INSTALL_ANSIBLE =
             BashCommands.chain(
-                    sudo("apt-add-repository -y ppa:ansible/ansible"),
+                    ifExecutableElse0("apt-add-repository",sudo("apt-add-repository -y ppa:ansible/ansible")),
                     INSTALL_CURL,
                     INSTALL_TAR,
                     INSTALL_UNZIP,

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a01cba0c/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleConfig.java
----------------------------------------------------------------------
diff --git a/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleConfig.java b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleConfig.java
index d63c2c6..a47ffeb 100644
--- a/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleConfig.java
+++ b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleConfig.java
@@ -46,12 +46,16 @@ public interface AnsibleConfig {
     @SetFromFlag("ansible.service.start")
     ConfigKey<String> ANSIBLE_SERVICE_START = ConfigKeys.newStringConfigKey("ansible.service.start",
             "Default start command used with conjunction with the Ansible's service module",
-            "sudo ansible localhost -c local -m service -a \"name=%s state=started\"");
+            "ansible localhost -c local -m service -a \"name=%s state=started\"");
 
     @SetFromFlag("ansible.service.stop")
     ConfigKey<String> ANSIBLE_SERVICE_STOP = ConfigKeys.newStringConfigKey("ansible.service.stop",
             "Default stop command used with conjunction with the Ansible's service module",
-            "sudo ansible localhost -c local -m service -a \"name=%s state=stopped\"");
+            "ansible localhost -c local -m service -a \"name=%s state=stopped\"");
+
+    @SetFromFlag("ansible.service.checkHost")
+    ConfigKey<String> ANSIBLE_SERVICE_CHECK_HOST = ConfigKeys.newStringConfigKey("ansible.service.check.host",
+            "IP to be checked. Default: All IPs ", "0.0.0.0");
 
     @SetFromFlag("ansible.service.checkPort")
     ConfigKey<Integer> ANSIBLE_SERVICE_CHECK_PORT = ConfigKeys.newIntegerConfigKey("ansible.service.check.port");

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a01cba0c/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityImpl.java
----------------------------------------------------------------------
diff --git a/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityImpl.java b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityImpl.java
index c2635ca..43fcd72 100644
--- a/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityImpl.java
+++ b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntityImpl.java
@@ -41,12 +41,10 @@ public class AnsibleEntityImpl extends EffectorStartableImpl implements AnsibleE
         lifecycleTasks.attachLifecycleEffectors(this);
     }
 
-    @Override
     public void populateServiceNotUpDiagnostics() {
         // TODO no-op currently; should check ssh'able etc
     }
 
-    @Override
     public String ansibleCommand(String module, String args) {
         final ProcessTaskWrapper<Integer> command = DynamicTasks.queue(
             AnsiblePlaybookTasks.moduleCommand(module, config().get(ANSIBLE_VARS), lifecycleTasks.getRunDir(), args));

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a01cba0c/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleLifecycleEffectorTasks.java
----------------------------------------------------------------------
diff --git a/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleLifecycleEffectorTasks.java b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleLifecycleEffectorTasks.java
index e1a8622..e51d9e9 100644
--- a/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleLifecycleEffectorTasks.java
+++ b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleLifecycleEffectorTasks.java
@@ -35,6 +35,8 @@ import org.apache.brooklyn.location.ssh.SshMachineLocation;
 import org.apache.brooklyn.util.core.task.DynamicTasks;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.net.Urls;
+
+import static org.apache.brooklyn.util.ssh.BashCommands.sudo;
 import org.apache.brooklyn.util.text.Strings;
 import org.apache.brooklyn.util.time.Duration;
 import org.apache.brooklyn.util.time.Time;
@@ -99,14 +101,20 @@ public class AnsibleLifecycleEffectorTasks extends MachineLifecycleEffectorTasks
     }
 
     protected void startWithAnsibleAsync() {
+        
         String installDir = Urls.mergePaths(getBaseDir(), "installs/ansible");
 
         String playbookUrl = entity().config().get(ANSIBLE_PLAYBOOK_URL);
         String playbookYaml = entity().config().get(ANSIBLE_PLAYBOOK_YAML);
 
-        if (Strings.isNonBlank(playbookUrl) && Strings.isNonBlank(playbookYaml)) {
-            throw new IllegalArgumentException("You can specify " +  AnsibleConfig.ANSIBLE_PLAYBOOK_URL.getName()
-                +  " or " + AnsibleConfig.ANSIBLE_PLAYBOOK_YAML.getName() + " but not both of them!");
+        if (playbookUrl != null && playbookYaml != null) {
+            throw new IllegalArgumentException( "You can not specify both "+  AnsibleConfig.ANSIBLE_PLAYBOOK_URL.getName() + 
+                " and " + AnsibleConfig.ANSIBLE_PLAYBOOK_YAML.getName() + " as arguments.");
+        }
+
+        if (playbookUrl == null && playbookYaml == null) { 
+                throw new IllegalArgumentException("You have to specify either " + AnsibleConfig.ANSIBLE_PLAYBOOK_URL.getName() + 
+                " or " + AnsibleConfig.ANSIBLE_PLAYBOOK_YAML.getName() + " as arguments.");
         }
 
         DynamicTasks.queue(AnsiblePlaybookTasks.installAnsible(installDir, false));
@@ -138,15 +146,21 @@ public class AnsibleLifecycleEffectorTasks extends MachineLifecycleEffectorTasks
         Maybe<SshMachineLocation> machine = Locations.findUniqueSshMachineLocation(entity().getLocations());
 
         if (machine.isPresent()) {
-            
-            String serviceName = String.format("[%s]%s", entity().config().get(SERVICE_NAME).substring(0, 1),
-                    entity().config().get(SERVICE_NAME).substring(1));
-            String checkCmd = String.format("ps -ef | grep %s", serviceName);
+            // For example “ps -f| grep httpd” matches for any process including the text “httpd”,
+            // which includes the grep command itself, whereas “ps | grep [h]ttpd” matches only processes
+            // including the text “httpd” (doesn’t include the grep) and additionally 
+            // provides a correct return code
+            //
+            // The command constructed bellow will look like  - ps -ef |grep [h]ttpd
+            String serviceNameCheck = getServiceName().replaceFirst("^(.)(.*)", "[$1]$2");
+            String checkCmd = String.format("ps -ef | grep %s", serviceNameCheck);
 
             Integer serviceCheckPort = entity().config().get(ANSIBLE_SERVICE_CHECK_PORT);
 
             if (serviceCheckPort != null) {
-                checkCmd = String.format("sudo ansible localhost -c local -m wait_for -a \"host=0.0.0.0 port=%d\"", serviceCheckPort);
+                checkCmd = sudo(String.format("ansible localhost -c local -m wait_for -a \"host=" + 
+                                        entity().config().get(ANSIBLE_SERVICE_CHECK_HOST) + 
+                                        "\" port=%d\"", serviceCheckPort));
             }
             serviceSshFeed = SshFeed.builder()
                     .entity(entity())
@@ -160,7 +174,8 @@ public class AnsibleLifecycleEffectorTasks extends MachineLifecycleEffectorTasks
                     
              entity().feeds().addFeed(serviceSshFeed);
         } else {
-            LOG.warn("Location(s) {} not an ssh-machine location, so not polling for status; setting serviceUp immediately", entity().getLocations());
+            LOG.warn("Location(s) {} not an ssh-machine location, so not polling for status; "
+                    + "setting serviceUp immediately", entity().getLocations());
         }
     }
 
@@ -169,7 +184,8 @@ public class AnsibleLifecycleEffectorTasks extends MachineLifecycleEffectorTasks
 
         // if it's still up after 5s assume we are good (default behaviour)
         Time.sleep(Duration.FIVE_SECONDS);
-        if (!((Integer)0).equals(DynamicTasks.queue(SshEffectorTasks.ssh(String.format(entity().config().get(AnsibleConfig.ANSIBLE_SERVICE_START), getServiceName()))).get())) {
+        int result = DynamicTasks.queue(SshEffectorTasks.ssh(sudo(getServiveStartCommand()))).get();
+        if (0 != result) {
             throw new IllegalStateException("The process for "+entity()+" appears not to be running (service "+getServiceName()+")");
         }
 
@@ -193,11 +209,19 @@ public class AnsibleLifecycleEffectorTasks extends MachineLifecycleEffectorTasks
 
     protected boolean tryStopService() {
         if (getServiceName()==null) return false;
-        int result = DynamicTasks.queue(SshEffectorTasks.ssh(String.format(entity().config().get(AnsibleConfig.ANSIBLE_SERVICE_STOP), getServiceName()))).get();
+        int result = DynamicTasks.queue(SshEffectorTasks.ssh(sudo(getServiveStopCommand()))).get();
         if (0 == result) return true;
         if (entity().getAttribute(Attributes.SERVICE_STATE_ACTUAL) != Lifecycle.RUNNING)
             return true;
-        
+
         throw new IllegalStateException("The process for "+entity()+" appears could not be stopped (exit code "+result+" to service stop)");
     }
+
+    private String getServiveStartCommand() {
+        return String.format(entity().config().get(AnsibleConfig.ANSIBLE_SERVICE_START), getServiceName());
+    }
+
+    private String getServiveStopCommand() {
+        return String.format(entity().config().get(AnsibleConfig.ANSIBLE_SERVICE_STOP), getServiceName());
+    }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a01cba0c/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java
----------------------------------------------------------------------
diff --git a/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java
index 36076a1..152bc61 100644
--- a/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java
+++ b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java
@@ -68,9 +68,9 @@ public class AnsiblePlaybookTasks {
     }
 
     public static TaskFactory<?> runAnsible(final String dir, Object extraVars, String playbookName) {
-        String cmd = String.format("sudo ansible-playbook -i \"localhost,\" -c local "
+        String cmd = sudo(String.format("ansible-playbook -i \"localhost,\" -c local "
             + optionalExtraVarsParameter(extraVars)
-            + " -s %s.yaml", playbookName);
+            + " -s %s.yaml", playbookName));
 
         if (LOG.isDebugEnabled()) {
             LOG.debug("Ansible command: {}", cmd);