/** * Fill information about Farm/FarmRole for each object based on cloudServerId. * cloudServerId could be empty or didn't exist in our database. * * @param array[] $data Array of arrays */ private function applyFarmRoleInfo(&$data) { $cloudServerIds = []; foreach ($data as $row) { if ($row['cloudServerId']) { $cloudServerIds[] = $row['cloudServerId']; } } if (empty($cloudServerIds)) { return; } $server = new Entity\Server(); $history = new Entity\Server\History(); $farm = new Entity\Farm(); $farmRole = new Entity\FarmRole(); $cloudServerIds = join(",", array_map(function ($serverId) { return $this->db->qstr($serverId); }, $cloudServerIds)); $sql = "\n SELECT {$farm->columnId} AS farmId, {$farm->columnName} AS farmName, {$farmRole->columnId} AS farmRoleId,\n {$farmRole->columnAlias} AS farmRoleName, {$server->columnServerId} AS serverId, {$server->columnIndex} AS serverIndex,\n {$history->columnCloudServerId} AS cloudServerId FROM {$server->table()}\n JOIN {$history->table()} ON {$server->columnServerId} = {$history->columnServerId}\n JOIN {$farm->table()} ON {$server->columnFarmId} = {$farm->columnId}\n JOIN {$farmRole->table()} ON {$server->columnFarmRoleId} = {$farmRole->columnId}\n WHERE {$server->columnEnvId} = ? AND {$history->columnCloudServerId} IN ({$cloudServerIds})\n "; $result = []; foreach ($this->db->Execute($sql, [$this->getEnvironmentId()]) as $row) { $result[$row['cloudServerId']] = $row; } foreach ($data as &$row) { if (!empty($row['cloudServerId']) && !empty($result[$row['cloudServerId']])) { $row = array_merge($row, $result[$row['cloudServerId']]); } } }
public static function targetToData(OrchestrationRule $entity, $object) { /* @var $entity FarmRoleScript */ parent::targetToData($entity, $object); if ($entity->target == static::TARGET_VALUE_SPECIFIED_FARM_ROLE) { /* @var $entity FarmRoleScript */ $farmRole = new FarmRole(); $targets = new FarmRoleScriptingTarget(); /* @var $target FarmRole */ foreach (FarmRole::find([['farmId' => $entity->farmId], AbstractEntity::STMT_FROM => "{$farmRole->table()} JOIN {$targets->table('t')} ON {$targets->columnTarget('t')} = {$farmRole->columnAlias()}", AbstractEntity::STMT_WHERE => "{$targets->columnFarmRoleScriptId('t')} = {$targets->qstr('farmRoleScriptId', $entity->id)} AND {$targets->columnTargetType('t')} = {$targets->qstr('targetType', OrchestrationRule::TARGET_ROLES)}"]) as $target) { $object->target->roles[] = ['id' => $target->id]; } } }
/** * @param string $query * @throws Scalr_Exception_Core */ public function xSearchResourcesAction($query) { if (trim($query) == '') { $this->response->data(['data' => []]); return; } $environments = $this->request->getScope() == ScopeInterface::SCOPE_ACCOUNT ? array_map(function ($r) { return $r['id']; }, $this->user->getEnvironments()) : [$this->getEnvironmentId()]; $f = new Entity\Farm(); $s = new Entity\Server(); $fr = new Entity\FarmRole(); $e = new Entity\Account\Environment(); $at = new Entity\Account\Team(); $sp = new Entity\Server\Property(); $farmSql = []; $serverSql = []; $queryEnc = "%{$query}%"; foreach ($environments as $envId) { $acl = $this->user->getAclRolesByEnvironment($envId); $isTermporaryServerPerm = $acl->isAllowed(Acl::RESOURCE_IMAGES_ENVIRONMENT, Acl::PERM_IMAGES_ENVIRONMENT_BUILD) || $acl->isAllowed(Acl::RESOURCE_IMAGES_ENVIRONMENT, Acl::PERM_IMAGES_ENVIRONMENT_IMPORT); if ($acl->isAllowed(Acl::RESOURCE_FARMS)) { $farmSql[] = "{$f->columnEnvId} = {$envId}"; if ($isTermporaryServerPerm) { $serverSql[] = "{$s->columnEnvId} = {$envId}"; } else { $serverSql[] = "{$s->columnEnvId} = {$envId} AND {$s->columnFarmId} IS NOT NULL"; } } else { $q = []; if ($acl->isAllowed(Acl::RESOURCE_TEAM_FARMS)) { $t = array_map(function ($t) { return $t['id']; }, $this->user->getTeams()); if (count($t)) { $q[] = "{$f->columnTeamId} IN (" . join(',', $t) . ")"; } } if ($acl->isAllowed(Acl::RESOURCE_OWN_FARMS)) { $q[] = "{$f->columnCreatedById} = {$this->user->getId()}"; } if (count($q)) { $farmSql[] = "{$f->columnEnvId} = {$envId} AND (" . join(" OR ", $q) . ")"; } if ($isTermporaryServerPerm) { $q[] = "{$s->columnStatus} IN ('" . Entity\Server::STATUS_IMPORTING . "', '" . Entity\Server::STATUS_TEMPORARY . "') AND {$s->columnFarmId} IS NULL"; } if (count($q)) { $serverSql[] = "{$s->columnEnvId} = {$envId} AND (" . join(" OR ", $q) . ")"; } } } $farms = []; if (count($farmSql)) { $farmStmt = $this->db->Execute("\n SELECT {$f->columnId} AS id, {$f->columnName} AS name, {$f->columnEnvId} AS envId, {$f->columnStatus} AS status,\n {$f->columnAdded} AS added, {$f->columnCreatedByEmail} AS createdByEmail, {$at->columnName} AS teamName, {$e->columnName} AS `envName`\n FROM {$f->table()}\n LEFT JOIN {$at->table()} ON {$at->columnId} = {$f->columnTeamId}\n LEFT JOIN {$e->table()} ON {$f->columnEnvId} = {$e->columnId}\n WHERE ({$f->columnName} LIKE ?) AND (" . join(" OR ", $farmSql) . ")", [$queryEnc]); while ($farm = $farmStmt->FetchRow()) { $farm['status'] = Entity\Farm::getStatusName($farm['status']); $farm['added'] = Scalr_Util_DateTime::convertTz($farm['added'], 'M j, Y H:i'); $farms[] = ['entityName' => 'farm', 'envId' => $farm['envId'], 'envName' => $farm['envName'], 'matchField' => 'Name', 'matchValue' => $farm['name'], 'data' => $farm]; } } $servers = []; if (count($serverSql)) { $serverStmt = $this->db->Execute("\n SELECT {$s->columnServerId} AS serverId, {$s->columnFarmId} AS farmId, {$s->columnFarmRoleId} AS farmRoleId,\n {$s->columnEnvId} AS envId, {$s->columnPlatform} AS platform, {$s->columnInstanceTypeName} AS instanceTypeName,\n {$s->columnStatus} AS status, {$s->columnCloudLocation} AS cloudLocation, {$s->columnRemoteIp} AS publicIp,\n {$s->columnLocalIp} AS privateIp, {$s->columnAdded} AS added, {$f->columnName} AS farmName,\n {$fr->columnAlias} AS farmRoleName, {$e->columnName} AS `envName`, {$fr->columnRoleId} AS roleId,\n {$sp->columnValue('sp1', 'hostname')}\n FROM {$s->table()}\n LEFT JOIN {$f->table()} ON {$f->columnId} = {$s->columnFarmId}\n LEFT JOIN {$fr->table()} ON {$fr->columnId} = {$s->columnFarmRoleId}\n LEFT JOIN {$e->table()} ON {$e->columnId} = {$s->columnEnvId}\n LEFT JOIN {$sp->table('sp1')} ON {$sp->columnServerId('sp1')} = {$s->columnServerId} AND {$sp->columnName('sp1')} = ?\n WHERE ({$s->columnRemoteIp} LIKE ? OR {$s->columnLocalIp} LIKE ? OR {$sp->columnValue('sp1')} LIKE ?) AND (" . join(" OR ", $serverSql) . ")\n GROUP BY {$s->columnServerId}", [Scalr_Role_Behavior::SERVER_BASE_HOSTNAME, $queryEnc, $queryEnc, $queryEnc]); $names = ['publicIp' => 'Public IP', 'privateIp' => 'Private IP', 'hostname' => 'Hostname']; while ($server = $serverStmt->FetchRow()) { $server['added'] = Scalr_Util_DateTime::convertTz($server['added'], 'M j, Y H:i'); if (strstr($server['publicIp'], $query)) { $m = 'publicIp'; } else { if (strstr($server['privateIp'], $query)) { $m = 'privateIp'; } else { $m = 'hostname'; } } $servers[] = ['entityName' => 'server', 'envId' => $server['envId'], 'envName' => $server['envName'], 'matchField' => $names[$m], 'matchValue' => $server[$m], 'data' => $server]; } } $this->response->data(['data' => array_merge($farms, $servers)]); }
/** * {@inheritdoc} * @see ApiEntityAdapter::validateEntity() */ public function validateEntity($entity) { if (!$entity instanceof FarmRole) { throw new InvalidArgumentException(sprintf("First argument must be instance of Scalr\\Model\\Entity\\FarmRole class")); } if ($entity->id !== null) { if (!FarmRole::findPk($entity->id)) { throw new ApiErrorException(404, ErrorMessage::ERR_OBJECT_NOT_FOUND, sprintf("Could not find out the Farm with ID: %d", $entity->id)); } } if (empty($entity->farmId)) { throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_STRUCTURE, "Missed property farm.id"); } else { /* @var $farm Farm */ $farm = $this->controller->getFarm($entity->farmId, true); } if (empty($entity->alias)) { throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_STRUCTURE, "Missed property alias"); } if (!preg_match("/^[[:alnum:]](?:-*[[:alnum:]])*\$/", $entity->alias)) { throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, "Alias should start and end with letter or number and contain only letters, numbers and dashes."); } if (empty($entity->roleId)) { throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_STRUCTURE, "Missed property role.id"); } $roleBehaviors = $entity->getRole()->getBehaviors(); $uniqueBehaviors = array_intersect($roleBehaviors, array_merge(...array_values(static::$uniqueFarmBehaviors))); if (!empty($uniqueBehaviors)) { //farm can include only one mysql or percona role if (array_intersect($uniqueBehaviors, static::$uniqueFarmBehaviors[ROLE_BEHAVIORS::MYSQL])) { $uniqueBehaviors = array_merge($uniqueBehaviors, array_diff(static::$uniqueFarmBehaviors[ROLE_BEHAVIORS::MYSQL], $uniqueBehaviors)); } $farmRoleEntity = new FarmRole(); $roleEntity = new Role(); /* @var $conflicts EntityIterator */ $conflicts = Role::find([AbstractEntity::STMT_FROM => "{$roleEntity->table()} JOIN {$farmRoleEntity->table('fr')} ON {$farmRoleEntity->columnRoleId('fr')} = {$roleEntity->columnId()}", AbstractEntity::STMT_WHERE => "{$farmRoleEntity->columnFarmId('fr')} = {$farmRoleEntity->qstr('farmId', $entity->farmId)} " . (empty($entity->id) ? '' : " AND {$farmRoleEntity->columnId('fr')} <> {$farmRoleEntity->qstr('id', $entity->id)}"), ['behaviors' => ['$regex' => implode('|', $uniqueBehaviors)]]]); if ($conflicts->count() > 0) { $conflictedBehaviors = []; /* @var $role Role */ foreach ($conflicts as $role) { $conflictedBehaviors = array_merge($conflictedBehaviors, array_intersect($uniqueBehaviors, $role->getBehaviors())); } if (!empty(array_intersect($conflictedBehaviors, static::$uniqueFarmBehaviors[ROLE_BEHAVIORS::MYSQL]))) { $conflictedBehaviors = array_diff($conflictedBehaviors, static::$uniqueFarmBehaviors[ROLE_BEHAVIORS::MYSQL]); $conflictedBehaviors[] = 'mysql/percona'; } $conflictedBehaviors = RoleAdapter::behaviorsToData($conflictedBehaviors); throw new ApiErrorException(409, ErrorMessage::ERR_UNICITY_VIOLATION, sprintf('Only one [%s] role can be added to farm', implode(', ', $conflictedBehaviors))); } } if (empty($entity->platform)) { throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_STRUCTURE, "Missed property platform"); } switch ($entity->platform) { case SERVER_PLATFORMS::EC2: if (empty($entity->settings[FarmRoleSetting::INSTANCE_TYPE])) { throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, "Missed property instance.type"); } /* @var $platform Ec2PlatformModule */ $platform = PlatformFactory::NewPlatform(SERVER_PLATFORMS::EC2); if (!in_array($entity->settings[FarmRoleSetting::INSTANCE_TYPE], $platform->getInstanceTypes())) { throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, "Wrong instance type"); } $gov = new Scalr_Governance($this->controller->getEnvironment()->id); $allowGovernanceIns = $gov->getValue(SERVER_PLATFORMS::EC2, Scalr_Governance::INSTANCE_TYPE); if (isset($allowGovernanceIns) && !in_array($entity->settings[FarmRoleSetting::INSTANCE_TYPE], $allowGovernanceIns)) { throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, sprintf("Only %s %s allowed according to governance settings", ...count($allowGovernanceIns) > 1 ? [implode(', ', $allowGovernanceIns), 'instances are'] : [array_shift($allowGovernanceIns), 'instance is'])); } if (!in_array($entity->cloudLocation, Aws::getCloudLocations())) { throw new ApiErrorException(404, ErrorMessage::ERR_OBJECT_NOT_FOUND, "Unknown region"); } $vpcGovernanceRegions = $gov->getValue(SERVER_PLATFORMS::EC2, Scalr_Governance::AWS_VPC, 'regions'); if (isset($vpcGovernanceRegions) && !array_key_exists($entity->cloudLocation, $vpcGovernanceRegions)) { $regions = array_keys($vpcGovernanceRegions); throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, sprintf("Only %s %s allowed according to governance settings", ...count($regions) > 1 ? [implode(', ', $regions), 'regions are'] : [array_shift($regions), 'region is'])); } $env = Scalr_Environment::init()->loadById($this->controller->getEnvironment()->id); $aws = $this->controller->getContainer()->aws($entity->cloudLocation, $env); if (!empty($entity->settings[FarmRoleSetting::AWS_AVAIL_ZONE]) && $entity->settings[FarmRoleSetting::AWS_AVAIL_ZONE] !== 'x-scalr-diff') { $availZones = explode(":", str_replace("x-scalr-custom=", '', $entity->settings[FarmRoleSetting::AWS_AVAIL_ZONE])); $ec2availabilityZones = []; foreach ($aws->ec2->availabilityZone->describe() as $zone) { /* @var $zone AvailabilityZoneData */ if (stristr($zone->zoneState, 'available')) { $ec2availabilityZones[] = $zone->zoneName; } } $diffZones = array_diff($availZones, $ec2availabilityZones); if (!empty($diffZones)) { throw new ApiErrorException(404, ErrorMessage::ERR_OBJECT_NOT_FOUND, sprintf('%s %s available. Available zones are %s', ...count($diffZones) > 1 ? [implode(', ', $diffZones), 'zones are not', implode(', ', $ec2availabilityZones)] : [array_shift($diffZones), 'zone is not', implode(', ', $ec2availabilityZones)])); } } if (!empty($farm->settings[FarmSetting::EC2_VPC_ID])) { if (empty($entity->settings[FarmRoleSetting::AWS_VPC_SUBNET_ID])) { throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_STRUCTURE, "VPC Subnet(s) should be described"); } $vpcId = $farm->settings[FarmSetting::EC2_VPC_ID]; $subnets = $platform->listSubnets($env, $entity->cloudLocation, $vpcId, true); $vpcGovernanceIds = $gov->getValue(SERVER_PLATFORMS::EC2, Scalr_Governance::AWS_VPC, 'ids'); $subnetType = null; foreach (json_decode($entity->settings[FarmRoleSetting::AWS_VPC_SUBNET_ID]) as $subnetId) { $found = false; foreach ($subnets as $subnet) { if ($subnet['id'] == $subnetId) { if ($subnetType == null) { $subnetType = $subnet['type']; } else { if ($subnet['type'] != $subnetType) { throw new ApiErrorException(409, ErrorMessage::ERR_UNICITY_VIOLATION, "All subnets must be a same type"); } } //check governance subnet settings if (isset($vpcGovernanceIds[$vpcId])) { if (!empty($vpcGovernanceIds[$vpcId]) && is_array($vpcGovernanceIds[$vpcId]) && !in_array($subnetId, $vpcGovernanceIds[$vpcId])) { throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, sprintf("Only %s %s allowed by governance settings", ...count($vpcGovernanceIds[$vpcId]) > 1 ? [implode(', ', $vpcGovernanceIds[$vpcId]), 'subnets are'] : [array_shift($vpcGovernanceIds[$vpcId]), 'subnet is'])); } else { if ($vpcGovernanceIds[$vpcId] == "outbound-only" && $subnetType != 'private') { throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, "Only private subnets allowed by governance settings"); } else { if ($vpcGovernanceIds[$vpcId] == "full" && $subnetType != 'public') { throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, "Only public subnets allowed by governance settings"); } } } } $found = true; } } if (!$found) { throw new ApiErrorException(404, ErrorMessage::ERR_OBJECT_NOT_FOUND, "Subnet with id '{$subnetId}' not found"); } } if (!empty($entity->settings[Scalr_Role_Behavior_Router::ROLE_VPC_SCALR_ROUTER_ID])) { $router = $this->controller->getFarmRole($entity->settings[Scalr_Role_Behavior_Router::ROLE_VPC_SCALR_ROUTER_ID]); if (empty($router->settings[Scalr_Role_Behavior_Router::ROLE_VPC_NID])) { throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, "Farm-role with id '{$router->id}' is not a valid router"); } } else { if (\Scalr::config('scalr.instances_connection_policy') != 'local' && $subnetType == 'private') { throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_STRUCTURE, "You must describe a VPC Router"); } } } break; default: if (isset(SERVER_PLATFORMS::GetList()[$entity->platform])) { throw new ApiErrorException(501, ErrorMessage::ERR_NOT_IMPLEMENTED, "Platform '{$entity->platform}' is not supported yet"); } else { throw new ApiErrorException(404, ErrorMessage::ERR_OBJECT_NOT_FOUND, "Unknown platform '{$entity->platform}'"); } } if (!$this->controller->hasPermissions($entity, true)) { //Checks entity level write access permissions throw new ApiErrorException(403, ErrorMessage::ERR_PERMISSION_VIOLATION, "Insufficient permissions"); } }