public function getProgramIsRunning($programName) { // what are we doing? $log = usingLog()->startAction("is program '{$programName}' running under supervisor on host '{$this->args[0]}'?"); // get the host details $hostDetails = $this->getHostDetails(); //run the supervisorctl command $result = usingHost($hostDetails->hostId)->runCommandAndIgnoreErrors("sudo supervisorctl status"); // |egrep '^$programName' | awk '{print \\$2}'"); // did the command succeed? if ($result->didCommandFail()) { $msg = "command failed with return code '{$result->returnCode}' and output '{$result->output}'"; $log->endAction($msg); throw new E5xx_ActionFailed(__METHOD__); } // reduce the output down $lines = explode("\n", $result->output); $lines = FilterForMatchingRegex::against($lines, "/^{$programName} /"); $lines = FilterColumns::from($lines, "1", ' '); if (empty($lines)) { $log->endAction("supervisor does not know about '{$programName}'"); return false; } // what happened? if ($lines[0] == 'RUNNING') { $log->endAction('current status is RUNNING'); return true; } // if we get here, then the program is not RUNNING, and we // treat that as a failure $log->endAction('current status is ' . $lines[0]); return false; }
public function stopProgram($programName) { // what are we doing? $log = usingLog()->startAction("stop program '{$programName}' on host '{$this->args[0]}'"); // get the host details $hostDetails = $this->getHostDetails(); // stop the program $result = usingHost($hostDetails->hostId)->runCommand("sudo supervisorctl stop '{$programName}'"); // did the command succeed? if ($result->didCommandFail()) { throw new E5xx_ActionFailed(__METHOD__, "failed to start process '{$programName} (via supervisord)'"); } // all done $log->endAction(); return true; }
}); // ======================================================================== // // POSSIBLE ACTION(S) // // ------------------------------------------------------------------------ $story->addAction(function () { // make sure all the sessions are running first $checkpoint = getCheckpoint(); foreach (hostWithRole('host_target') as $hostId) { foreach ($checkpoint->sessions as $session) { expectsHost($hostId)->screenIsRunning($session); } } foreach (hostWithRole('host_target') as $hostId) { usingHost($hostId)->stopAllScreens(); } }); // ======================================================================== // // POST-TEST INSPECTION // // ------------------------------------------------------------------------ $story->addPostTestInspection(function () { $checkpoint = getCheckpoint(); foreach (hostWithRole('host_target') as $hostId) { foreach ($checkpoint->sessions as $session) { expectsHost($hostId)->screenIsNotRunning($session); } } });
// ======================================================================== // // STORY SETUP / TEAR-DOWN // // ------------------------------------------------------------------------ $story->addTestSetup(function () { // cleanup after ourselves foreach (hostWithRole('upload_target') as $hostname) { usingHost($hostname)->uploadFile(__DIR__ . '/testfile.txt', "testfile.txt"); } }); $story->addTestTeardown(function () { // cleanup after ourselves foreach (hostWithRole('upload_target') as $hostname) { // remove the file from the test environment usingHost($hostname)->runCommand("if [[ -e testfile.txt ]] ; then rm -f testfile.txt ; fi"); // remove the file from our computer too $filename = '/tmp/testfile-' . $hostname . '.txt'; if (file_exists($filename)) { // tidy up unlink($filename); } } }); // ======================================================================== // // POSSIBLE ACTION(S) // // ------------------------------------------------------------------------ $story->addAction(function () { foreach (hostWithRole('upload_target') as $hostname) {
$checkpoint = getCheckpoint(); // if we've left the session running, go and kill it off foreach (hostWithRole('host_target') as $hostId) { $details = fromHost($hostId)->getScreenSessionDetails($checkpoint->session); if ($details) { usingHost($hostId)->stopProcess($details->pid); } } }); // ======================================================================== // // POSSIBLE ACTION(S) // // ------------------------------------------------------------------------ $story->addAction(function () { $checkpoint = getCheckpoint(); foreach (hostWithRole('host_target') as $hostId) { usingHost($hostId)->startInScreen($checkpoint->session, "top"); } }); // ======================================================================== // // POST-TEST INSPECTION // // ------------------------------------------------------------------------ $story->addPostTestInspection(function () { $checkpoint = getCheckpoint(); foreach (hostWithRole('host_target') as $hostId) { expectsHost($hostId)->screenIsRunning($checkpoint->session); } });
/** * * @param stdClass $groupDef * @param array $provisioningVars * @return void */ public function createHost($groupDef, $provisioningVars = array()) { // what are we doing? $log = usingLog()->startAction('create new VM'); // make sure we're happy with this group $this->checkGroupDefinition($groupDef); // where is the action? $baseFolder = $this->getVagrantDir($groupDef); // make sure we're happy with details about the machine foreach ($groupDef->details->machines as $hostId => $machine) { // TODO: it would be great to autodetect this one day if (!isset($machine->osName)) { throw new E5xx_ActionFailed(__METHOD__, "missing groupDef->details->machines['{$hostId}']->osName"); } if (!isset($machine->roles)) { throw new E5xx_ActionFailed(__METHOD__, "missing groupDef->details->machines['{$hostId}']->roles"); } } // make sure the VM is stopped, if it is running $log->addStep('stop vagrant VM in ' . $baseFolder . ' if already running', function () use($baseFolder) { $command = "vagrant destroy --force"; $this->runCommandAgainstHostManager($baseFolder, $command); }); // remove any existing hosts table entry foreach ($groupDef->details->machines as $hostId => $machine) { usingHostsTable()->removeHost($hostId); // remove any roles usingRolesTable()->removeHostFromAllRoles($hostId); } // work out which network interface to use $this->setVagrantBridgedInterface(); // let's start the VM $command = "vagrant up"; $result = $log->addStep('create vagrant VM(s) in ' . $baseFolder, function () use($baseFolder, $command) { return $this->runCommandAgainstHostManager($baseFolder, $command); }); // did it work? if ($result->returnCode != 0) { $log->endAction("VM failed to start or provision :("); throw new E5xx_ActionFailed(__METHOD__); } // yes it did!! // store the details foreach ($groupDef->details->machines as $hostId => $machine) { // we want all the details from the config file $vmDetails = clone $machine; // this allows the story to perform actions against a single // machine if required $vmDetails->type = 'VagrantVm'; // new in v2.x: // // when provisioning a folder of vagrant vms, we now use // the same name for the VM that vagrant uses $vmDetails->hostId = $hostId; // remember where the machine lives $vmDetails->dir = $baseFolder; // we need to remember how to SSH into the box $vmDetails->sshUsername = '******'; $vmDetails->sshKeyFile = $this->determinePrivateKey($vmDetails); $vmDetails->sshOptions = ["-i '" . $vmDetails->sshKeyFile . "'", "-o StrictHostKeyChecking=no", "-o UserKnownHostsFile=/dev/null", "-o LogLevel=quiet"]; $vmDetails->scpOptions = ["-i '" . $vmDetails->sshKeyFile . "'", "-o StrictHostKeyChecking=no"]; // remember how to connect to the machine via the network $vmDetails->ipAddress = $this->determineIpAddress($vmDetails); $vmDetails->hostname = $this->determineHostname($vmDetails); // mark the box as provisioned // we will use this in stopBox() to avoid destroying VMs that failed // to provision $vmDetails->provisioned = true; // remember this vm, now that it is running usingHostsTable()->addHost($vmDetails->hostId, $vmDetails); foreach ($vmDetails->roles as $role) { usingRolesTable()->addHostToRole($vmDetails, $role); } // now, let's get this VM into our SSH known_hosts file, to avoid // prompting people when we try and provision this VM $log->addStep("get the VM into the SSH known_hosts file", function () use($vmDetails) { usingHost($vmDetails->hostId)->runCommand("ls"); }); } // all done $log->endAction(); }
$checkpoint = getCheckpoint(); // if we've left the session running, go and kill it off foreach (hostWithRole('host_target') as $hostId) { $details = fromHost($hostId)->getScreenSessionDetails($checkpoint->session); if ($details) { usingHost($hostId)->stopProcess($details->pid); } } }); // ======================================================================== // // POSSIBLE ACTION(S) // // ------------------------------------------------------------------------ $story->addAction(function () { $checkpoint = getCheckpoint(); foreach (hostWithRole('host_target') as $hostId) { usingHost($hostId)->stopInScreen($checkpoint->session); } }); // ======================================================================== // // POST-TEST INSPECTION // // ------------------------------------------------------------------------ $story->addPostTestInspection(function () { $checkpoint = getCheckpoint(); foreach (hostWithRole('host_target') as $hostId) { expectsHost($hostId)->screenIsNotRunning($checkpoint->session); } });
/** * @return array<object> */ public function getAllScreenSessions() { // what are we doing? $log = usingLog()->startAction("get details about all screen sessions on host '{$this->args[0]}'"); // are there any details? $cmd = "screen -ls"; $result = usingHost($this->args[0])->runCommandAndIgnoreErrors($cmd); // NOTE: // // screen is not a well-behaved UNIX program, and its exit code // can be non-zero when everything is good if (empty($result->output)) { $msg = "unable to get list of screen sessions"; $log->endAction($msg); return []; } // reduce the output down to a list of sessions $lines = explode("\n", $result->output); $lines = FilterForMatchingRegex::against($lines, "/[0-9]+.+\t/"); if (empty($lines)) { $msg = "no screen processes running"; $log->endAction($msg); return []; } $retval = []; foreach ($lines as $line) { $parts = explode('.', $line); $processDetails = new BaseObject(); $processDetails->hostId = $this->args[0]; $processDetails->type = 'screen'; $processDetails->pid = trim($parts[0]); $processDetails->name = rtrim($parts[1]); $retval[] = $processDetails; } // all done $log->endAction("found " . count($retval) . " screen process(es)"); // all done return $retval; }
// // POSSIBLE ACTION(S) // // ------------------------------------------------------------------------ $story->addAction(function () { foreach (hostWithRole('upload_target') as $hostname) { usingHost($hostname)->uploadFile(__DIR__ . '/testfile.txt', "testfile.txt"); } }); // ======================================================================== // // POST-TEST INSPECTION // // ------------------------------------------------------------------------ $story->addPostTestInspection(function () { foreach (hostWithRole('upload_target') as $hostname) { $result = usingHost($hostname)->runCommand('ls testfile.txt'); $fileFound = false; $lines = explode("\n", $result->output); foreach ($lines as $line) { if ($line == "testfile.txt") { $fileFound = true; } } if (!$fileFound) { $msg = "file not found on host '{$hostname}'"; usingLog()->writeToLog($msg); usingErrors()->throwException($msg); } } });
/** * * @param VagrantVmDetails $vmDetails * @param array $provisioningVars * @return void */ public function createHost($vmDetails, $provisioningVars = array()) { // what are we doing? $log = usingLog()->startAction('provision new VM'); // make sure we like the provided details foreach (array('name', 'osName', 'homeFolder') as $param) { if (!isset($vmDetails->{$param})) { throw new E5xx_ActionFailed(__METHOD__, "missing vmDetails['{$param}']"); } } // make sure the folder exists $config = $this->st->getConfig(); if (!isset($config->storyplayer->modules->vagrant)) { throw new E5xx_ActionFailed(__METHOD__, "'vagrant' section missing in your storyplayer.json config file"); } if (!isset($config->storyplayer->modules->vagrant->dir)) { throw new E5xx_ActionFailed(__METHOD__, "'dir' setting missing from 'vagrant' section of your storyplayer.json config file"); } $pathToHomeFolder = $config->storyplayer->modules->vagrant->dir . '/' . $vmDetails->homeFolder; if (!is_dir($pathToHomeFolder)) { throw new E5xx_ActionFailed(__METHOD__, "VM dir '{$pathToHomeFolder}' does not exist"); } // remember where the Vagrantfile is $vmDetails->dir = $pathToHomeFolder; // make sure the VM is stopped, if it is running $log->addStep("stop vagrant VM in '{$pathToHomeFolder}' if already running", function () use($vmDetails) { $command = "vagrant destroy --force"; $this->runCommandAgainstHostManager($vmDetails, $command); }); // remove any existing hosts table entry usingHostsTable()->removeHost($vmDetails->hostId); // remove any roles usingRolesTable()->removeHostFromAllRoles($vmDetails->hostId); // let's start the VM $command = "vagrant up"; $result = $log->addStep("create vagrant VM in '{$pathToHomeFolder}'", function () use($command, $vmDetails) { return $this->runCommandAgainstHostManager($vmDetails, $command); }); // did it work? if ($result->returnCode !== 0) { $log->endAction("VM failed to start or provision :("); throw new E5xx_ActionFailed(__METHOD__); } // yes it did!! // now, we need to know how to contact this VM $vmDetails->ipAddress = $this->determineIpAddress($vmDetails); $vmDetails->hostname = $this->determineHostname($vmDetails); // mark the box as provisioned // we will use this in stopBox() to avoid destroying VMs that failed // to provision $vmDetails->provisioned = true; // remember this vm, now that it is running usingHostsTable()->addHost($vmDetails->hostId, $vmDetails); // now, let's get this VM into our SSH known_hosts file, to avoid // prompting people when we try and provision this VM $log->addStep("get the VM into the SSH known_hosts file", function () use($vmDetails) { usingHost($vmDetails->hostId)->runCommand("ls"); }); // all done $log->endAction("VM successfully started; IP address is {$vmDetails->ipAddress}"); }
public function provisionHosts(ProvisioningDefinition $hosts, $provConfig) { // what are we doing? $log = usingLog()->startAction("use dsbuild to provision host(s)"); // the params file that we are going to output $dsbuildParams = new BaseObject(); // build up the list of settings to write out foreach ($hosts as $hostId => $hostProps) { // what is the host's IP address? $ipAddress = fromHost($hostId)->getIpAddress(); $propName = $hostId . '_ipv4Address'; $dsbuildParams->{$propName} = $ipAddress; if (isset($hostProps->params)) { $dsbuildParams->mergeFrom($hostProps->params); } } // add in all the config settings that we know about $dsbuildParams->storyplayer_ipv4Address = fromConfig()->get('storyplayer.ipAddress'); $dsbuildParams->mergeFrom($this->flattenData($this->st->getActiveConfig()->getData(''))); // write them out $this->writeDsbuildParamsShellFile((array) $dsbuildParams); $this->writeDsbuildParamsYamlFile((array) $dsbuildParams); // at this point, we are ready to attempt provisioning // // provision each host in the order that they're listed foreach ($hosts as $hostId => $hostProps) { // which dsbuildfile are we going to run? $hostDir = fromHost($hostId)->getLocalFolder(); $dsbuildFilename = $this->getDsbuildFilename($hostDir, $provConfig, $hostId); if ($dsbuildFilename === null) { // there is no dsbuildfile at all to run $log->endAction("cannot find dsbuildfile to run :("); throw new E5xx_ActionFailed(__METHOD__, "no dsbuildfile to run"); } // at this point, we are ready to provision $commandRunner = new CommandRunner(); // copy the dsbuildparams files to the target machine using scp // NOTE: the "vagrant rsync" command seems not working with some Vagrant provisioners (e.g. OpenStack) $command = 'scp' . ' ' . $dsbuildParams->{'hosts_' . $hostId . '_scpOptions_0'} . ' ' . $dsbuildParams->{'hosts_' . $hostId . '_scpOptions_1'} . ' dsbuildparams.*' . ' ' . $dsbuildParams->{'hosts_' . $hostId . '_sshUsername'} . '@' . $dsbuildParams->{'hosts_' . $hostId . '_ipAddress'} . ':/vagrant/'; $result = $commandRunner->runSilently($command); if (!$result->didCommandSucceed()) { // try to rsync folders in case of scp fail $command = 'vagrant rsync ' . $hostId; $commandRunner->runSilently($command); } // provision $command = 'sudo bash /vagrant/' . $dsbuildFilename; $result = usingHost($hostId)->runCommand($command); // what happened? if (!$result->didCommandSucceed()) { throw new E5xx_ActionFailed(__METHOD__, "provisioning failed"); } } // all done $log->endAction(); }
public function stopProcess($pid, $grace = 5) { // what are we doing? $log = usingLog()->startAction("stop process '{$pid}' on host '{$this->args[0]}'"); // is the process running at all? if (!fromHost($this->args[0])->getPidIsRunning($pid)) { $log->endAction("process is not running"); return; } // yes it is, so stop it // send a TERM signal to the screen session $log->addStep("send SIGTERM to process '{$pid}'", function () use($pid) { if ($this->getIsLocalhost()) { posix_kill($pid, SIGTERM); } else { usingHost($this->args[0])->runCommand("kill {$pid}"); } }); // has this worked? $isStopped = $log->addStep("wait for process to terminate", function () use($pid, $grace, $log) { for ($i = 0; $i < $grace; $i++) { if (!fromHost($this->args[0])->getPidIsRunning($pid)) { return true; } // process still exists sleep(1); } return false; }); // did the process stop? if ($isStopped) { $log->endAction(); return; } $log->addStep("send SIGKILL to process '{$pid}'", function () use($pid) { if ($this->getIsLocalhost()) { posix_kill($pid, SIGKILL); } else { usingHost($this->args[0])->runCommand("kill -9 {$pid}"); } sleep(1); }); // success? if (fromHost($this->args[0])->getProcessIsRunning($pid)) { $log->endAction("process is still running :("); throw new E5xx_ActionFailed(__METHOD__); } // all done $log->endAction("process has finished"); }