@Override public String getSeeds() { Set<Entity> seeds = getConfig(CassandraNode.INITIAL_SEEDS); if (seeds == null) { log.warn( "No seeds available when requested for " + this, new Throwable("source of no Cassandra seeds when requested")); return null; } String snitchName = getConfig(CassandraNode.ENDPOINT_SNITCH_NAME); MutableSet<String> seedsHostnames = MutableSet.of(); for (Entity entity : seeds) { // tried removing ourselves if there are other nodes, but that is a BAD idea! // blows up with a "java.lang.RuntimeException: No other nodes seen!" if (snitchName.equals("Ec2MultiRegionSnitch") || snitchName.contains("MultiCloudSnitch")) { // http://www.datastax.com/documentation/cassandra/2.0/mobile/cassandra/architecture/architectureSnitchEC2MultiRegion_c.html // says the seeds should be public IPs. seedsHostnames.add(entity.getAttribute(CassandraNode.ADDRESS)); } else { String sensorName = getConfig(BROADCAST_ADDRESS_SENSOR); if (Strings.isNonBlank(sensorName)) { seedsHostnames.add(entity.getAttribute(Sensors.newStringSensor(sensorName))); } else { Maybe<String> optionalSeedHostname = Machines.findSubnetOrPublicHostname(entity); if (optionalSeedHostname.isPresent()) { String seedHostname = optionalSeedHostname.get(); seedsHostnames.add(seedHostname); } else { log.warn( "In node {}, seed hostname missing for {}; not including in seeds list", this, entity); } } } } String result = Strings.join(seedsHostnames, ","); log.info("Seeds for {}: {}", this, result); return result; }
protected void configureResolver(Entity entity) { Maybe<SshMachineLocation> machine = Machines.findUniqueSshMachineLocation(entity.getLocations()); if (machine.isPresent()) { if (getConfig(REPLACE_RESOLV_CONF)) { machine.get().copyTo(new StringReader(getConfig(RESOLV_CONF_TEMPLATE)), "/etc/resolv.conf"); } else { appendTemplate( getConfig(INTERFACE_CONFIG_TEMPLATE), "/etc/sysconfig/network-scripts/ifcfg-eth0", machine.get()); machine .get() .execScript( "reload network", ImmutableList.of(BashCommands.sudo("service network reload"))); } LOG.info("configured resolver on {}", machine); } else { LOG.debug("{} can't configure resolver at {}: no SshMachineLocation", this, entity); } }
public void update() { Lifecycle serverState = getAttribute(Attributes.SERVICE_STATE_ACTUAL); if (Lifecycle.STOPPED.equals(serverState) || Lifecycle.STOPPING.equals(serverState) || Lifecycle.DESTROYED.equals(serverState) || !getAttribute(Attributes.SERVICE_UP)) { LOG.debug( "Skipped update of {} when service state is {} and running is {}", new Object[] { this, getAttribute(Attributes.SERVICE_STATE_ACTUAL), getAttribute(SERVICE_UP) }); return; } synchronized (this) { Iterable<Entity> availableEntities = FluentIterable.from(getEntities().getMembers()) .filter(new HasHostnameAndValidLifecycle()); LOG.debug("{} updating with entities: {}", this, Iterables.toString(availableEntities)); ImmutableListMultimap<String, Entity> hostnameToEntity = Multimaps.index(availableEntities, new HostnameTransformer()); Map<String, String> octetToName = Maps.newHashMap(); BiMap<String, String> ipToARecord = HashBiMap.create(); Multimap<String, String> aRecordToCnames = MultimapBuilder.hashKeys().hashSetValues().build(); Multimap<String, String> ipToAllNames = MultimapBuilder.hashKeys().hashSetValues().build(); for (Map.Entry<String, Entity> e : hostnameToEntity.entries()) { String domainName = e.getKey(); Maybe<SshMachineLocation> location = Machines.findUniqueSshMachineLocation(e.getValue().getLocations()); if (!location.isPresent()) { LOG.debug( "Member {} of {} does not have an SSH location so will not be configured", e.getValue(), this); continue; } else if (ipToARecord.inverse().containsKey(domainName)) { continue; } String address = location.get().getAddress().getHostAddress(); ipToAllNames.put(address, domainName); if (!ipToARecord.containsKey(address)) { ipToARecord.put(address, domainName); if (getReverseLookupNetwork().contains(new Cidr(address + "/32"))) { String octet = Iterables.get(Splitter.on('.').split(address), 3); if (!octetToName.containsKey(octet)) octetToName.put(octet, domainName); } } else { aRecordToCnames.put(ipToARecord.get(address), domainName); } } setAttribute(A_RECORDS, ImmutableMap.copyOf(ipToARecord.inverse())); setAttribute(PTR_RECORDS, ImmutableMap.copyOf(octetToName)); setAttribute(CNAME_RECORDS, Multimaps.unmodifiableMultimap(aRecordToCnames)); setAttribute(ADDRESS_MAPPINGS, Multimaps.unmodifiableMultimap(ipToAllNames)); // Update Bind configuration files and restart the service getDriver().updateBindConfiguration(); } }
public static HostAndPort getBrooklynAccessibleAddress(Entity entity, int port) { String host; // look up port forwarding PortForwardManager pfw = entity.getConfig(PORT_FORWARDING_MANAGER); if (pfw != null) { Collection<Location> ll = entity.getLocations(); synchronized (BrooklynAccessUtils.class) { // TODO finer-grained synchronization for (MachineLocation machine : Iterables.filter(ll, MachineLocation.class)) { HostAndPort hp = pfw.lookup(machine, port); if (hp != null) { log.debug( "BrooklynAccessUtils found port-forwarded address {} for entity {}, port {}, using machine {}", new Object[] {hp, entity, port, machine}); return hp; } } Maybe<SupportsPortForwarding> supportPortForwardingLoc = Machines.findUniqueElement(ll, SupportsPortForwarding.class); if (supportPortForwardingLoc.isPresent()) { Cidr source = entity.getConfig(MANAGEMENT_ACCESS_CIDR); SupportsPortForwarding loc = supportPortForwardingLoc.get(); if (source != null) { log.debug( "BrooklynAccessUtils requesting new port-forwarding rule to access " + port + " on " + entity + " (at " + loc + ", enabled for " + source + ")"); // TODO discuss, is this the best way to do it // (will probably _create_ the port forwarding rule!) HostAndPort hp = loc.getSocketEndpointFor(source, port); if (hp != null) { log.debug( "BrooklynAccessUtils created port-forwarded address {} for entity {}, port {}, using {}", new Object[] {hp, entity, port, loc}); return hp; } } else { log.warn( "No " + MANAGEMENT_ACCESS_CIDR.getName() + " configured for " + entity + ", so cannot forward " + "port " + port + " " + "even though " + PORT_FORWARDING_MANAGER.getName() + " was supplied, and " + "have location supporting port forwarding " + loc); } } } } host = entity.getAttribute(Attributes.HOSTNAME); if (host != null) return HostAndPort.fromParts(host, port); throw new IllegalStateException( "Cannot find way to access port " + port + " on " + entity + " from Brooklyn (no host.name)"); }
@SuppressWarnings({"unchecked", "deprecation"}) protected void startWithChefSoloAsync() { String baseDir = MachineLifecycleEffectorTasks.resolveOnBoxDir( entity(), Machines.findUniqueMachineLocation(entity().getLocations(), SshMachineLocation.class) .get()); String installDir = Urls.mergePaths(baseDir, "installs/chef"); @SuppressWarnings("rawtypes") Map<String, String> cookbooks = (Map) ConfigBag.newInstance(entity().getConfig(CHEF_COOKBOOK_URLS)) .putIfAbsent(entity().getConfig(CHEF_COOKBOOKS)) .getAllConfig(); if (cookbooks.isEmpty()) log.warn("No cookbook_urls set for " + entity() + "; launch will likely fail subsequently"); DynamicTasks.queue( ChefSoloTasks.installChef(installDir, false), ChefSoloTasks.installCookbooks(installDir, cookbooks, false)); // TODO chef for and run a prestart recipe if necessary // TODO open ports String primary = getPrimaryCookbook(); // put all config under brooklyn/cookbook/config Navigator<MutableMap<Object, Object>> attrs = Jsonya.newInstancePrimitive().at("brooklyn"); if (Strings.isNonBlank(primary)) attrs.at(primary); attrs.at("config"); attrs.put(entity().getAllConfigBag().getAllConfig()); // and put launch attrs at root try { attrs .root() .put( (Map<?, ?>) Tasks.resolveDeepValue( entity().getConfig(CHEF_LAUNCH_ATTRIBUTES), Object.class, entity().getExecutionContext())); } catch (Exception e) { Exceptions.propagate(e); } Collection<? extends String> runList = entity().getConfig(CHEF_LAUNCH_RUN_LIST); if (runList == null) runList = entity().getConfig(CHEF_RUN_LIST); if (runList == null) { if (Strings.isNonBlank(primary)) runList = ImmutableList.of(primary + "::" + "start"); else throw new IllegalStateException( "Require a primary cookbook or a run_list to effect " + "start" + " on " + entity()); } String runDir = Urls.mergePaths( baseDir, "apps/" + entity().getApplicationId() + "/chef/entities/" + entity().getEntityType().getSimpleName() + "_" + entity().getId()); DynamicTasks.queue( ChefSoloTasks.buildChefFile( runDir, installDir, "launch", runList, (Map<String, Object>) attrs.root().get())); DynamicTasks.queue( ChefSoloTasks.runChef(runDir, "launch", entity().getConfig(CHEF_RUN_CONVERGE_TWICE))); }
protected void doStop(ConfigBag parameters, Callable<StopMachineDetails<Integer>> stopTask) { preStopConfirmCustom(); log.info("Stopping {} in {}", entity(), entity().getLocations()); StopMode stopMachineMode = getStopMachineMode(parameters); StopMode stopProcessMode = parameters.get(StopSoftwareParameters.STOP_PROCESS_MODE); DynamicTasks.queue("pre-stop", new PreStopCustomTask()); // BROOKLYN-263: // With this change the stop effector will wait for Location to provision so it can terminate // the machine, if a provisioning request is in-progress. // // The ProvisionMachineTask stores transient internal state in PROVISIONING_TASK_STATE and // PROVISIONED_MACHINE: it records when the provisioning is running and when done; and it // records the final machine. We record the machine in the internal sensor (rather than // just relying on getLocations) because the latter is set much later in the start() // process. // // This code is a big improvement (previously there was a several-minute window in some // clouds where a call to stop() would leave the machine running). // // However, there are still races. If the start() code has not yet reached the call to // location.obtain() then we won't wait, and the start() call won't know to abort. It's // fiddly to get that right, because we need to cope with restart() - so we mustn't leave // any state behind that will interfere with subsequent sequential calls to start(). // There is some attempt to handle it by ProvisionMachineTask checking if the expectedState // is stopping/stopped. Maybe<MachineLocation> machine = Machines.findUniqueMachineLocation(entity().getLocations()); ProvisioningTaskState provisioningState = entity().sensors().get(AttributesInternal.INTERNAL_PROVISIONING_TASK_STATE); if (machine.isAbsent() && provisioningState == ProvisioningTaskState.RUNNING) { Duration maxWait = entity().config().get(STOP_WAIT_PROVISIONING_TIMEOUT); log.info( "When stopping {}, waiting for up to {} for the machine to finish provisioning, before terminating it", entity(), maxWait); boolean success = Repeater.create("Wait for a machine to appear") .until( new Callable<Boolean>() { @Override public Boolean call() throws Exception { ProvisioningTaskState state = entity() .sensors() .get(AttributesInternal.INTERNAL_PROVISIONING_TASK_STATE); return (state != ProvisioningTaskState.RUNNING); } }) .backoffTo(Duration.FIVE_SECONDS) .limitTimeTo(maxWait) .run(); if (!success) { log.warn( "When stopping {}, timed out after {} waiting for the machine to finish provisioning - machine may we left running", entity(), maxWait); } machine = Maybe.ofDisallowingNull(entity().sensors().get(INTERNAL_PROVISIONED_MACHINE)); } entity().sensors().remove(AttributesInternal.INTERNAL_PROVISIONING_TASK_STATE); entity().sensors().remove(INTERNAL_PROVISIONED_MACHINE); Task<List<?>> stoppingProcess = null; if (canStop(stopProcessMode, entity())) { stoppingProcess = Tasks.parallel( "stopping", Tasks.create("stopping (process)", new StopProcessesAtMachineTask()), Tasks.create("stopping (feeds)", new StopFeedsAtMachineTask())); DynamicTasks.queue(stoppingProcess); } Task<StopMachineDetails<Integer>> stoppingMachine = null; if (canStop(stopMachineMode, machine.isAbsent())) { // Release this machine (even if error trying to stop process - we rethrow that after) Map<String, Object> stopMachineFlags = MutableMap.of(); if (Entitlements.getEntitlementContext() != null) { stopMachineFlags.put( "tags", MutableSet.of( BrooklynTaskTags.tagForEntitlement(Entitlements.getEntitlementContext()))); } Task<StopMachineDetails<Integer>> stopMachineTask = Tasks.<StopMachineDetails<Integer>>builder() .displayName("stopping (machine)") .body(stopTask) .flags(stopMachineFlags) .build(); stoppingMachine = DynamicTasks.queue(stopMachineTask); DynamicTasks.drain(entity().getConfig(STOP_PROCESS_TIMEOUT), false); // shutdown the machine if stopping process fails or takes too long synchronized (stoppingMachine) { // task also used as mutex by DST when it submits it; ensure it only submits once! if (!stoppingMachine.isSubmitted()) { // force the stoppingMachine task to run by submitting it here StringBuilder msg = new StringBuilder("Submitting machine stop early in background for ") .append(entity()); if (stoppingProcess == null) { msg.append(". Process stop skipped, pre-stop not finished?"); } else { msg.append(" because process stop has ") .append((stoppingProcess.isDone() ? "finished abnormally" : "not finished")); } log.warn(msg.toString()); Entities.submit(entity(), stoppingMachine); } } } try { // This maintains previous behaviour of silently squashing any errors on the stoppingProcess // task if the // stoppingMachine exits with a nonzero value boolean checkStopProcesses = (stoppingProcess != null && (stoppingMachine == null || stoppingMachine.get().value == 0)); if (checkStopProcesses) { // TODO we should test for destruction above, not merely successful "stop", as things like // localhost and ssh won't be destroyed DynamicTasks.waitForLast(); if (machine.isPresent()) { // throw early errors *only if* there is a machine and we have not destroyed it stoppingProcess.get(); } } } catch (Throwable e) { ServiceStateLogic.setExpectedState(entity(), Lifecycle.ON_FIRE); Exceptions.propagate(e); } entity().sensors().set(SoftwareProcess.SERVICE_UP, false); ServiceStateLogic.setExpectedState(entity(), Lifecycle.STOPPED); DynamicTasks.queue("post-stop", new PostStopCustomTask()); if (log.isDebugEnabled()) log.debug("Stopped software process entity " + entity()); }
public void run() { log.info("Starting {} on machine {}", entity(), machine); Collection<Location> oldLocs = entity().getLocations(); if (!oldLocs.isEmpty()) { List<MachineLocation> oldSshLocs = ImmutableList.copyOf(Iterables.filter(oldLocs, MachineLocation.class)); if (!oldSshLocs.isEmpty()) { // check if existing locations are compatible log.debug( "Entity " + entity() + " had machine locations " + oldSshLocs + " when starting at " + machine + "; checking if they are compatible"); for (MachineLocation oldLoc : oldSshLocs) { // machines are deemed compatible if hostname and address are the same, or they are // localhost // this allows a machine create by jclouds to then be defined with an ip-based spec if (!"localhost".equals(machine.getConfig(AbstractLocation.ORIGINAL_SPEC))) { checkLocationParametersCompatible( machine, oldLoc, "hostname", oldLoc.getAddress().getHostName(), machine.getAddress().getHostName()); checkLocationParametersCompatible( machine, oldLoc, "address", oldLoc.getAddress().getHostAddress(), machine.getAddress().getHostAddress()); } } log.debug( "Entity " + entity() + " old machine locations " + oldSshLocs + " were compatible, removing them to start at " + machine); entity().removeLocations(oldSshLocs); } } entity().addLocations(ImmutableList.of((Location) machine)); // elsewhere we rely on (public) hostname being set _after_ subnet_hostname // (to prevent the tiny possibility of races resulting in hostname being returned // simply because subnet is still being looked up) Maybe<String> lh = Machines.getSubnetHostname(machine); Maybe<String> la = Machines.getSubnetIp(machine); if (lh.isPresent()) entity().sensors().set(Attributes.SUBNET_HOSTNAME, lh.get()); if (la.isPresent()) entity().sensors().set(Attributes.SUBNET_ADDRESS, la.get()); entity().sensors().set(Attributes.HOSTNAME, machine.getAddress().getHostName()); entity().sensors().set(Attributes.ADDRESS, machine.getAddress().getHostAddress()); if (machine instanceof SshMachineLocation) { SshMachineLocation sshMachine = (SshMachineLocation) machine; UserAndHostAndPort sshAddress = UserAndHostAndPort.fromParts( sshMachine.getUser(), sshMachine.getAddress().getHostName(), sshMachine.getPort()); // FIXME: Who or what is SSH_ADDRESS intended for? It's not necessarily the address that // the SshMachineLocation is using for ssh connections (because it accepts SSH_HOST as an // override). entity().sensors().set(Attributes.SSH_ADDRESS, sshAddress); } if (Boolean.TRUE.equals(entity().getConfig(SoftwareProcess.OPEN_IPTABLES))) { if (machine instanceof SshMachineLocation) { @SuppressWarnings("unchecked") Iterable<Integer> inboundPorts = (Iterable<Integer>) machine.config().get(CloudLocationConfig.INBOUND_PORTS); machineInitTasks.openIptablesAsync(inboundPorts, (SshMachineLocation) machine); } else { log.warn("Ignoring flag OPEN_IPTABLES on non-ssh location {}", machine); } } if (Boolean.TRUE.equals(entity().getConfig(SoftwareProcess.STOP_IPTABLES))) { if (machine instanceof SshMachineLocation) { machineInitTasks.stopIptablesAsync((SshMachineLocation) machine); } else { log.warn("Ignoring flag STOP_IPTABLES on non-ssh location {}", machine); } } if (Boolean.TRUE.equals(entity().getConfig(SoftwareProcess.DONT_REQUIRE_TTY_FOR_SUDO))) { if (machine instanceof SshMachineLocation) { machineInitTasks.dontRequireTtyForSudoAsync((SshMachineLocation) machine); } else { log.warn("Ignoring flag DONT_REQUIRE_TTY_FOR_SUDO on non-ssh location {}", machine); } } resolveOnBoxDir(entity(), machine); preStartCustom(machine); }