/** * Launches server * * @param \ServerCreateInfo $ServerCreateInfo optional The server create info * @param \DBServer $DBServer optional The DBServer object * @param bool $delayed optional * @param integer|array $reason optional * @param \Scalr_Account_User|int $user optional The Scalr_Account_User object or its unique identifier * @return DBServer|null Returns the DBServer object on cussess or null otherwise */ public static function LaunchServer(ServerCreateInfo $ServerCreateInfo = null, DBServer $DBServer = null, $delayed = false, $reason = 0, $user = null) { $db = self::getDb(); $farm = null; //Ensures handling identifier of the user instead of the object if ($user !== null && !$user instanceof \Scalr_Account_User) { try { $user = Scalr_Account_User::init()->loadById(intval($user)); } catch (\Exception $e) { } } if (!$DBServer && $ServerCreateInfo) { $ServerCreateInfo->SetProperties(array(SERVER_PROPERTIES::SZR_KEY => self::GenerateRandomKey(40), SERVER_PROPERTIES::SZR_KEY_TYPE => SZR_KEY_TYPE::ONE_TIME)); $DBServer = DBServer::Create($ServerCreateInfo, false, true); } elseif (!$DBServer && !$ServerCreateInfo) { // incorrect arguments self::getContainer()->logger(LOG_CATEGORY::FARM)->error(sprintf("Cannot create server")); return null; } else { if ($DBServer && empty($DBServer->cloudLocation)) { trigger_error('Cloud location is missing in DBServer', E_USER_WARNING); } } $propsToSet = array(); if ($user instanceof \Scalr_Account_User) { $propsToSet[SERVER_PROPERTIES::LAUNCHED_BY_ID] = $user->id; $propsToSet[SERVER_PROPERTIES::LAUNCHED_BY_EMAIL] = $user->getEmail(); } //We should keep role_id and farm_role_id in server properties to use in cost analytics if (!empty($DBServer->farmRoleId)) { $propsToSet[SERVER_PROPERTIES::FARM_ROLE_ID] = $DBServer->farmRoleId; $propsToSet[SERVER_PROPERTIES::ROLE_ID] = $DBServer->farmRoleId ? $DBServer->GetFarmRoleObject()->RoleID : 0; } try { // Ensures the farm object will be fetched as correctly as possible $farm = $DBServer->farmId ? $DBServer->GetFarmObject() : null; $farmRole = $DBServer->farmRoleId ? $DBServer->GetFarmRoleObject() : null; if (!$farmRole instanceof DBFarmRole) { $farmRole = null; } else { if (!$farm instanceof DBFarm) { // Gets farm through FarmRole object in this case $farm = $farmRole->GetFarmObject(); } } if ($farm instanceof DBFarm) { $propsToSet[SERVER_PROPERTIES::FARM_CREATED_BY_ID] = $farm->ownerId ?: $farm->GetSetting(Entity\FarmSetting::CREATED_BY_ID); $propsToSet[SERVER_PROPERTIES::FARM_CREATED_BY_EMAIL] = $farm->ownerId ? Entity\Account\User::findPk($farm->ownerId)->email : $farm->GetSetting(Entity\FarmSetting::CREATED_BY_EMAIL); $projectId = $farm->GetSetting(Entity\FarmSetting::PROJECT_ID); if (!empty($projectId)) { try { $projectEntity = ProjectEntity::findPk($projectId); if ($projectEntity instanceof ProjectEntity) { /* @var $projectEntity ProjectEntity */ $ccId = $projectEntity->ccId; } else { $projectId = null; } } catch (Exception $e) { $projectId = null; } } $propsToSet[SERVER_PROPERTIES::FARM_PROJECT_ID] = $projectId; } if ($farmRole instanceof DBFarmRole) { $role = $farmRole->GetRoleObject(); $DBServer->isScalarized = $role->isScalarized; if (!$DBServer->isScalarized) { $propsToSet[SERVER_PROPERTIES::SZR_VESION] = ''; } $DBServer->Save(); } if (!empty($ccId)) { $propsToSet[SERVER_PROPERTIES::ENV_CC_ID] = $ccId; } elseif ($DBServer->envId && ($environment = $DBServer->GetEnvironmentObject()) instanceof Scalr_Environment) { $propsToSet[SERVER_PROPERTIES::ENV_CC_ID] = $environment->getPlatformConfigValue(Scalr_Environment::SETTING_CC_ID); } } catch (Exception $e) { self::getContainer()->logger(LOG_CATEGORY::FARM)->error(sprintf("Could not load related object for recently created server %s. It says: %s", $DBServer->serverId, $e->getMessage())); } if (!empty($propsToSet)) { $DBServer->SetProperties($propsToSet); } $fnGetReason = function ($reasonId) { $args = func_get_args(); $args[0] = DBServer::getLaunchReason($reasonId); return [call_user_func_array('sprintf', $args), $reasonId]; }; if ($reason) { list($reasonMsg, $reasonId) = is_array($reason) ? call_user_func_array($fnGetReason, $reason) : $fnGetReason($reason); $DBServer->SetProperties([SERVER_PROPERTIES::LAUNCH_REASON => $reasonMsg, SERVER_PROPERTIES::LAUNCH_REASON_ID => $reasonId]); } else { $reasonMsg = $DBServer->GetProperty(SERVER_PROPERTIES::LAUNCH_REASON); $reasonId = $DBServer->GetProperty(SERVER_PROPERTIES::LAUNCH_REASON_ID); } if ($delayed) { $DBServer->updateStatus(SERVER_STATUS::PENDING_LAUNCH); return $DBServer; } if ($ServerCreateInfo && $ServerCreateInfo->roleId) { $dbRole = DBRole::loadById($ServerCreateInfo->roleId); if ($dbRole->generation == 1) { $DBServer->updateStatus(SERVER_STATUS::PENDING_LAUNCH); $DBServer->SetProperties([SERVER_PROPERTIES::LAUNCH_ERROR => "ami-scripts servers no longer supported", SERVER_PROPERTIES::LAUNCH_ATTEMPT => $DBServer->GetProperty(SERVER_PROPERTIES::LAUNCH_ATTEMPT) + 1, SERVER_PROPERTIES::LAUNCH_LAST_TRY => (new DateTime())->format('Y-m-d H:i:s')]); return $DBServer; } } // Limit amount of pending servers if ($DBServer->isOpenstack()) { $config = self::getContainer()->config; if ($config->defined("scalr.{$DBServer->platform}.pending_servers_limit")) { $pendingServersLimit = $config->get("scalr.{$DBServer->platform}.pending_servers_limit"); $pendingServers = $db->GetOne("SELECT COUNT(*) FROM servers WHERE platform=? AND status=? AND server_id != ?", array($DBServer->platform, SERVER_STATUS::PENDING, $DBServer->serverId)); if ($pendingServers >= $pendingServersLimit) { self::getContainer()->logger("SERVER_LAUNCH")->warn("{$pendingServers} servers in PENDING state on {$DBServer->platform}. Limit is: {$pendingServersLimit}. Waiting."); $DBServer->updateStatus(SERVER_STATUS::PENDING_LAUNCH); $DBServer->SetProperties([SERVER_PROPERTIES::LAUNCH_ATTEMPT => $DBServer->GetProperty(SERVER_PROPERTIES::LAUNCH_ATTEMPT) + 1, SERVER_PROPERTIES::LAUNCH_LAST_TRY => (new DateTime())->format('Y-m-d H:i:s')]); return $DBServer; } else { self::getContainer()->logger("SERVER_LAUNCH")->warn("{$pendingServers} servers in PENDING state on {$DBServer->platform}. Limit is: {$pendingServersLimit}. Launching server."); } } } try { $account = Scalr_Account::init()->loadById($DBServer->clientId); $account->validateLimit(Scalr_Limits::ACCOUNT_SERVERS, 1); PlatformFactory::NewPlatform($DBServer->platform)->LaunchServer($DBServer); $DBServer->status = SERVER_STATUS::PENDING; $DBServer->Save(); try { $DBServer->getServerHistory()->markAsLaunched($reasonMsg, $reasonId); $DBServer->updateTimelog('ts_launched'); if ($DBServer->imageId) { //Update Image last used date /* @var $server Entity\Server */ $server = Entity\Server::findPk($DBServer->serverId); $image = $server->getImage(); if ($image) { $image->update(['dtLastUsed' => new DateTime()]); } if ($server->farmRoleId && empty($server->getFarmRole())) { trigger_error(sprintf("Call to a member function getRole() on null. Server: %s, FarmRole: %d", $server->serverId, $server->farmRoleId), E_USER_WARNING); } if ($server->farmRoleId && !empty($server->getFarmRole()->getRole())) { //Update Role last used date $server->getFarmRole()->getRole()->update(['lastUsed' => new DateTime()]); } } } catch (Exception $e) { self::getContainer()->logger('SERVER_HISTORY')->error(sprintf("Cannot update servers history: {$e->getMessage()}")); } } catch (Exception $e) { self::getContainer()->logger(LOG_CATEGORY::FARM)->error(new FarmLogMessage($DBServer, sprintf("Cannot launch server on '%s' platform: %s", !empty($DBServer->platform) ? $DBServer->platform : null, $e->getMessage()))); $existingLaunchError = $DBServer->GetProperty(SERVER_PROPERTIES::LAUNCH_ERROR); $DBServer->SetProperties([SERVER_PROPERTIES::LAUNCH_ERROR => $e->getMessage(), SERVER_PROPERTIES::LAUNCH_ATTEMPT => $DBServer->GetProperty(SERVER_PROPERTIES::LAUNCH_ATTEMPT) + 1, SERVER_PROPERTIES::LAUNCH_LAST_TRY => (new DateTime())->format('Y-m-d H:i:s')]); $DBServer->updateStatus(SERVER_STATUS::PENDING_LAUNCH); if ($DBServer->farmId && !$existingLaunchError) { self::FireEvent($DBServer->farmId, new InstanceLaunchFailedEvent($DBServer, $e->getMessage())); } } if ($DBServer->status == SERVER_STATUS::PENDING) { self::FireEvent($DBServer->farmId, new BeforeInstanceLaunchEvent($DBServer)); $DBServer->SetProperty(SERVER_PROPERTIES::LAUNCH_ERROR, ""); } return $DBServer; }
/** * @test * @functional */ public function testComplex() { /* @var $farm Farm */ $farm = $this->createTestFarm('server', ['base-ubuntu1404']); /* @var $farmRole FarmRole */ $farmRole = $farm->farmRoles->current(); $server = null; static::toDelete(Farm::class, [$farm->id]); $uri = self::getUserApiUrl("/farms/{$farm->id}/actions/launch"); $this->request($uri, Request::METHOD_POST); for ($time = time(), $sleep = 50; time() - $time < 400 && (empty($server) || $server->status != Server::STATUS_RUNNING); $sleep += 50) { sleep($sleep); $testServers = $this->listServers(['farmId' => $farm->id]); if (count($testServers) > 0) { $server = reset($testServers); /* @var $server Server */ continue; } } $this->assertNotEmpty($server); $this->assertEquals($server->status, 'running'); $testDescribe = [['farmId' => $farm->id], ['farmRoleId' => $farmRole->id], ['serverId' => 'all']]; // testing describe and fetch action foreach ($testDescribe as $value) { $servers = $this->listServers($value); $serverAdapter = $this->getAdapter('server'); $filterable = $serverAdapter->getRules()[ApiEntityAdapter::RULE_TYPE_FILTERABLE]; foreach ($servers as $server) { /* @var $server Server */ foreach ($filterable as $property) { $filterValue = $server->{$property}; $listResult = $this->listServers($value, [$property => $filterValue]); if (!static::isRecursivelyEmpty($filterValue)) { foreach ($listResult as $filtered) { $this->assertEquals($filterValue, $filtered->{$property}, "Property '{$property}' mismatch"); } } } $response = $this->getServer($server->id); $this->assertEquals(200, $response->status, $this->printResponseError($response)); $dbServer = Server::findPk($server->id); $this->assertObjectEqualsEntity($response->getBody()->data, $dbServer, $serverAdapter); } } $entity = Server::findPk($server->id); /* @var $entity Server */ $DBServerObject = $entity->__getDBServer(); $oldModel = DBServer::LoadByID($server->id); $this->assertEquals($DBServerObject, $oldModel); $serverEntity = (new Server())->__getEntityFromDBServer($oldModel); $this->assertEquals($entity, $serverEntity); // testing reboot action $response = $this->rebootServer($server->id, true); $this->assertEquals(200, $response->status, $this->printResponseError($response)); $data = $response->getBody()->data; $this->assertEquals($server->id, $data->id); $propertyReboot = ServerProperty::findOne([['serverId' => $server->id], ['name' => SERVER_PROPERTIES::REBOOTING]]); /* @var $propertyReboot ServerProperty */ for ($time = time(), $sleep = 50; time() - $time < 300 && (!$propertyReboot || $propertyReboot->value); $sleep += 50) { $propertyReboot = ServerProperty::findOne([['serverId' => $server->id], ['name' => SERVER_PROPERTIES::REBOOTING]]); } $server = $this->waitForChanges($server->id, Server::STATUS_RUNNING); // testing suspend action $response = $this->suspendServer($server->id); $this->assertEquals(200, $response->status, $this->printResponseError($response)); $data = $response->getBody()->data; $this->assertEquals($server->id, $data->id); $this->assertEquals($data->status, 'pending_suspend'); $server = $this->waitForChanges($server->id, Server::STATUS_SUSPENDED, 600); // testing resume action $sleep = 0; do { try { $response = $this->resumeServer($server->id); if ($response->status == 200) { break; } } catch (Exception $e) { if (strpos($e->getMessage(), "is not in a state from which it can be started") !== false) { $sleep += 60; sleep(60); } else { throw $e; } } } while ($sleep < 300); $this->assertEquals(200, $response->status, $this->printResponseError($response)); $data = $response->getBody()->data; $this->assertEquals($server->id, $data->id); $this->assertEquals($data->status, 'resuming'); $server = $this->waitForChanges($server->id, Server::STATUS_RUNNING); }
/** * Check if given server exists, is allowed for current environment and has farm, farmRole, role * * @param string $serverId * @return Server * @throws Exception */ public function getServerEntity($serverId) { /* @var $server Server */ if (!$serverId || !($server = Server::findPk($serverId))) { throw new Exception('Server not found'); } if (empty($server->getFarm())) { throw new Exception("Farm was not found for this Server"); } if (empty($server->getFarmRole())) { throw new Exception("FarmRole was not found for this Server"); } if (empty($server->getFarmRole()->getRole())) { throw new Exception("Role was not found for this Server"); } return $server; }