@Override public Integer executeNativeOrPsCommand( Map flags, String regularCommand, String powerShellCommand, String phase, Boolean allowNoOp) { if (Strings.isBlank(regularCommand) && Strings.isBlank(powerShellCommand)) { if (allowNoOp) { return new WinRmToolResponse("", "", 0).getStatusCode(); } else { throw new IllegalStateException( String.format("Exactly one of cmd or psCmd must be set for %s of %s", phase, entity)); } } else if (!Strings.isBlank(regularCommand) && !Strings.isBlank(powerShellCommand)) { throw new IllegalStateException( String.format( "%s and %s cannot both be set for %s of %s", regularCommand, powerShellCommand, phase, entity)); } ByteArrayOutputStream stdIn = new ByteArrayOutputStream(); ByteArrayOutputStream stdOut = flags.get("out") != null ? (ByteArrayOutputStream) flags.get("out") : new ByteArrayOutputStream(); ByteArrayOutputStream stdErr = flags.get("err") != null ? (ByteArrayOutputStream) flags.get("err") : new ByteArrayOutputStream(); Task<?> currentTask = Tasks.current(); if (currentTask != null) { if (BrooklynTaskTags.stream(Tasks.current(), BrooklynTaskTags.STREAM_STDIN) == null) { writeToStream(stdIn, Strings.isBlank(regularCommand) ? powerShellCommand : regularCommand); Tasks.addTagDynamically( BrooklynTaskTags.tagForStreamSoft(BrooklynTaskTags.STREAM_STDIN, stdIn)); } if (BrooklynTaskTags.stream(currentTask, BrooklynTaskTags.STREAM_STDOUT) == null) { Tasks.addTagDynamically( BrooklynTaskTags.tagForStreamSoft(BrooklynTaskTags.STREAM_STDOUT, stdOut)); flags.put("out", stdOut); Tasks.addTagDynamically( BrooklynTaskTags.tagForStreamSoft(BrooklynTaskTags.STREAM_STDERR, stdErr)); flags.put("err", stdErr); } } WinRmToolResponse response; if (Strings.isBlank(regularCommand)) { response = getLocation().executePsScript(ImmutableList.of(powerShellCommand)); } else { response = getLocation().executeCommand(ImmutableList.of(regularCommand)); } if (currentTask != null) { writeToStream(stdOut, response.getStdOut()); writeToStream(stdErr, response.getStdErr()); } return response.getStatusCode(); }
@Override public final T get() { if (log.isDebugEnabled()) log.debug("Queuing task to resolve " + dsl + ", called by " + Tasks.current()); EntityInternal entity = (EntityInternal) BrooklynTaskTags.getTargetOrContextEntity(Tasks.current()); ExecutionContext exec = (entity != null) ? entity.getExecutionContext() : BasicExecutionContext.getCurrentExecutionContext(); if (exec == null) { throw new IllegalStateException("No execution context available to resolve " + dsl); } Task<T> task = newTask(); T result; try { result = exec.submit(task).get(); } catch (InterruptedException | ExecutionException e) { Task<?> currentTask = Tasks.current(); if (currentTask != null && currentTask.isCancelled()) { task.cancel(true); } throw Exceptions.propagate(e); } if (log.isDebugEnabled()) log.debug("Resolved " + result + " from " + dsl); return result; }
/** * Default restart implementation for an entity. * * <p>Stops processes if possible, then starts the entity again. */ public void restart(ConfigBag parameters) { ServiceStateLogic.setExpectedState(entity(), Lifecycle.STOPPING); RestartMachineMode isRestartMachine = parameters.get(RestartSoftwareParameters.RESTART_MACHINE_TYPED); if (isRestartMachine == null) isRestartMachine = RestartMachineMode.AUTO; if (isRestartMachine == RestartMachineMode.AUTO) isRestartMachine = getDefaultRestartStopsMachine() ? RestartMachineMode.TRUE : RestartMachineMode.FALSE; // Calling preStopCustom without a corresponding postStopCustom invocation // doesn't look right so use a separate callback pair; Also depending on the arguments // stop() could be called which will call the {pre,post}StopCustom on its own. DynamicTasks.queue("pre-restart", new PreRestartTask()); if (isRestartMachine == RestartMachineMode.FALSE) { DynamicTasks.queue("stopping (process)", new StopProcessesAtMachineTask()); } else { Map<String, Object> stopMachineFlags = MutableMap.of(); if (Entitlements.getEntitlementContext() != null) { stopMachineFlags.put( "tags", MutableSet.of( BrooklynTaskTags.tagForEntitlement(Entitlements.getEntitlementContext()))); } Task<String> stopTask = Tasks.<String>builder() .displayName("stopping (machine)") .body(new StopMachineTask()) .flags(stopMachineFlags) .build(); DynamicTasks.queue(stopTask); } DynamicTasks.queue("starting", new StartInLocationsTask()); restartChildren(parameters); DynamicTasks.queue("post-restart", new PostRestartTask()); DynamicTasks.waitForLast(); ServiceStateLogic.setExpectedState(entity(), Lifecycle.RUNNING); }
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()); }
protected EntityInternal entity() { return (EntityInternal) BrooklynTaskTags.getTargetOrContextEntity(Tasks.current()); }
@SuppressWarnings({"rawtypes", "unchecked"}) @Override public V call() { T value = source.getAttribute(sensor); // return immediately if either the ready predicate or the abort conditions hold if (ready(value)) return postProcess(value); final List<Exception> abortionExceptions = Lists.newCopyOnWriteArrayList(); long start = System.currentTimeMillis(); for (AttributeAndSensorCondition abortCondition : abortSensorConditions) { Object abortValue = abortCondition.source.getAttribute(abortCondition.sensor); if (abortCondition.predicate.apply(abortValue)) { abortionExceptions.add( new Exception( "Abort due to " + abortCondition.source + " -> " + abortCondition.sensor)); } } if (abortionExceptions.size() > 0) { throw new CompoundRuntimeException( "Aborted waiting for ready from " + source + " " + sensor, abortionExceptions); } TaskInternal<?> current = (TaskInternal<?>) Tasks.current(); if (current == null) throw new IllegalStateException("Should only be invoked in a running task"); Entity entity = BrooklynTaskTags.getTargetOrContextEntity(current); if (entity == null) throw new IllegalStateException( "Should only be invoked in a running task with an entity tag; " + current + " has no entity tag (" + current.getStatusDetail(false) + ")"); final LinkedList<T> publishedValues = new LinkedList<T>(); final Semaphore semaphore = new Semaphore(0); // could use Exchanger SubscriptionHandle subscription = null; List<SubscriptionHandle> abortSubscriptions = Lists.newArrayList(); try { subscription = entity .subscriptions() .subscribe( source, sensor, new SensorEventListener<T>() { @Override public void onEvent(SensorEvent<T> event) { synchronized (publishedValues) { publishedValues.add(event.getValue()); } semaphore.release(); } }); for (final AttributeAndSensorCondition abortCondition : abortSensorConditions) { abortSubscriptions.add( entity .subscriptions() .subscribe( abortCondition.source, abortCondition.sensor, new SensorEventListener<Object>() { @Override public void onEvent(SensorEvent<Object> event) { if (abortCondition.predicate.apply(event.getValue())) { abortionExceptions.add( new Exception( "Abort due to " + abortCondition.source + " -> " + abortCondition.sensor)); semaphore.release(); } } })); Object abortValue = abortCondition.source.getAttribute(abortCondition.sensor); if (abortCondition.predicate.apply(abortValue)) { abortionExceptions.add( new Exception( "Abort due to " + abortCondition.source + " -> " + abortCondition.sensor)); } } if (abortionExceptions.size() > 0) { throw new CompoundRuntimeException( "Aborted waiting for ready from " + source + " " + sensor, abortionExceptions); } CountdownTimer timer = timeout != null ? timeout.countdownTimer() : null; Duration maxPeriod = ValueResolver.PRETTY_QUICK_WAIT; Duration nextPeriod = ValueResolver.REAL_QUICK_PERIOD; while (true) { // check the source on initial run (could be done outside the loop) // and also (optionally) on each iteration in case it is more recent value = source.getAttribute(sensor); if (ready(value)) break; if (timer != null) { if (timer.getDurationRemaining().isShorterThan(nextPeriod)) { nextPeriod = timer.getDurationRemaining(); } if (timer.isExpired()) { if (onTimeout.isPresent()) return onTimeout.get(); throw new RuntimeTimeoutException("Unsatisfied after " + Duration.sinceUtc(start)); } } String prevBlockingDetails = current.setBlockingDetails(blockingDetails); try { if (semaphore.tryAcquire(nextPeriod.toMilliseconds(), TimeUnit.MILLISECONDS)) { // immediately release so we are available for the next check semaphore.release(); // if other permits have been made available (e.g. multiple notifications) drain them // all as no point running multiple times semaphore.drainPermits(); } } finally { current.setBlockingDetails(prevBlockingDetails); } // check any subscribed values which have come in first while (true) { synchronized (publishedValues) { if (publishedValues.isEmpty()) break; value = publishedValues.pop(); } if (ready(value)) break; } // if unmanaged then ignore the other abort conditions if (!ignoreUnmanaged && Entities.isNoLongerManaged(entity)) { if (onUnmanaged.isPresent()) return onUnmanaged.get(); throw new NotManagedException(entity); } if (abortionExceptions.size() > 0) { throw new CompoundRuntimeException( "Aborted waiting for ready from " + source + " " + sensor, abortionExceptions); } nextPeriod = nextPeriod.times(2).upperBound(maxPeriod); } if (LOG.isDebugEnabled()) LOG.debug("Attribute-ready for {} in entity {}", sensor, source); return postProcess(value); } catch (InterruptedException e) { throw Exceptions.propagate(e); } finally { if (subscription != null) { entity.subscriptions().unsubscribe(subscription); } for (SubscriptionHandle handle : abortSubscriptions) { entity.subscriptions().unsubscribe(handle); } } }
/** * Returns the current management context; for use in implementations of {@link #get()} that are * not associated with an entity. */ protected static final ManagementContextInternal managementContext() { return (ManagementContextInternal) BrooklynTaskTags.getManagementContext(Tasks.current()); }