| 3319 | |
| 3320 | |
| 3321 | Future<Response> Master::Http::_startMaintenance( |
| 3322 | const RepeatedPtrField<MachineID>& machineIds, |
| 3323 | const Owned<ObjectApprovers>& approvers) const |
| 3324 | { |
| 3325 | // Validate every machine in the list. |
| 3326 | Try<Nothing> isValid = maintenance::validation::machines(machineIds); |
| 3327 | if (isValid.isError()) { |
| 3328 | return BadRequest(isValid.error()); |
| 3329 | } |
| 3330 | |
| 3331 | // Check that all machines are part of a maintenance schedule. |
| 3332 | // TODO(josephw): Allow a transition from `UP` to `DOWN`. |
| 3333 | foreach (const MachineID& id, machineIds) { |
| 3334 | if (!master->machines.contains(id)) { |
| 3335 | return BadRequest( |
| 3336 | "Machine '" + stringify(JSON::protobuf(id)) + |
| 3337 | "' is not part of a maintenance schedule"); |
| 3338 | } |
| 3339 | |
| 3340 | if (master->machines[id].info.mode() != MachineInfo::DRAINING) { |
| 3341 | return BadRequest( |
| 3342 | "Machine '" + stringify(JSON::protobuf(id)) + |
| 3343 | "' is not in DRAINING mode and cannot be brought down"); |
| 3344 | } |
| 3345 | |
| 3346 | if (!approvers->approved<START_MAINTENANCE>(id)) { |
| 3347 | return Forbidden(); |
| 3348 | } |
| 3349 | } |
| 3350 | |
| 3351 | return master->registrar->apply(Owned<RegistryOperation>( |
| 3352 | new maintenance::StartMaintenance(machineIds))) |
| 3353 | .then(defer(master->self(), [=](bool result) -> Response { |
| 3354 | // See the top comment in "master/maintenance.hpp" for why this check |
| 3355 | // is here, and is appropriate. |
| 3356 | CHECK(result); |
| 3357 | |
| 3358 | // We currently send a `ShutdownMessage` to each slave. This terminates |
| 3359 | // all the executors for all the frameworks running on that slave. |
| 3360 | // We also manually remove the slave to force sending TASK_LOST updates |
| 3361 | // for all the tasks that were running on the slave and `LostSlaveMessage` |
| 3362 | // messages to the framework. This guards against the slave having dropped |
| 3363 | // the `ShutdownMessage`. |
| 3364 | foreach (const MachineID& machineId, machineIds) { |
| 3365 | // The machine may not be in machines. This means no slaves are |
| 3366 | // currently registered on that machine so this is a no-op. |
| 3367 | if (master->machines.contains(machineId)) { |
| 3368 | // NOTE: Copies are needed because removeSlave modifies |
| 3369 | // master->machines. |
| 3370 | foreach ( |
| 3371 | const SlaveID& slaveId, |
| 3372 | utils::copy(master->machines[machineId].slaves)) { |
| 3373 | Slave* slave = master->slaves.registered.get(slaveId); |
| 3374 | CHECK_NOTNULL(slave); |
| 3375 | |
| 3376 | // Tell the slave to shut down. |
| 3377 | ShutdownMessage shutdownMessage; |
| 3378 | shutdownMessage.set_message("Operator initiated 'Machine DOWN'"); |