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

org.apache.brooklyn.entity.machine.SetHostnameCustomizer Maven / Gradle / Ivy

There is a newer version: 1.1.0
Show newest version
/*
 * 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.machine;

import static com.google.common.base.Preconditions.checkArgument;

import java.util.Arrays;

import org.apache.brooklyn.api.location.BasicMachineLocationCustomizer;
import org.apache.brooklyn.api.location.MachineLocation;
import org.apache.brooklyn.config.ConfigKey;
import org.apache.brooklyn.core.config.ConfigKeys;
import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks;
import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks.SshEffectorTaskFactory;
import org.apache.brooklyn.location.ssh.SshMachineLocation;
import org.apache.brooklyn.util.core.config.ConfigBag;
import org.apache.brooklyn.util.core.task.DynamicTasks;
import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper;
import org.apache.brooklyn.util.core.text.TemplateProcessor;
import org.apache.brooklyn.util.net.Networking;
import org.apache.brooklyn.util.ssh.BashCommands;
import org.apache.brooklyn.util.text.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Joiner;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.reflect.TypeToken;

/**
 * Sets the hostname on an ssh'able machine. Currently only CentOS and RHEL are supported.
 * 

* The customizer can be configured with a hard-coded hostname, or with a freemarker template * whose value (after substitutions) will be used for the hostname. */ // TODO basic hostname setting is done by JcloudsLocation, combine that code with this public class SetHostnameCustomizer extends BasicMachineLocationCustomizer { public static final Logger log = LoggerFactory.getLogger(SetHostnameCustomizer.class); public static final ConfigKey FIXED_HOSTNAME = ConfigKeys.newStringConfigKey( "hostname.fixed", "The statically defined hostname to be set on the machine (if non-null)"); public static final ConfigKey FIXED_DOMAIN = ConfigKeys.newStringConfigKey( "domain.fixed", "The statically defined domain name to be set on the machine (if non-null)"); // the var?? tests if it exists, passing value to ?string(if_present,if_absent) // the ! provides a default value afterwards, which is never used, but is required for parsing // when the config key is not available; // thus the below prefers the first private address, then the first public address, and then // substitutes dots for dashes. public static final ConfigKey HOSTNAME_TEMPLATE = ConfigKeys.newStringConfigKey( "hostname.templated", "The hostname template, to be resolved and then set on the machine (if non-null). " +"Assumed to be in free-marker format.", "ip-${(" + "(location.privateAddresses[0]??)?string(" + "location.privateAddresses[0]!'X', " + "(location.publicAddresses[0]??)?string(" + "location.publicAddresses[0]!'X', " + "\"none\"))" + ")" + "?replace(\".\",\"-\")}" + "-${location.id}"); public static final ConfigKey LOCAL_HOSTNAME = ConfigKeys.newStringConfigKey( "hostname.local.hostname", "Host name, as known on the local box. Config is set on the location."); public static final ConfigKey LOCAL_IP = ConfigKeys.newStringConfigKey( "hostname.local.address", "Host address, as known on the local box. Config is set on the location."); public static final ConfigKey> MACHINE_FILTER = ConfigKeys.newConfigKey( new TypeToken>() {}, "machineFilter", "A filter to say which machines this should be applied to", Predicates.instanceOf(SshMachineLocation.class)); private final ConfigBag config; public SetHostnameCustomizer(ConfigBag config) { // TODO Any checks that they've given us sufficient configuration? this.config = config; } @Override public void customize(MachineLocation machine) { if (config.get(MACHINE_FILTER).apply(machine)) { log.info("SetHostnameCustomizer setting hostname on "+machine); } else { log.info("SetHostnameCustomizer ignoring non-ssh machine "+machine); return; } try { String localHostname = setLocalHostname((SshMachineLocation) machine); machine.config().set(LOCAL_HOSTNAME, localHostname); String localIp = execHostnameMinusI((SshMachineLocation) machine); machine.config().set(LOCAL_IP, localIp); } catch (Exception e) { log.info("SetHostnameCustomizer failed to set hostname on "+machine+" (rethrowing)", e); throw e; } } protected String generateHostname(SshMachineLocation machine) { String hostnameTemplate = config.get(HOSTNAME_TEMPLATE); if (Strings.isNonBlank(hostnameTemplate)) { return TemplateProcessor.processTemplateContents(hostnameTemplate, machine, ImmutableMap.of()); } else { return null; } } /** * Sets the machine's hostname to the value controlled by fixed_hostname and hostname_template. * If these are blank (and fixed_domain is blank), then just return the current hostname of * the machine. */ public String setLocalHostname(SshMachineLocation machine) { String hostFixed = config.get(FIXED_HOSTNAME); String domainFixed = config.get(FIXED_DOMAIN); String hostnameTemplate = config.get(HOSTNAME_TEMPLATE); String hostname; if (Strings.isNonBlank(hostFixed)) { hostname = hostFixed; } else { if (Strings.isNonBlank(hostnameTemplate)) { hostname = generateHostname(machine); } else { hostname = execHostname(machine); if (Strings.isBlank(domainFixed)) { return hostname; } } } return setLocalHostname(machine, hostname, domainFixed); } /** * Sets the machine's hostname to the given value, ensuring /etc/hosts and /etc/sysconfig/network are both * correctly updated. */ public String setLocalHostname(SshMachineLocation machine, String hostName, String domainFixed) { log.info("Setting local hostname of " + machine + " to " + hostName + (Strings.isNonBlank(domainFixed) ? ", " + domainFixed : "")); boolean hasDomain = Strings.isNonBlank(domainFixed); String fqdn = hasDomain ? hostName+"."+domainFixed : hostName; exec(machine, true, BashCommands.sudo(String.format("sed -i.bak -e '1i127.0.0.1 %s %s' -e '/^127.0.0.1/d' /etc/hosts", fqdn, hostName)), BashCommands.sudo(String.format("sed -i.bak -e 's/^HOSTNAME=.*$/HOSTNAME=%s/' /etc/sysconfig/network", fqdn)), BashCommands.sudo(String.format("hostname %s", fqdn))); return hostName; } protected void registerEtcHosts(SshMachineLocation machine, String ip, Iterable hostnames) { log.info("Updating /etc/hosts of "+machine+": adding "+ip+" = "+hostnames); checkArgument(Strings.isNonBlank(ip) && Networking.isValidIp4(ip), "invalid IPv4 address %s", ip); if (Strings.isBlank(ip) || Iterables.isEmpty(hostnames)) return; String line = ip+" "+Joiner.on(" ").join(hostnames); exec(machine, true, "echo " + line + " >> /etc/hosts"); } protected String execHostname(SshMachineLocation machine) { if (log.isDebugEnabled()) log.debug("Retrieve `hostname` via ssh for {}", machine); ProcessTaskWrapper cmd = exec(machine, false, "echo hostname=`hostname`"); // ProcessTaskWrapper cmd = DynamicTasks.queue(SshEffectorTasks.ssh(machine, "echo hostname=`hostname`") // .summary("getHostname")) // .block(); for (String line : cmd.getStdout().split("\n")) { if (line.contains("hostname=") && !line.contains("`hostname`")) { return line.substring(line.indexOf("hostname=") + "hostname=".length()).trim(); } } log.info("No hostname found for {} (got {}; {})", new Object[] {machine, cmd.getStdout(), cmd.getStderr()}); return null; } protected String execHostnameMinusI(SshMachineLocation machine) { if (log.isDebugEnabled()) log.debug("Retrieve `hostname -I` via ssh for {}", machine); ProcessTaskWrapper cmd = exec(machine, false, "echo localip=`hostname -I`"); for (String line : cmd.getStdout().split("\n")) { if (line.contains("localip=") && !line.contains("`hostname -I`")) { return line.substring(line.indexOf("localip=") + "localip=".length()).trim(); } } log.info("No local ip found for {} (got {}; {})", new Object[] {machine, cmd.getStdout(), cmd.getStderr()}); return null; } protected ProcessTaskWrapper exec(SshMachineLocation machine, boolean asRoot, String... cmds) { SshEffectorTaskFactory taskFactory = SshEffectorTasks.ssh(machine, cmds); if (asRoot) taskFactory.runAsRoot(); ProcessTaskWrapper result = DynamicTasks.queue(taskFactory).block(); if (result.get() != 0) { throw new IllegalStateException("SetHostnameCustomizer got exit code "+result.get()+" executing on machine "+machine +"; cmds="+Arrays.asList(cmds)+"; stdout="+result.getStdout()+"; stderr="+result.getStderr()); } return result; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy