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 <[email protected]> Authored: Fri Feb 5 10:05:41 2016 +0200 Committer: Yavor Yanchev <[email protected]> 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(); + } + +}
