public function _billingCode($from, $to, $action) { switch ($action) { case static::ACT_CONVERT_TO_OBJECT: /* @var $from ProjectEntity */ $to->billingCode = $from->getProperty(ProjectPropertyEntity::NAME_BILLING_CODE); break; case static::ACT_CONVERT_TO_ENTITY: /* @var $to ProjectEntity */ break; case static::ACT_GET_FILTER_CRITERIA: $project = new ProjectEntity(); $property = new ProjectPropertyEntity(); return [AbstractEntity::STMT_FROM => $project->table() . " LEFT JOIN " . $property->table() . " ON {$property->columnProjectId} = {$project->columnProjectId}", AbstractEntity::STMT_WHERE => "{$property->columnName} = '" . ProjectPropertyEntity::NAME_BILLING_CODE . "' AND {$property->columnValue} = " . $property->qstr('value', $from->billingCode)]; } }
public function defaultAction() { $ccs = array(); $projects = array(); foreach (CostCentreEntity::find([['archived' => 0]]) as $cc) { $ccs[$cc->ccId] = $cc->name; } foreach (ProjectEntity::find([['archived' => 0]]) as $project) { $projects[$project->projectId] = $project->name; } $this->response->page('ui/admin/analytics/notifications/view.js', array('notifications.ccs' => NotificationEntity::findBySubjectType(NotificationEntity::SUBJECT_TYPE_CC)->getArrayCopy(), 'notifications.projects' => NotificationEntity::findBySubjectType(NotificationEntity::SUBJECT_TYPE_PROJECT)->getArrayCopy(), 'reports' => ReportEntity::all()->getArrayCopy(), 'ccs' => $ccs, 'projects' => $projects), array(), array('ui/admin/analytics/notifications/view.css')); }
public function defaultAction() { $ccs = array(); $projects = array(); foreach (CostCentreEntity::find([['archived' => 0]]) as $cc) { $ccs[$cc->ccId] = $cc->name; } foreach (ProjectEntity::find([['archived' => 0]]) as $project) { $projects[$project->projectId] = $project->name; } $this->response->page('ui/analytics/notifications/view.js', array('notifications.ccs' => array('enabled' => SettingEntity::getValue(SettingEntity::ID_NOTIFICATIONS_CCS_ENABLED), 'items' => NotificationEntity::findBySubjectType(NotificationEntity::SUBJECT_TYPE_CC)), 'notifications.projects' => array('enabled' => SettingEntity::getValue(SettingEntity::ID_NOTIFICATIONS_PROJECTS_ENABLED), 'items' => NotificationEntity::findBySubjectType(NotificationEntity::SUBJECT_TYPE_PROJECT)), 'reports' => array('enabled' => SettingEntity::getValue(SettingEntity::ID_REPORTS_ENABLED), 'items' => ReportEntity::all()), 'ccs' => $ccs, 'projects' => $projects), array(), array()); }
/** * Gets specified Script taking into account both scope and authentication token * * @param string $projectId Unique identifier of the project * * @return ProjectEntity Returns the Project Entity on success * @throws ApiErrorException */ public function getProject($projectId) { /* @var $project ProjectEntity */ $project = ProjectEntity::findPk($projectId); if (!$project) { throw new ApiErrorException(404, ErrorMessage::ERR_OBJECT_NOT_FOUND, "Requested Project either does not exist or is not owned by your environment."); } if (!$this->hasPermissions($project)) { //Checks entity level write access permissions throw new ApiErrorException(403, ErrorMessage::ERR_PERMISSION_VIOLATION, "Insufficient permissions"); } return $project; }
/** * Constructor * * @param \DBFarm $farm The DBFarm instance * @param string $projectId The uuid of the project */ public function __construct(DBFarm $farm, $projectId) { parent::__construct(); $projectEntity = ProjectEntity::findPk($projectId); $this->projects[] = $projectId; if ($projectEntity) { $this->ccs[] = $projectEntity->ccId; $projectName = $projectEntity->name; } else { $projectName = AccountTagEntity::fetchName($projectId, TagEntity::TAG_ID_PROJECT); } $this->message = sprintf("User %s assigned a new farm '%s' id:%d to the project '%s'", strip_tags($this->getUserEmail()), strip_tags($farm->Name), $farm->ID, strip_tags($projectName)); $this->messageToHash = sprintf('%s|%s|%s|%s', $this->timelineEvent->dtime->format('Y-m-d'), $this->getUserEmail(), $farm->ID, $projectId); }
public function defaultAction() { $ccs = []; $projects = []; foreach (CostCentreEntity::find([['archived' => 0]]) as $cc) { /* @var $cc CostCentreEntity */ $ccs[$cc->ccId] = $cc->name; } foreach (ProjectEntity::find([['archived' => 0]]) as $project) { /* @var $project ProjectEntity */ $projects[$project->projectId] = $project->name; } $this->response->page('ui/admin/analytics/notifications/view.js', ['notifications.ccs' => NotificationEntity::findBySubjectType(NotificationEntity::SUBJECT_TYPE_CC)->getArrayCopy(), 'notifications.projects' => NotificationEntity::find([['subjectType' => NotificationEntity::SUBJECT_TYPE_PROJECT], ['accountId' => null]])->getArrayCopy(), 'reports' => ReportEntity::find([['accountId' => null]])->getArrayCopy(), 'ccs' => $ccs, 'projects' => $projects], [], ['ui/admin/analytics/notifications/view.css']); }
/** * Constructor * * @param \DBFarm $farm The DBFarm instance * @param string $projectId The identifier of the project to assign * @param string $oldProjectId The identifier of the old project which is replaced */ public function __construct(DBFarm $farm, $projectId, $oldProjectId) { parent::__construct(); array_push($this->projects, $projectId, $oldProjectId); $projectEntity = ProjectEntity::findPk($projectId); if ($projectEntity) { $this->ccs[$projectEntity->ccId] = $projectEntity->ccId; $projectName = $projectEntity->name; } else { $projectName = AccountTagEntity::fetchName($projectId, TagEntity::TAG_ID_PROJECT); } $oldProjectEntity = ProjectEntity::findPk($oldProjectId); if ($oldProjectEntity) { $this->ccs[$oldProjectEntity->ccId] = $oldProjectEntity->ccId; $oldProjectName = $oldProjectEntity->name; } else { $oldProjectName = AccountTagEntity::fetchName($oldProjectId, TagEntity::TAG_ID_PROJECT); } $this->message = sprintf("User %s replaced project '%s' with project '%s' in the farm '%s' id:%d", strip_tags($this->getUserEmail()), strip_tags($oldProjectName), strip_tags($projectName), strip_tags($farm->Name), $farm->ID); $this->messageToHash = sprintf('%s|%s|%s|%s|%s', $this->timelineEvent->dtime->format('Y-m-d'), $this->getUserEmail(), $oldProjectId, $projectId, $farm->ID); }
/** * @test */ public function testComplex() { //create archived project $projectArch = $this->createTestProject(['name' => $this->getTestName(), 'archived' => ProjectEntity::ARCHIVED]); $projects = $this->listProjects([], 1); $adapter = $this->getAdapter('project'); $filterable = $adapter->getRules()[ApiEntityAdapter::RULE_TYPE_FILTERABLE]; foreach ($projects as $project) { foreach ($filterable as $property) { $filterValue = $project->{$property}; $listResult = $this->listProjects([$property => $filterValue], 1); if (!empty($filterValue)) { foreach ($listResult as $filtered) { $this->assertEquals($filterValue, $filtered->{$property}, "Property '{$property}' mismatch"); } } } $response = $this->getProject($project->id); $this->assertEquals(200, $response->status, $this->printResponseError($response)); $dbProject = ProjectEntity::findPk($project->id); $this->assertFalse((bool) $dbProject->archived); $this->assertObjectEqualsEntity($response->getBody()->data, $dbProject, $adapter); } $filterByName = $this->listProjects(['name' => $projectArch->name], 1); $this->assertNotEmpty($filterByName); foreach ($filterByName as $project) { $this->assertObjectEqualsEntity($project, $projectArch, $adapter); $this->assertNotContains($project, $projects, "List of project shouldn't have an archived project", false, false); } $filterByBillingCode = $this->listProjects(['billingCode' => $projectArch->getProperty(ProjectPropertyEntity::NAME_BILLING_CODE)], 1); $this->assertNotEmpty($filterByBillingCode); foreach ($filterByBillingCode as $project) { $this->assertObjectEqualsEntity($project, $projectArch, $adapter); $this->assertNotContains($project, $projects, "List of project shouldn't have an archived project", false, false); } $ccId = Scalr_Environment::init()->loadById($this->getEnvironment()->id)->getPlatformConfigValue(Scalr_Environment::SETTING_CC_ID); $cc = \Scalr::getContainer()->analytics->ccs->get($ccId); //test create project $projectData = ['name' => 'test', 'costCenter' => ['id' => $ccId], 'billingCode' => $cc->getProperty(CostCentrePropertyEntity::NAME_BILLING_CODE), 'leadEmail' => '*****@*****.**', 'description' => 'test']; $response = $this->postProject($projectData); $this->assertEquals(201, $response->status, $this->printResponseError($response)); $projectId = $response->getBody()->data->id; $dbProject = ProjectEntity::findPk($projectId); $this->assertNotEmpty($dbProject); $this->projectToDelete($projectId); $this->assertObjectEqualsEntity($projectData, $dbProject, $adapter); //test empty project name $projectData = ['name' => "\t\r\n\v<a href=\"#\">\t\r\n\v</a>\t\r\n\v", 'costCenter' => ['id' => $ccId], 'billingCode' => $cc->getProperty(CostCentrePropertyEntity::NAME_BILLING_CODE), 'leadEmail' => '*****@*****.**', 'description' => 'test-empty-name']; $response = $this->postProject($projectData); $this->assertEquals(400, $response->status, $this->printResponseError($response)); //test wrong ccId $projectData = ['name' => "test-wrong-cc-id", 'costCenter' => ['id' => "\t\r\n\v<a href=\"#\">\t\r\n\v</a>\t\r\n\v"], 'billingCode' => $cc->getProperty(CostCentrePropertyEntity::NAME_BILLING_CODE), 'leadEmail' => '*****@*****.**', 'description' => 'test-empty-name']; $response = $this->postProject($projectData); $this->assertEquals(404, $response->status, $this->printResponseError($response)); //test wrong leadEmail $projectData = ['name' => "test-wrong-cc-id", 'costCenter' => ['id' => $ccId], 'billingCode' => $cc->getProperty(CostCentrePropertyEntity::NAME_BILLING_CODE), 'leadEmail' => "\t\r\n\v<a href=\"#\">\t\r\n\v</a>\t\r\n\v", 'description' => 'test-empty-lead-email']; $response = $this->postProject($projectData); $this->assertEquals(400, $response->status, $this->printResponseError($response)); }
/** * 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 => Scalr::GenerateRandomKey(40), SERVER_PROPERTIES::SZR_KEY_TYPE => SZR_KEY_TYPE::ONE_TIME)); $DBServer = DBServer::Create($ServerCreateInfo, false, true); } elseif (!$DBServer && !$ServerCreateInfo) { // incorrect arguments Logger::getLogger(LOG_CATEGORY::FARM)->error(sprintf("Cannot create server")); return null; } $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->createdByUserId; $propsToSet[SERVER_PROPERTIES::FARM_CREATED_BY_EMAIL] = $farm->createdByUserEmail; $projectId = $farm->GetSetting(DBFarm::SETTING_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) { $propsToSet[SERVER_PROPERTIES::INFO_INSTANCE_TYPE_NAME] = $farmRole->GetSetting(DBFarmRole::SETTING_INFO_INSTANCE_TYPE_NAME); } 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) { Logger::getLogger(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 ($delayed) { $DBServer->status = SERVER_STATUS::PENDING_LAUNCH; 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]); $DBServer->Save(); return $DBServer; } if ($ServerCreateInfo && $ServerCreateInfo->roleId) { $dbRole = DBRole::loadById($ServerCreateInfo->roleId); if ($dbRole->generation == 1) { $DBServer->status = SERVER_STATUS::PENDING_LAUNCH; $DBServer->Save(); $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 = \Scalr::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) { Logger::getLogger("SERVER_LAUNCH")->warn("{$pendingServers} servers in PENDING state on {$DBServer->platform}. Limit is: {$pendingServersLimit}. Waiting."); $DBServer->status = SERVER_STATUS::PENDING_LAUNCH; $DBServer->Save(); $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 { Logger::getLogger("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 { if ($reason) { list($reasonMsg, $reasonId) = is_array($reason) ? call_user_func_array($fnGetReason, $reason) : $fnGetReason($reason); } else { $reasonMsg = $DBServer->GetProperty(SERVER_PROPERTIES::LAUNCH_REASON); $reasonId = $DBServer->GetProperty(SERVER_PROPERTIES::LAUNCH_REASON_ID); } $DBServer->getServerHistory()->markAsLaunched($reasonMsg, $reasonId); $DBServer->updateTimelog('ts_launched'); if ($DBServer->imageId) { //Update Image last used date $image = Image::findOne([['id' => $DBServer->imageId], ['envId' => $DBServer->envId], ['platform' => $DBServer->platform], ['cloudLocation' => $DBServer->cloudLocation]]); if (!$image) { $image = Image::findOne([['id' => $DBServer->imageId], ['envId' => NULL], ['platform' => $DBServer->platform], ['cloudLocation' => $DBServer->cloudLocation]]); } if ($image) { $image->dtLastUsed = new DateTime(); $image->save(); } //Update Role last used date if ($DBServer->farmRoleId) { $dbRole = $DBServer->GetFarmRoleObject()->GetRoleObject(); $dbRole->dtLastUsed = date("Y-m-d H:i:s"); $dbRole->save(); } } } catch (Exception $e) { Logger::getLogger('SERVER_HISTORY')->error(sprintf("Cannot update servers history: {$e->getMessage()}")); } } catch (Exception $e) { Logger::getLogger(LOG_CATEGORY::FARM)->error(new FarmLogMessage($DBServer->farmId, sprintf("Cannot launch server on '%s' platform: %s", $DBServer->platform, $e->getMessage()), $DBServer->serverId)); $existingLaunchError = $DBServer->GetProperty(SERVER_PROPERTIES::LAUNCH_ERROR); $DBServer->status = SERVER_STATUS::PENDING_LAUNCH; $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->Save(); if ($DBServer->farmId && !$existingLaunchError) { Scalr::FireEvent($DBServer->farmId, new InstanceLaunchFailedEvent($DBServer, $e->getMessage())); } } if ($DBServer->status == SERVER_STATUS::PENDING) { Scalr::FireEvent($DBServer->farmId, new BeforeInstanceLaunchEvent($DBServer)); $DBServer->SetProperty(SERVER_PROPERTIES::LAUNCH_ERROR, ""); } return $DBServer; }
/** * Gets relation dependent budget * * Another words it will return total budgeted amount for all projects * which have relations to the cost center. * * If we use this method for the project it will return 0 * * @return float Returns relation dependent budget amount according to current state in the database */ public function getRelationDependentBudget() { if (empty($this->subjectId)) { throw new InvalidArgumentException(sprintf("Identifier of the subject has not been provided for the %s", get_class($this))); } //Projects have no descendants if ($this->subjectType == self::SUBJECT_TYPE_PROJECT) { return 0; } $params = [$this->year, self::SUBJECT_TYPE_PROJECT]; $stmt = ''; foreach (ProjectEntity::findByCcId($this->subjectId) as $project) { $stmt .= ", " . $this->qstr('subjectId', $project->projectId); } if ($stmt != '') { $ret = $this->db()->GetOne("\n SELECT SUM(q.`budget`) AS `budget`\n FROM " . $this->table('q') . "\n WHERE q.`year` = ?\n AND q.`subject_type` = ?\n " . (is_numeric($this->quarter) ? " AND q.`quarter` =" . intval($this->quarter) : "") . "\n AND q.`subject_id` IN (" . ltrim($stmt, ',') . ")\n ", $params); } else { $ret = 0.0; } return $ret; }
/** * Gets project properties and parameters * * @param ProjectEntity $projectEntity Project entity * @param string $calculate optional Whether response should be adjusted with cost usage data * @return array Returns cost centre properties and parameters */ private function getProjectData(ProjectEntity $projectEntity, $calculate = false) { $ret = array('ccId' => $projectEntity->ccId, 'ccName' => $projectEntity->getCostCenter() !== null ? $projectEntity->getCostCenter()->name : null, 'projectId' => $projectEntity->projectId, 'name' => $projectEntity->name, 'billingCode' => $projectEntity->getProperty(ProjectPropertyEntity::NAME_BILLING_CODE), 'description' => $projectEntity->getProperty(ProjectPropertyEntity::NAME_DESCRIPTION), 'leadEmail' => $projectEntity->getProperty(ProjectPropertyEntity::NAME_LEAD_EMAIL), 'created' => $projectEntity->created->format('Y-m-d'), 'createdByEmail' => $projectEntity->createdByEmail, 'archived' => $projectEntity->archived, 'farmsCount' => count($projectEntity->getFarmsList())); if ($calculate) { $iterator = new ChartPeriodIterator('month', gmdate('Y-m-01'), null, 'UTC'); $usage = $this->getContainer()->analytics->usage->get(['projectId' => $ret['projectId']], $iterator->getStart(), $iterator->getEnd()); //It calculates usage for previous period same days $prevusage = $this->getContainer()->analytics->usage->get(['projectId' => $ret['projectId']], $iterator->getPreviousStart(), $iterator->getPreviousEnd()); //Calclulates usage for previous whole period if ($iterator->getPreviousEnd() != $iterator->getWholePreviousPeriodEnd()) { $prevWholePeriodUsage = $this->getContainer()->analytics->usage->get(['projectId' => $ret['projectId']], $iterator->getPreviousStart(), $iterator->getWholePreviousPeriodEnd()); } else { $prevWholePeriodUsage = $prevusage; } $ret = $this->getWrappedUsageData(['ccId' => $ret['ccId'], 'projectId' => $ret['projectId'], 'iterator' => $iterator, 'usage' => $usage['cost'], 'prevusage' => $prevusage['cost'], 'prevusagewhole' => $prevWholePeriodUsage['cost']]) + $ret; } return $ret; }
/** * Gets all projects associated with the cost centre * * @return ArrayCollection Returns collection of the ProjectEntity objects */ public function getProjects() { return ProjectEntity::result(self::RESULT_ENTITY_COLLECTION)->findByCcId($this->ccId); }
/** * xMoveProjectsAction * * @param JsonData $projects Projects that should be moved * @throws AnalyticsException * @throws Exception * @throws \Scalr\Exception\ModelException */ public function xMoveProjectsAction(JsonData $projects = null) { $envChange = []; $accountChange = []; $projectChange = []; $ccEntityCache = []; $collisions = []; foreach ($projects as $project) { $projectEntity = ProjectEntity::findPk($project['projectId']); /* @var $projectEntity ProjectEntity */ if (empty($ccEntity)) { $ccEntity = $projectEntity->getCostCenter(); } if ($ccEntity->ccId == $project['ccId']) { continue; } if (empty($ccEntityCache[$project['ccId']])) { $newCcEntity = CostCentreEntity::findPk($project['ccId']); /* @var $newCcEntity CostCentreEntity */ if (!$newCcEntity) { throw new Exception(sprintf("Cost center with id %s has not been found.", $project['ccId']), 404); } $ccEntityCache[$project['ccId']] = $newCcEntity->ccId; } $farms[$projectEntity->projectId] = $projectEntity->getFarmsList(); foreach ($farms[$projectEntity->projectId] as $farmId => $farmName) { $farmEntity = Farm::findPk($farmId); /* @var $farmEntity Farm */ if (empty($accountChange[$farmEntity->accountId])) { $accountCss = AccountCostCenterEntity::findOne([['accountId' => $farmEntity->accountId], ['ccId' => $newCcEntity->ccId]]); if (!$accountCss) { $accountChange[$farmEntity->accountId] = $newCcEntity->ccId; } } if (empty($envChange[$farmEntity->envId])) { $project['name'] = $projectEntity->name; $envChange[$farmEntity->envId] = $project; } else { if ($envChange[$farmEntity->envId]['ccId'] != $project['ccId']) { if (!in_array($projectEntity->name, $collisions)) { $collisions[] = $projectEntity->name; } if (!in_array($envChange[$farmEntity->envId]['name'], $collisions)) { $collisions[] = $envChange[$farmEntity->envId]['name']; } continue; } } } $projectEntity->ccId = $project['ccId']; $projectChange[$projectEntity->projectId] = $projectEntity; } $remainningEnvs = []; $projectsCount = count($projectChange); if ($projectsCount) { if (isset($ccEntity)) { $envList = $ccEntity->getEnvironmentsList(); foreach ($envList as $envId => $name) { if (isset($envChange[$envId])) { $ccProjects = $this->getContainer()->analytics->projects->getUsedInEnvironment($envId); foreach ($ccProjects as $project) { /* @var $project ProjectEntity */ if (!isset($farms[$project->projectId])) { $farms[$project->projectId] = $project->getFarmsList(); } if (count($farms[$project->projectId]) > 0 && !isset($projectChange[$project->projectId])) { if (!in_array($envId, $remainningEnvs)) { $remainningEnvs[] = $envId; } } } } } } $this->db->BeginTrans(); try { foreach ($accountChange as $accountId => $ccId) { $accountCss = new AccountCostCenterEntity($accountId, $ccId); $accountCss->save(); } if (empty($remainningEnvs) && empty($collisions)) { foreach ($envChange as $envId => $data) { $envProp = EnvironmentProperty::findOne([['envId' => $envId], ['name' => EnvironmentProperty::SETTING_CC_ID]]); /* @var $envProp EnvironmentProperty */ $envProp->value = $data['ccId']; $envProp->save(); } } foreach ($projectChange as $project) { /* @var $project ProjectEntity */ $project->save(); } $this->db->CommitTrans(); } catch (Exception $e) { $this->db->RollbackTrans(); throw $e; } } if (count($collisions) > 0) { $this->response->warning(sprintf("%d Project%s %s been moved however collision occurred. Projects '%s' are used in the Farms from the same Environment however they have been moved to different Cost Centers.", $projectsCount, $projectsCount > 1 ? 's' : '', $projectsCount > 1 ? 'have' : 'has', implode("', '", $collisions))); } else { if (count($remainningEnvs) > 0) { $this->response->warning(sprintf("%d Project%s %s been moved however some Projects don't correspond to Cost Centers assigned to Environments '%s'.", $projectsCount, $projectsCount > 1 ? 's' : '', $projectsCount > 1 ? 'have' : 'has', implode("', '", $remainningEnvs))); } else { $this->response->success(sprintf("%d Project%s %s been moved to other Cost Center.", $projectsCount, $projectsCount > 1 ? 's' : '', $projectsCount > 1 ? 'have' : 'has')); } } }
/** * Gets farm properties and parameters * * @param DBFarm $dbFarm DBFarm object * @return array Returns farm properties and parameters */ private function getFarmData(DBFarm $dbFarm) { $projectId = $dbFarm->GetSetting(\DBFarm::SETTING_PROJECT_ID); $ret = array('farmId' => $dbFarm->ID, 'name' => $dbFarm->Name, 'description' => $dbFarm->Comments, 'createdByEmail' => $dbFarm->createdByUserEmail, 'projectId' => $projectId, 'projectName' => !empty($projectId) ? ProjectEntity::findPk($projectId)->name : null); return $ret; }
/** * * @return array */ public function GetScriptingVars() { $dbFarmRole = $this->GetFarmRoleObject(); $roleId = $dbFarmRole->RoleID; $dbRole = DBRole::loadById($roleId); $isDbMsr = $dbRole->getDbMsrBehavior(); if ($isDbMsr) { $isMaster = $this->GetProperty(Scalr_Db_Msr::REPLICATION_MASTER); } else { $isMaster = $this->GetProperty(\SERVER_PROPERTIES::DB_MYSQL_MASTER); } $retval = array('image_id' => $dbRole->__getNewRoleObject()->getImage($this->platform, $dbFarmRole->CloudLocation)->imageId, 'external_ip' => $this->remoteIp, 'internal_ip' => $this->localIp, 'role_name' => $dbRole->name, 'isdbmaster' => $isMaster, 'instance_index' => $this->index, 'instance_farm_index' => $this->farmIndex, 'server_type' => $this->type, 'server_hostname' => $this->GetProperty(Scalr_Role_Behavior::SERVER_BASE_HOSTNAME), 'server_id' => $this->serverId, 'farm_id' => $this->farmId, 'farm_role_id' => $this->farmRoleId, 'farm_role_alias' => $this->GetFarmRoleObject()->Alias, 'farm_name' => $this->GetFarmObject()->Name, 'farm_hash' => $this->GetFarmObject()->Hash, 'farm_owner_email' => $this->GetFarmObject()->createdByUserEmail, 'farm_team' => $this->GetFarmObject()->teamId ? (new Scalr_Account_Team())->loadById($this->GetFarmObject()->teamId)->name : '', 'behaviors' => implode(",", $dbRole->getBehaviors()), 'env_id' => $this->GetEnvironmentObject()->id, 'env_name' => $this->GetEnvironmentObject()->name, 'cloud_location' => $this->GetCloudLocation(), 'cloud_server_id' => $this->GetCloudServerID()); if ($this->cloudLocationZone) { $retval['cloud_location_zone'] = $this->cloudLocationZone; } if ($this->platform == SERVER_PLATFORMS::EC2) { $retval['instance_id'] = $this->GetProperty(EC2_SERVER_PROPERTIES::INSTANCE_ID); $retval['ami_id'] = $this->GetProperty(EC2_SERVER_PROPERTIES::AMIID); $retval['region'] = $this->GetProperty(EC2_SERVER_PROPERTIES::REGION); $retval['avail_zone'] = $this->GetProperty(EC2_SERVER_PROPERTIES::AVAIL_ZONE); if ($dbFarmRole->GetSetting(Entity\FarmRoleSetting::AWS_ELB_ENABLED)) { $elbId = $dbFarmRole->GetSetting(Entity\FarmRoleSetting::AWS_ELB_ID); $retval['aws_elb_name'] = $elbId; } } if (\Scalr::getContainer()->analytics->enabled) { $ccId = $this->GetProperty(\SERVER_PROPERTIES::ENV_CC_ID); if ($ccId) { $cc = CostCentreEntity::findPk($ccId); if ($cc) { /* @var $cc CostCentreEntity */ $retval['cost_center_id'] = $ccId; $retval['cost_center_bc'] = $cc->getProperty(CostCentrePropertyEntity::NAME_BILLING_CODE); $retval['cost_center_name'] = $cc->name; } else { throw new Exception("Cost center {$ccId} not found"); } } $projectId = $this->GetProperty(\SERVER_PROPERTIES::FARM_PROJECT_ID); if ($projectId) { $project = ProjectEntity::findPk($projectId); /* @var $project ProjectEntity */ $retval['project_id'] = $projectId; $retval['project_bc'] = $project->getProperty(ProjectPropertyEntity::NAME_BILLING_CODE); $retval['project_name'] = $project->name; } } return $retval; }
/** * @test */ public function textComplex() { $projects = $this->listProjects(); $adapter = $this->getAdapter('project'); foreach ($projects as $project) { foreach ($adapter->getRules()[ApiEntityAdapter::RULE_TYPE_FILTERABLE] as $property) { foreach ($this->listProjects([$property => $project->{$property}]) as $filteredProject) { $this->assertEquals($project->{$property}, $filteredProject->{$property}); } } $response = $this->getProject($project->id); $this->assertEquals(200, $response->status, $this->printResponseError($response)); $dbProject = ProjectEntity::findPk($project->id); $this->assertObjectEqualsEntity($response->getBody()->data, $dbProject, $adapter); } $ccId = Scalr_Environment::init()->loadById($this->getEnvironment()->id)->getPlatformConfigValue(Scalr_Environment::SETTING_CC_ID); $cc = \Scalr::getContainer()->analytics->ccs->get($ccId); $projectData = ['name' => 'test', 'costCenter' => ['id' => $ccId], 'billingCode' => $cc->getProperty(CostCentrePropertyEntity::NAME_BILLING_CODE), 'leadEmail' => '*****@*****.**', 'description' => 'test']; $response = $this->postProject($projectData); $this->assertEquals(201, $response->status, $this->printResponseError($response)); $projectId = $response->getBody()->data->id; $dbProject = ProjectEntity::findPk($projectId); $this->assertNotEmpty($dbProject); $this->projectToDelete($projectId); $this->assertObjectEqualsEntity($projectData, $dbProject, $adapter); }
/** * xGetPeriodLogAction * * @param string $mode The mode (week, month, quarter, year) * @param string $startDate The start date of the period in UTC ('Y-m-d') * @param string $endDate The end date of the period in UTC ('Y-m-d') * @param string $type Type of the data gathered in log file (farms, clouds, projects) * @param string $ccId optional The identifier of the cost center * @param string $projectId optional The identifier of the project */ public function xGetPeriodCsvAction($mode, $startDate, $endDate, $type, $ccId = null, $projectId = null) { $name = 'Cloud'; if (!empty($ccId) && empty($projectId)) { $data = $this->getContainer()->analytics->usage->getCostCenterPeriodData($ccId, $mode, $startDate, $endDate); $entity = CostCentreEntity::findPk($ccId); if ($type !== 'clouds') { $name = 'Project'; $extraFields = 'Cost Center name;Billing code;Lead email address;'; } } else { if (!empty($projectId)) { $data = $this->getContainer()->analytics->usage->getProjectPeriodData($projectId, $mode, $startDate, $endDate); $entity = ProjectEntity::findPk($projectId); if ($type !== 'clouds') { $name = 'Farm'; $extraFields = 'Project name;Billing code;Lead email address;'; } } else { throw new \InvalidArgumentException(sprintf("Method %s requires both ccId or projectId to be specified.", __METHOD__)); } } $extraData = [$entity->name, $entity->getProperty('billing.code'), $entity->getProperty('lead.email')]; $head[] = $name; $end[] = "Total spent"; if (isset($extraFields)) { $head = array_merge($head, $extraData); $end = array_merge($end, $extraData); } $totals = 0; foreach ($data['timeline'] as $timeline) { $totals += $timeline['cost']; $head[] = $timeline['label']; $end[] = $timeline['cost']; } $head[] = 'Total'; $end[] = $totals; $temp = tmpfile(); fputcsv($temp, $head); foreach ($data[$type] as $item) { $row = []; $row[] = $item['name']; $dataCost = []; foreach ($data['timeline'] as $key => $value) { $dataCost[] = isset($item['data'][$key]['cost']) ? $item['data'][$key]['cost'] : 0; } $itemTotal = array_sum($dataCost); if (isset($extraFields)) { $row = array_merge($row, $extraData); } $row = array_merge($row, $dataCost); $row[] = $itemTotal; fputcsv($temp, $row); } fputcsv($temp, $end); $metadata = stream_get_meta_data($temp); $label = Scalr_Util_DateTime::convertTz(time(), 'M_j_Y_H:i:s'); $fileName = $entity->name . '.' . $entity->getProperty('billing.code') . '.' . $type . '.' . $label; $bad = array_merge(array_map('chr', range(0, 31)), ["<", ">", ":", '"', "/", "\\", "|", "?", "*"]); $fileName = str_replace($bad, "", $fileName); $this->response->setHeader('Content-Encoding', 'utf-8'); $this->response->setHeader('Content-Type', 'text/csv', true); $this->response->setHeader('Expires', 'Mon, 10 Jan 1997 08:00:00 GMT'); $this->response->setHeader('Pragma', 'no-cache'); $this->response->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate'); $this->response->setHeader('Cache-Control', 'post-check=0, pre-check=0'); $this->response->setHeader('Content-Disposition', 'attachment; filename=' . $fileName . ".csv"); $this->response->setResponse(file_get_contents($metadata['uri'])); fclose($temp); }
/** * Finds projects by key * It searches by name or billing number * * @param string $key optional Search key * @return ArrayCollection Returns collection of the ProjectEntity objects */ public function findByKey($key = null) { if (is_null($key) || $key === '') { return $this->all(); } $collection = new ArrayCollection(); $projectEntity = new ProjectEntity(); //Includes archived projects $projectPropertyEntity = new ProjectPropertyEntity(); //Cost center entity $ccEntity = new CostCentreEntity(); $rs = $this->db->Execute("\n SELECT " . $projectEntity->fields('p') . ", " . $ccEntity->fields('c', true) . "\n FROM " . $projectEntity->table('p') . "\n LEFT JOIN " . $ccEntity->table('c') . " ON c.`cc_id` = p.`cc_id`\n WHERE p.`name` LIKE ?\n OR EXISTS (\n SELECT 1 FROM " . $projectPropertyEntity->table('pp') . "\n WHERE `pp`.project_id = `p`.`project_id`\n AND `pp`.`name` = ? AND `pp`.`value` LIKE ?\n )\n ", ['%' . $key . '%', ProjectPropertyEntity::NAME_BILLING_CODE, '%' . $key . '%']); while ($rec = $rs->FetchRow()) { $item = new ProjectEntity(); $item->load($rec); if ($rec['c_cc_id']) { $cc = new CostCentreEntity(); $cc->load($rec, 'c'); $item->setCostCenter($cc); } $collection->append($item); } return $collection; }
protected function isApplied1($stage) { return CostCentreEntity::findPk(Usage::DEFAULT_CC_ID) !== null && ProjectEntity::findPk(Usage::DEFAULT_PROJECT_ID) !== null; }
protected function run8($stage) { if ($this->getCountOfAccountTagValues(TagEntity::TAG_ID_PROJECT) && $this->console->confirm('Would you like to remove old projects from account_tag_values?')) { $this->console->out("Removing old projects"); $this->db->Execute("DELETE FROM account_tag_values WHERE tag_id = ?", array(TagEntity::TAG_ID_PROJECT)); } $this->console->out('Populating projects to the dictionary'); foreach (ProjectEntity::all() as $projectEntity) { /* @var $projectEntity ProjectEntity */ $this->db->Execute("\n INSERT IGNORE `account_tag_values` (`account_id`, `tag_id`, `value_id`, `value_name`)\n VALUES (?, ?, ?, ?)\n ", [$projectEntity->accountId ?: 0, TagEntity::TAG_ID_PROJECT, $projectEntity->projectId, $projectEntity->name]); } }
/** * xGetPeriodLogAction * * @param string $mode The mode (week, month, quarter, year) * @param string $startDate The start date of the period in UTC ('Y-m-d') * @param string $endDate The end date of the period in UTC ('Y-m-d') * @param string $type Type of the data gathered in log file * @param string $projectId optional The identifier of the project */ public function xGetPeriodCsvAction($mode, $startDate, $endDate, $type, $projectId = null, $farmId = null) { if ($type == 'farms') { $name = 'Farm'; } else { if ($type == 'clouds') { $name = 'Cloud'; } else { $name = 'Farm Roles'; } } if (!empty($projectId)) { $filter = ['envId' => $this->environment->id, 'accountId' => $this->environment->clientId]; $data = $this->getContainer()->analytics->usage->getProjectPeriodData($projectId, $mode, $startDate, $endDate, $filter); $entity = ProjectEntity::findPk($projectId); if ($type !== 'clouds') { $extraFields = 'Project name;Billing code;Lead email address;'; } $fileName = $entity->name . '.' . $entity->getProperty('billing.code'); } else { if (!empty($farmId)) { $data = $this->getContainer()->analytics->usage->getFarmPeriodData($farmId, $this->environment, $mode, $startDate, $endDate); $farm = \DBFarm::LoadByID($farmId); $fileName = $farm->Name . '.' . $farmId; } else { $data = $this->getContainer()->analytics->usage->getEnvironmentPeriodData($this->environment, $mode, $startDate, $endDate); $fileName = 'Environment' . $this->environment->id; } } if (!empty($entity)) { $extraData = [$entity->name, $entity->getProperty('billing.code'), $entity->getProperty('lead.email')]; } $head[] = $name; $end[] = "Total spent"; if (isset($extraFields)) { $head = array_merge($head, $extraData); $end = array_merge($end, $extraData); } $totals = 0; foreach ($data['timeline'] as $timeline) { $totals += $timeline['cost']; $head[] = $timeline['label']; $end[] = $timeline['cost']; } $head[] = 'Total'; $end[] = $totals; $temp = tmpfile(); fputcsv($temp, $head); foreach ($data[$type] as $item) { $row = []; $row[] = $item['name']; $dataCost = []; foreach ($data['timeline'] as $key => $value) { $dataCost[] = isset($item['data'][$key]['cost']) ? $item['data'][$key]['cost'] : 0; } $itemTotal = array_sum($dataCost); if (isset($extraFields)) { $row = array_merge($row, $extraData); } $row = array_merge($row, $dataCost); $row[] = $itemTotal; fputcsv($temp, $row); } fputcsv($temp, $end); $metadata = stream_get_meta_data($temp); $fileName = $fileName . '.' . $type . '.' . Scalr_Util_DateTime::convertTz(time(), 'M_j_Y_H:i:s'); $bad = array_merge(array_map('chr', range(0, 31)), ["<", ">", ":", '"', "/", "\\", "|", "?", "*"]); $fileName = str_replace($bad, "", $fileName); $this->response->setHeader('Content-Encoding', 'utf-8'); $this->response->setHeader('Content-Type', 'text/csv', true); $this->response->setHeader('Expires', 'Mon, 10 Jan 1997 08:00:00 GMT'); $this->response->setHeader('Pragma', 'no-cache'); $this->response->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate'); $this->response->setHeader('Cache-Control', 'post-check=0, pre-check=0'); $this->response->setHeader('Content-Disposition', 'attachment; filename=' . $fileName . ".csv"); $this->response->setResponse(file_get_contents($metadata['uri'])); fclose($temp); }
/** * Checks if user has permissions to project in environment or account scope * * @param string $projectId Identifier of the project * @param array $criteria ['envId' => '', 'clientid' => ''] * @return bool|mixed */ public function checkPermission($projectId, array $criteria) { $and = ''; foreach ($criteria as $name => $value) { $field = 'f.' . \Scalr::decamelize($name); $and .= " AND " . $field . "=" . $this->db->escape($value); } $projectEntity = new ProjectEntity(); $projectId = $projectEntity->type('projectId')->toDb($projectId); $where = " WHERE p.project_id = UNHEX('" . $projectId . "') AND EXISTS (\n SELECT * FROM farms f\n LEFT JOIN farm_settings fs ON f.id = fs.farmid\n WHERE fs.name = '" . Entity\FarmSetting::PROJECT_ID . "'\n AND REPLACE(fs.value, '-', '') = HEX(p.project_id)\n {$and})"; $sql = "SELECT " . $projectEntity->fields('p') . "\n FROM " . $projectEntity->table('p') . $where; return $this->db->GetOne($sql); }
/** * Checks if user has access to project or cost center * * @param string $projectId optional Id of the project * @param string $ccId optional Id of the cost center * @return boolean Returns false if user is not lead of the subject */ public function isSubjectLead($projectId = null, $ccId = null) { if (!empty($projectId)) { $project = ProjectEntity::findPk($projectId); if (empty($project) || $project->getProperty(ProjectPropertyEntity::NAME_LEAD_EMAIL) !== $this->getEmail()) { return false; } } else { if (!empty($ccId)) { $ccs = CostCentreEntity::findPk($ccId); if (empty($ccs) || $ccs->getProperty(CostCentrePropertyEntity::NAME_LEAD_EMAIL) !== $this->getEmail()) { return false; } } } return true; }
/** * {@inheritdoc} * @see \Scalr\Model\AbstractEntity::save() */ public function save() { //Checks data integrity. $criteria = [['name' => $this->name], ['ccId' => $this->ccId]]; if ($this->projectId) { $criteria[] = ['projectId' => ['$ne' => $this->projectId]]; } //The name of the project should be unique withing the current cost center $item = ProjectEntity::findOne($criteria); if ($item) { throw new AnalyticsException(sprintf('A Project with this name already exists. Please choose another name.')); } parent::save(); if ($this->projectId && \Scalr::getContainer()->analytics->enabled) { \Scalr::getContainer()->analytics->tags->syncValue($this->accountId ?: 0, \Scalr\Stats\CostAnalytics\Entity\TagEntity::TAG_ID_PROJECT, $this->projectId, $this->name); } }
/** * Gets project properties and parameters * * @param ProjectEntity $projectEntity Project entity * @param string $calculate optional Whether response should be adjusted with cost usage data * @return array Returns cost centre properties and parameters */ private function getProjectData(ProjectEntity $projectEntity, $calculate = false) { $ret = array('ccId' => $projectEntity->ccId, 'ccName' => $projectEntity->getCostCenter() !== null ? $projectEntity->getCostCenter()->name : null, 'projectId' => $projectEntity->projectId, 'name' => $projectEntity->name, 'billingCode' => $projectEntity->getProperty(ProjectPropertyEntity::NAME_BILLING_CODE), 'description' => $projectEntity->getProperty(ProjectPropertyEntity::NAME_DESCRIPTION), 'leadEmail' => $projectEntity->getProperty(ProjectPropertyEntity::NAME_LEAD_EMAIL), 'created' => $projectEntity->created->format('Y-m-d'), 'createdByEmail' => $projectEntity->createdByEmail, 'archived' => $projectEntity->archived, 'shared' => $projectEntity->shared, 'farmsCount' => count($projectEntity->getFarmsList())); if (!empty($projectEntity->accountId) && $projectEntity->shared === ProjectEntity::SHARED_WITHIN_ACCOUNT) { $ret['accountId'] = $projectEntity->accountId; $ret['accountName'] = Scalr_Account::init()->loadById($projectEntity->accountId)->name; } elseif (!empty($projectEntity->envId) && $projectEntity->shared === ProjectEntity::SHARED_WITHIN_ENV) { $ret['accountId'] = $projectEntity->accountId; $ret['accountName'] = Scalr_Account::init()->loadById($projectEntity->accountId)->name; $ret['envId'] = $projectEntity->envId; $ret['envName'] = Scalr_Environment::init()->loadById($projectEntity->envId)->name; } if ($calculate) { $iterator = ChartPeriodIterator::create('month', gmdate('Y-m-01'), null, 'UTC'); $usage = $this->getContainer()->analytics->usage->get(['projectId' => $ret['projectId']], $iterator->getStart(), $iterator->getEnd()); //It calculates usage for previous period same days $prevusage = $this->getContainer()->analytics->usage->get(['projectId' => $ret['projectId']], $iterator->getPreviousStart(), $iterator->getPreviousEnd()); $ret = $this->getWrappedUsageData(['ccId' => $ret['ccId'], 'projectId' => $ret['projectId'], 'iterator' => $iterator, 'usage' => $usage['cost'], 'prevusage' => $prevusage['cost']]) + $ret; } return $ret; }
private function getFarmCostData($farmId) { $result = []; $costCenter = $this->getContainer()->analytics->ccs->get($this->getEnvironment()->getPlatformConfigValue(Scalr_Environment::SETTING_CC_ID)); $currentYear = (new \DateTime('now', new \DateTimeZone('UTC')))->format('Y'); $quarters = new Quarters(SettingEntity::getQuarters()); $currentQuarter = $quarters->getQuarterForDate(new \DateTime('now', new \DateTimeZone('UTC'))); $projects = []; if ($farmId) { $farm = DBFarm::LoadByID($farmId); $currentProjectId = $farm->GetSetting(Entity\FarmSetting::PROJECT_ID); $currentProject = ProjectEntity::findPk($currentProjectId); /* @var $currentProject ProjectEntity */ if (!empty($currentProject)) { $quarterBudget = QuarterlyBudgetEntity::findOne([['year' => $currentYear], ['subjectType' => QuarterlyBudgetEntity::SUBJECT_TYPE_PROJECT], ['subjectId' => $currentProject->projectId], ['quarter' => $currentQuarter]]); $projects[] = ['projectId' => $currentProject->projectId, 'name' => "{$currentProject->name} / {$currentProject->getCostCenter()->name}", 'budgetRemain' => !is_null($quarterBudget) && $quarterBudget->budget > 0 ? max(0, round($quarterBudget->budget - $quarterBudget->cumulativespend)) : null]; } $result['projectId'] = $farm->GetSetting(Entity\FarmSetting::PROJECT_ID); $result['farmCostMetering'] = $result['projectId'] ? $this->getContainer()->analytics->usage->getFarmCostMetering($this->user->getAccountId(), $farmId) : null; } if ($costCenter instanceof CostCentreEntity) { $projectsIterator = new SharedProjectsFilterIterator($costCenter->getProjects(), $costCenter->ccId, $this->user, $this->getEnvironment()); foreach ($projectsIterator as $item) { /* @var $item Scalr\Stats\CostAnalytics\Entity\ProjectEntity */ if (!empty($currentProjectId) && $item->projectId == $currentProjectId) { continue; } $quarterBudget = QuarterlyBudgetEntity::findOne([['year' => $currentYear], ['subjectType' => QuarterlyBudgetEntity::SUBJECT_TYPE_PROJECT], ['subjectId' => $item->projectId], ['quarter' => $currentQuarter]]); $projects[] = array('projectId' => $item->projectId, 'name' => "{$item->name} / {$costCenter->name}", 'budgetRemain' => !is_null($quarterBudget) && $quarterBudget->budget > 0 ? max(0, round($quarterBudget->budget - $quarterBudget->cumulativespend)) : null); } $costCentreName = $costCenter->name; $isLocked = $costCenter->getProperty(CostCentrePropertyEntity::NAME_LOCKED); $accountCcs = AccountCostCenterEntity::findOne([['accountId' => $this->environment->clientId], ['ccId' => $costCenter->ccId]]); if ($isLocked || !$accountCcs instanceof AccountCostCenterEntity) { $costCentreLocked = 1; } else { $costCentreLocked = 0; } } else { $costCentreName = ''; $costCentreLocked = 0; } $supportedClouds = $this->getContainer()->analytics->prices->getSupportedClouds(); $result['analytics'] = array('costCenterName' => $costCentreName, 'costCenterLocked' => $costCentreLocked, 'projects' => $projects, 'unsupportedClouds' => array_values(array_diff($this->environment->getEnabledPlatforms(), $supportedClouds))); return $result; }
/** * Associates cost analytics project with the farm * * It does not perform any actions if cost analytics is disabled * * @param ProjectEntity|string $project The project entity or its identifier * @return string Returns identifier of the associated project * @throws InvalidArgumentException * @throws AnalyticsException */ public function setProject($project) { if (Scalr::getContainer()->analytics->enabled) { if ($project instanceof ProjectEntity) { $projectId = $project->projectId; } else { $projectId = $project; unset($project); } $analytics = Scalr::getContainer()->analytics; if ($projectId === null) { $ccId = $this->GetEnvironmentObject()->getPlatformConfigValue(Scalr_Environment::SETTING_CC_ID); if (!empty($ccId)) { //Assigns Project automatically only if it is the one withing the Cost Center $projects = ProjectEntity::findByCcId($ccId); if (count($projects) == 1) { $project = $projects->getArrayCopy()[0]; $projectId = $project->projectId; } } } elseif (!empty($projectId)) { //Validates specified project's identifier if (!preg_match('/^[[:xdigit:]-]{36}$/', $projectId)) { throw new InvalidArgumentException(sprintf("Identifier of the cost analytics Project must have valid UUID format. '%s' given.", strip_tags($projectId))); } $project = isset($project) ? $project : $analytics->projects->get($projectId); if (!$project) { throw new AnalyticsException(sprintf("Could not find Project with specified identifier %s.", strip_tags($projectId))); } else { if ($project->ccId !== $this->GetEnvironmentObject()->getPlatformConfigValue(Scalr_Environment::SETTING_CC_ID)) { throw new AnalyticsException(sprintf("Invalid project identifier. Parent Cost center of the Project should correspond to the Environment's cost center.")); } } } else { $projectId = null; } //Sets project to the farm object only if it has been provided if (isset($projectId)) { $project = isset($project) ? $project : $analytics->projects->get($projectId); $oldProjectId = $this->GetSetting(Entity\FarmSetting::PROJECT_ID); $this->SetSetting(Entity\FarmSetting::PROJECT_ID, $project->projectId); //Server property SERVER_PROPERTIES::FARM_PROJECT_ID should be updated //for all running servers associated with the farm. $this->DB->Execute("\n INSERT `server_properties` (`server_id`, `name`, `value`)\n SELECT s.`server_id`, ? AS `name`, ? AS `value`\n FROM `servers` s\n WHERE s.`farm_id` = ?\n ON DUPLICATE KEY UPDATE `value` = ?\n ", [SERVER_PROPERTIES::FARM_PROJECT_ID, $project->projectId, $this->ID, $project->projectId]); //Cost centre should correspond to Project's CC $this->DB->Execute("\n INSERT `server_properties` (`server_id`, `name`, `value`)\n SELECT s.`server_id`, ? AS `name`, ? AS `value`\n FROM `servers` s\n WHERE s.`farm_id` = ?\n ON DUPLICATE KEY UPDATE `value` = ?\n ", [SERVER_PROPERTIES::ENV_CC_ID, $project->ccId, $this->ID, $project->ccId]); if (empty($oldProjectId)) { $analytics->events->fireAssignProjectEvent($this, $project->projectId); } elseif ($oldProjectId !== $projectId) { $analytics->events->fireReplaceProjectEvent($this, $project->projectId, $oldProjectId); } } } return $projectId; }
/** * Gets detailed top 5 usage by farms for specified project on date * * @param string|null $projectId The identifier of the project * @param string $platform The cloud platform * @param string $mode The mode * @param string $date The UTC date within period ('Y-m-d H:00') * @param string $start The start date of the period in UTC ('Y-m-d') * @param string $end The end date of the period in UTC ('Y-m-d') * @param string $ccId optional The identifier of the cost center (It is used only when project is null) * @return array Returns detailed top 5 usage by farms for specified project on date * @throws AnalyticsException * @throws OutOfBoundsException */ public function getProjectFarmsTopUsageOnDate($projectId, $platform, $mode, $date, $start, $end, $ccId = null) { $projectId = empty($projectId) ? null : $projectId; $iterator = ChartPeriodIterator::create($mode, $start, $end ?: null, 'UTC'); //Interval which is used in the database query for grouping $queryInterval = preg_replace('/^1 /', '', $iterator->getInterval()); if ($projectId !== null) { $project = ProjectEntity::findPk($projectId); if ($project === null) { if (empty($ccId)) { throw new AnalyticsException(sprintf("Project %s does not exist. Please provide ccId.", $projectId)); } } } //Requests data for the specified period $rawUsage = $this->get(['projectId' => $projectId], $iterator->getStart(), $iterator->getEnd(), [$queryInterval, TagEntity::TAG_ID_PLATFORM, TagEntity::TAG_ID_FARM], true); //Requests data for the previous period $rawPrevUsage = $this->get(['projectId' => $projectId], $iterator->getPreviousStart(), $iterator->getPreviousEnd(), [$queryInterval, TagEntity::TAG_ID_PLATFORM, TagEntity::TAG_ID_FARM], true); //We do not need to calculate the percentage here $usg = (new AggregationCollection(['period', 'platform', 'farmId' => ['envId']], ['cost' => 'sum']))->load($rawUsage); $prevUsg = (new AggregationCollection(['period', 'platform', 'farmId'], ['cost' => 'sum']))->load($rawPrevUsage)->calculatePercentage(); //Previous chart point $prevcp = null; //Finds the key for current label foreach ($iterator as $chartPoint) { if ($chartPoint->dt->format('Y-m-d H:00') !== $date) { $prevcp = $chartPoint; continue; } $cp = $chartPoint; break; } if (!isset($cp)) { throw new OutOfRangeException(sprintf('Requested date (%s) is out of the range. Last point date is %s', $date, isset($prevcp->dt) ? $prevcp->dt->format('Y-m-d H:00') : 'undefined')); } $result = []; //Maximum number of the farms without grouping $max = 5; if (!empty($usg['data'][$cp->key]['data'][$platform]['data'])) { $usgFarms = new AggregationCollection(['farmId' => ['envId']], ['cost' => 'sum']); $ptr = $usg['data'][$cp->key]['data'][$platform]['data']; uasort($ptr, function ($a, $b) { if ($a['cost'] == $b['cost']) { return 0; } return $a['cost'] > $b['cost'] ? -1 : 1; }); //Aggregates farms if its number more then max + 1 if (count($ptr) > $max + 1) { $this->otherFarmsQuantity = count($ptr) - $max; $new = []; $i = 0; foreach ($ptr as $farmId => $v) { $v['cost_percentage'] = round($usg['data'][$cp->key]['data'][$platform]['cost'] == 0 ? 0 : $v['cost'] * 100 / $usg['data'][$cp->key]['data'][$platform]['cost'], 0); if ($i < $max) { $new[$farmId] = $v; } elseif (!isset($new[self::EVERYTHING_ELSE])) { $v['id'] = self::EVERYTHING_ELSE; $new[self::EVERYTHING_ELSE] = $v; } else { $new[self::EVERYTHING_ELSE]['cost'] += $v['cost']; } $i++; } $new[self::EVERYTHING_ELSE]['cost_percentage'] = round($usg['data'][$cp->key]['data'][$platform]['cost'] == 0 ? 0 : $new[self::EVERYTHING_ELSE]['cost'] * 100 / $usg['data'][$cp->key]['data'][$platform]['cost'], 0); $usgFarms->setData(['data' => $new]); } else { $usgFarms->setData($usg['data'][$cp->key]['data'][$platform])->calculatePercentage(); } //Forms result data array foreach ($usgFarms->getIterator() as $farmId => $pv) { $record = $this->getDetailedPointDataArray($farmId, $this->fetchFarmName($farmId), $pv, isset($prevUsg['data'][$cp->previousPeriodKey]['data'][$platform]['data'][$farmId]) ? $prevUsg['data'][$cp->previousPeriodKey]['data'][$platform]['data'][$farmId] : null, isset($usg['data'][$cp->key]['data'][$platform]['data'][$farmId]) ? $usg['data'][$cp->key]['data'][$platform]['data'][$farmId] : null); if ($farmId && $farmId != self::EVERYTHING_ELSE && !empty($pv['envId'])) { $record['environment'] = ['id' => (int) $pv['envId'], 'name' => AccountTagEntity::fetchName($pv['envId'], TagEntity::TAG_ID_ENVIRONMENT)]; } $result[] = $record; } } return ['data' => $result]; }
public function xSaveAction() { $this->request->defineParams(array('projectId' => ['type' => 'string'], 'year' => ['type' => 'int'], 'quarters' => ['type' => 'json'], 'selectedQuarter' => ['type' => 'string'])); $year = $this->getParam('year'); $selectedQuarter = $this->getParam('selectedQuarter'); if ($selectedQuarter !== 'year' && ($selectedQuarter < 1 || $selectedQuarter > 4)) { throw new OutOfBoundsException(sprintf("Invalid selectedQuarter number.")); } $quarterReq = []; foreach ($this->getParam('quarters') as $q) { if (!isset($q['quarter'])) { throw new InvalidArgumentException(sprintf("Missing quarter property for quarters data set in the request.")); } if ($q['quarter'] < 1 || $q['quarter'] > 4) { throw new OutOfRangeException(sprintf("Quarter value should be between 1 and 4.")); } if (!isset($q['budget'])) { throw new InvalidArgumentException(sprintf("Missing budget property for quarters data set in the request.")); } $quarterReq[$q['quarter']] = $q; } if ($this->getParam('projectId')) { $subjectType = QuarterlyBudgetEntity::SUBJECT_TYPE_PROJECT; $subjectId = $this->getParam('projectId'); $subjectEntity = ProjectEntity::findPk($subjectId); /* @var $subjectEntity ProjectEntity */ if ($subjectEntity->accountId != $this->user->getAccountId() || $subjectEntity->shared != ProjectEntity::SHARED_WITHIN_ACCOUNT) { throw new Scalr_Exception_InsufficientPermissions(); } } else { throw new InvalidArgumentException(sprintf('ProjectId must be provided with the request.')); } if (!preg_match("/^[[:xdigit:]-]{36}\$/", $subjectId)) { throw new InvalidArgumentException(sprintf("Invalid UUID has been passed.")); } if (!preg_match('/^\\d{4}$/', $year)) { throw new InvalidArgumentException(sprintf("Invalid year has been passed.")); } //Fetches the previous state of the entities from database $collection = QuarterlyBudgetEntity::getProjectBudget($year, $subjectId); $quarters = new Quarters(SettingEntity::getQuarters(true)); //Updates|creates entities for ($quarter = 1; $quarter <= 4; ++$quarter) { if (!isset($quarterReq[$quarter])) { continue; } $period = $quarters->getPeriodForQuarter($quarter, $year); //Checks if period has already been closed and forbids update if ($period->end->format('Y-m-d') < gmdate('Y-m-d')) { continue; } $entity = current($collection->filterByQuarter($quarter)); if ($entity instanceof QuarterlyBudgetEntity) { //We should update an entity $entity->budget = abs((double) $quarterReq[$quarter]['budget']); } else { //We should create a new one. $entity = new QuarterlyBudgetEntity($year, $quarter); $entity->subjectType = $subjectType; $entity->subjectId = $subjectId; $entity->budget = abs((double) $quarterReq[$quarter]['budget']); } $entity->save(); } if ($selectedQuarter == 'year') { $selectedPeriod = $quarters->getPeriodForYear($year); } else { $selectedPeriod = $quarters->getPeriodForQuarter($selectedQuarter, $year); } $data = $this->getProjectData(ProjectEntity::findPk($subjectId), $selectedPeriod, true); $budgetInfo = $this->getBudgetInfo($year, $data['ccId'], $data['projectId']); $this->response->data(['data' => $data, 'budgetInfo' => $budgetInfo]); $this->response->success('Budget changes have been saved'); }
/** * Gets project properties and parameters * * @param ProjectEntity $projectEntity Project entity * @return array Returns cost centre properties and parameters */ private function getProjectData(ProjectEntity $projectEntity) { $ret = array('projectId' => $projectEntity->projectId, 'name' => $projectEntity->name, 'ccId' => $projectEntity->ccId, 'ccName' => $projectEntity->getCostCenter() !== null ? $projectEntity->getCostCenter()->name : null, 'billingCode' => $projectEntity->getProperty(ProjectPropertyEntity::NAME_BILLING_CODE), 'description' => $projectEntity->getProperty(ProjectPropertyEntity::NAME_DESCRIPTION), 'leadEmail' => $projectEntity->getProperty(ProjectPropertyEntity::NAME_LEAD_EMAIL), 'created' => $projectEntity->created->format('Y-m-d'), 'createdByEmail' => $projectEntity->createdByEmail, 'archived' => $projectEntity->archived, 'shared' => $projectEntity->shared); if (!empty($projectEntity->accountId) && $projectEntity->shared === ProjectEntity::SHARED_WITHIN_ACCOUNT) { $ret['accountId'] = $projectEntity->accountId; $ret['accountName'] = Scalr_Account::init()->loadById($projectEntity->accountId)->name; } elseif (!empty($projectEntity->envId) && $projectEntity->shared === ProjectEntity::SHARED_WITHIN_ENV) { $ret['accountId'] = $projectEntity->accountId; $ret['accountName'] = Scalr_Account::init()->loadById($projectEntity->accountId)->name; $ret['envId'] = $projectEntity->envId; $ret['envName'] = \Scalr_Environment::init()->loadById($projectEntity->envId)->name; } return $ret; }