/** * @return Role[] */ public function getTestRoles() { $roles = Role::find([['envId' => $this->getEnvironment()->id]]); return array_filter(iterator_to_array($roles), function (Role $role) { $dbRole = \DBRole::loadById($role->id); $farms = $dbRole->getFarms(); foreach ($farms as $farmId) { $farm = \DBFarm::LoadByID($farmId); if ($farm->Status) { return false; } } return true; }); }
/** * @param string $platform Name of platform * @param string $cloudLocation Name of location * @param array $ids The list of the identifiers of the cloud instances * @param int $roleId optional Identifier of Role * @return array * @throws Exception */ public function checkStatus($platform, $cloudLocation, $ids, $roleId = null) { if (!in_array($platform, $this->allowedPlatforms)) { throw new Exception(sprintf("Platform '%s' is not supported", $platform)); } if (!$this->environment->isPlatformEnabled($platform)) { throw new Exception(sprintf("Platform '%s' is not enabled", $platform)); } if (empty($ids) || empty($ids[0])) { throw new Exception("You should provide at least one instanceId"); } $instances = PlatformFactory::NewPlatform($platform)->getOrphanedServers($this->getEnvironmentEntity(), $cloudLocation, $ids); $status = ['instances' => $instances]; $imageIds = array_unique(array_map(function ($item) { return $item->imageId; }, $instances)); if (count($imageIds) != 1) { $status['compatibility'] = ['success' => false]; return $status; } /* @var $instance OrphanedServer */ $instance = $instances[0]; $status['compatibility'] = ['success' => true]; // Check vpc compatibility if ($instance->vpcId && $instance->subnetId) { $gov = new Scalr_Governance($this->getEnvironmentId()); $vpcGovernanceRegions = $gov->getValue(SERVER_PLATFORMS::EC2, Scalr_Governance::AWS_VPC, 'regions'); if (isset($vpcGovernanceRegions)) { if (!array_key_exists($cloudLocation, $vpcGovernanceRegions)) { $status['compatibility']['success'] = false; $status['compatibility']['message'] = sprintf('Region <b>%s</b> is not allowed by the Governance.', $cloudLocation); } else { $vpcGovernanceIds = $vpcGovernanceRegions[$cloudLocation]['ids']; if (!empty($vpcGovernanceIds) && !in_array($instance->vpcId, $vpcGovernanceIds)) { $status['compatibility']['success'] = false; $status['compatibility']['message'] = sprintf('VPC <b>%s</b> is not allowed by the Governance.', $instance->vpcId); } else { $vpcGovernanceIds = $gov->getValue(SERVER_PLATFORMS::EC2, Scalr_Governance::AWS_VPC, 'ids'); /* @var $platformObject Ec2PlatformModule */ $platformObject = PlatformFactory::NewPlatform(SERVER_PLATFORMS::EC2); $subnet = $platformObject->listSubnets($this->getEnvironment(), $cloudLocation, $instance->vpcId, true, $instance->subnetId); if (isset($vpcGovernanceIds[$instance->vpcId])) { if (!empty($vpcGovernanceIds[$instance->vpcId]) && is_array($vpcGovernanceIds[$instance->vpcId]) && !in_array($instance->subnetId, $vpcGovernanceIds[$instance->vpcId])) { $status['compatibility']['success'] = false; $status['compatibility']['message'] = sprintf('Subnet <b>%s</b> is prohibited by the Governance.', $instance->subnetId); } else { if ($vpcGovernanceIds[$instance->vpcId] == "outbound-only" && $subnet['type'] != 'private') { $status['compatibility']['success'] = false; $status['compatibility']['message'] = 'Only private subnets are allowed by the Governance.'; } else { if ($vpcGovernanceIds[$instance->vpcId] == "full" && $subnet['type'] != 'public') { $status['compatibility']['success'] = false; $status['compatibility']['message'] = 'Only public subnets are allowed by the Governance.'; } } } } } } } } if (!$status['compatibility']['success']) { return $status; } $scopeCriteria = ['$or' => [['accountId' => null], ['$and' => [['accountId' => $this->getUser()->accountId], ['$or' => [['envId' => null], ['envId' => $this->getEnvironment()->id]]]]]]]; /* @var $image Entity\Image */ $image = Entity\Image::findOne([['platform' => $platform], ['cloudLocation' => $cloudLocation], ['id' => $instance->imageId], $scopeCriteria]); $status['image'] = ['success' => !!$image, 'data' => ['id' => $instance->imageId]]; if ($image) { if ($image->isScalarized) { $status['image']['success'] = false; $status['image']['isScalarized'] = true; return $status; } $status['image']['data'] = ['hash' => $image->hash, 'name' => $image->name, 'id' => $image->id, 'scope' => $image->getScope()]; $criteria = [['platform' => $platform], ['imageId' => $image->id]]; if (!($platform == SERVER_PLATFORMS::GCE || $platform == SERVER_PLATFORMS::AZURE)) { $criteria[] = ['cloudLocation' => $cloudLocation]; } $roleIds = []; foreach (Entity\RoleImage::find($criteria) as $ri) { $roleIds[] = $ri->roleId; } if (count($roleIds)) { $roles = Entity\Role::find([['id' => ['$in' => $roleIds]], ['isScalarized' => false], $scopeCriteria]); $status['role'] = ['availableRoles' => [], 'image' => Scalr_UI_Controller_Images::controller()->convertEntityToArray($image)]; $selectedRole = null; if (count($roles) == 1) { $selectedRole = $roles->current(); } else { if ($roleId && in_array($roleId, $roleIds)) { foreach ($roles as $role) { /* @var $role Entity\Role */ if ($role->id == $roleId) { $selectedRole = $role; break; } } } } foreach ($roles as $role) { /* @var $role Entity\Role */ $status['role']['availableRoles'][] = ['id' => $role->id, 'name' => $role->name, 'scope' => $role->getScope()]; } if ($selectedRole) { $status['role']['success'] = true; $status['role']['data'] = ['id' => $selectedRole->id, 'name' => $selectedRole->name, 'scope' => $selectedRole->getScope()]; $farms = []; $status['farmrole'] = ['instance' => ['instanceType' => $instance->instanceType, 'vpcId' => $instance->vpcId, 'subnetId' => $instance->subnetId, 'roleName' => $selectedRole->name]]; foreach (Entity\Farm::find([['envId' => $this->getEnvironment()->id], ['status' => FARM_STATUS::RUNNING]]) as $farm) { /* @var $farm Entity\Farm */ if ($this->request->hasPermissions($farm, Acl::PERM_FARMS_UPDATE) && $this->request->hasPermissions($farm, Acl::PERM_FARMS_SERVERS)) { // cloud specific (EC2) if ($farm->settings[Entity\FarmSetting::EC2_VPC_ID] == $instance->vpcId) { $farms[$farm->id] = ['id' => $farm->id, 'name' => $farm->name, 'farmroles' => []]; } } } foreach (Entity\FarmRole::find([['farmId' => ['$in' => array_keys($farms)]], ['roleId' => $selectedRole->id]]) as $farmRole) { /* @var $farmRole Entity\FarmRole */ if (isset($farms[$farmRole->farmId])) { if (!$instance->subnetId || $instance->subnetId && in_array($instance->subnetId, json_decode($farmRole->settings[Entity\FarmRoleSetting::AWS_VPC_SUBNET_ID]))) { $farms[$farmRole->farmId]['farmroles'][] = ['id' => $farmRole->id, 'name' => $farmRole->alias, 'tags' => $farmRole->getCloudTags(true)]; } } } $status['farmrole']['data'] = array_values($farms); $status['farmrole']['success'] = false; } else { if (count($roles) > 1) { $status['role']['success'] = false; } else { $status['role']['success'] = false; } } } else { $status['role']['success'] = false; $status['role']['image'] = Scalr_UI_Controller_Images::controller()->convertEntityToArray($image); } } return $status; }
/** * {@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"); } }