isInstanceTypeAllowed() public method

Checks if instance type is allowed
public isInstanceTypeAllowed ( string $platform, string $cloudLocation, string $instanceType ) : boolean
$platform string Platform
$cloudLocation string Cloud location
$instanceType string Instance type
return boolean Returns true if instance type is allowed
コード例 #1
0
ファイル: Builder.php プロジェクト: scalr/scalr
 /**
  *
  * @param array $farmSettings
  * @param array $roles
  * @return bool
  */
 private function isFarmConfigurationValid($farmId, $farmSettings, array $roles = array())
 {
     $this->errors = array('error_count' => 0);
     $farmVariables = new Scalr_Scripting_GlobalVariables($this->user->getAccountId(), $this->getEnvironmentId(), ScopeInterface::SCOPE_FARM);
     $farmRoleVariables = new Scalr_Scripting_GlobalVariables($this->user->getAccountId(), $this->getEnvironmentId(), ScopeInterface::SCOPE_FARMROLE);
     $name = $this->request->stripValue($farmSettings['name']);
     if (empty($name)) {
         $this->setBuildError('name', 'Farm name is invalid');
     }
     $timezone = $this->request->stripValue($farmSettings['timezone']);
     if (empty($timezone)) {
         $this->setBuildError('timezone', 'Farm timezone is invalid');
     }
     if ($farmSettings['variables']) {
         $result = $farmVariables->validateValues(is_array($farmSettings['variables']) ? $farmSettings['variables'] : [], 0, $farmId);
         if ($result !== TRUE) {
             $this->setBuildError('variables', $result);
         }
     }
     if (is_numeric($farmSettings['owner'])) {
         try {
             $u = (new Scalr_Account_User())->loadById($farmSettings['owner']);
             if ($u->getAccountId() != $this->user->getAccountId()) {
                 throw new Exception('User not found');
             }
         } catch (Exception $e) {
             $this->setBuildError('owner', $e->getMessage());
         }
     }
     if (is_array($farmSettings['teamOwner'])) {
         foreach ($farmSettings['teamOwner'] as $name) {
             if (!Entity\Account\Team::findOne([['name' => $name], ['accountId' => $this->getUser()->accountId]])) {
                 $this->setBuildError('teamOwner', sprintf("Team '%s' not found", $name));
             }
         }
     }
     if (!empty($roles)) {
         $hasVpcRouter = false;
         $vpcRouterRequired = false;
         $governance = new Scalr_Governance($this->getEnvironmentId());
         foreach ($roles as $role) {
             $dbRole = DBRole::loadById($role['role_id']);
             if (!$this->request->hasPermissions($dbRole->__getNewRoleObject())) {
                 $this->setBuildError($dbRole->name, 'You don\'t have access to this role');
             }
             try {
                 $dbRole->__getNewRoleObject()->getImage($role['platform'], $role['cloud_location']);
             } catch (Exception $e) {
                 $this->setBuildError($dbRole->name, sprintf("Role '%s' is not available in %s on %s", $dbRole->name, $role['platform'], $role['cloud_location']));
             }
             if (empty($role['settings'][Entity\FarmRoleSetting::INSTANCE_TYPE])) {
                 $this->setBuildError(Entity\FarmRoleSetting::INSTANCE_TYPE, 'Instance type is required.', $role['farm_role_id']);
             } else {
                 if (!$governance->isInstanceTypeAllowed($role['platform'], $role['cloud_location'], $role['settings'][Entity\FarmRoleSetting::INSTANCE_TYPE])) {
                     $this->setBuildError(Entity\FarmRoleSetting::INSTANCE_TYPE, 'Instance type is not allowed by governance.', $role['farm_role_id']);
                 }
             }
             if ($role['alias']) {
                 if (!preg_match("/^[[:alnum:]](?:-*[[:alnum:]])*\$/", $role['alias'])) {
                     $this->setBuildError('alias', 'Alias should start and end with letter or number and contain only letters, numbers and dashes.', $role['farm_role_id']);
                 }
             }
             if ($dbRole->hasBehavior(ROLE_BEHAVIORS::VPC_ROUTER)) {
                 $hasVpcRouter = true;
             }
             if ($dbRole->hasBehavior(ROLE_BEHAVIORS::RABBITMQ)) {
                 $role['settings'][Entity\FarmRoleSetting::SCALING_MAX_INSTANCES] = $role['settings'][Entity\FarmRoleSetting::SCALING_MIN_INSTANCES];
                 $role['settings'][Scalr_Role_Behavior_RabbitMQ::ROLE_NODES_RATIO] = (int) $role['settings'][Scalr_Role_Behavior_RabbitMQ::ROLE_NODES_RATIO];
                 if ($role['settings'][Scalr_Role_Behavior_RabbitMQ::ROLE_NODES_RATIO] < 1 || $role['settings'][Scalr_Role_Behavior_RabbitMQ::ROLE_NODES_RATIO] > 100) {
                     $this->setBuildError(Scalr_Role_Behavior_RabbitMQ::ROLE_NODES_RATIO, 'Nodes ratio should be an integer between 1 and 100', $role['farm_role_id']);
                 } else {
                     $this->checkInteger($role['farm_role_id'], Scalr_Role_Behavior_RabbitMQ::ROLE_DATA_STORAGE_EBS_SIZE, $role['settings'][Scalr_Role_Behavior_RabbitMQ::ROLE_DATA_STORAGE_EBS_SIZE], 'Storage size', 1, 1000);
                 }
             }
             if ($dbRole->hasBehavior(ROLE_BEHAVIORS::MONGODB)) {
                 if ($role['settings'][Scalr_Role_Behavior_MongoDB::ROLE_DATA_STORAGE_ENGINE] == 'ebs') {
                     if ($role['settings'][Scalr_Role_Behavior_MongoDB::ROLE_DATA_STORAGE_EBS_SIZE] < 10 || $role['settings'][Scalr_Role_Behavior_MongoDB::ROLE_DATA_STORAGE_EBS_SIZE] > 1000) {
                         $this->setBuildError(Scalr_Role_Behavior_MongoDB::ROLE_DATA_STORAGE_EBS_SIZE, sprintf("EBS size for mongoDB role should be between 10 and 1000 GB", $dbRole->name), $role['farm_role_id']);
                     }
                 }
             }
             if ($dbRole->hasBehavior(ROLE_BEHAVIORS::NGINX)) {
                 $proxies = (array) @json_decode($role['settings'][Scalr_Role_Behavior_Nginx::ROLE_PROXIES], true);
                 foreach ($proxies as $proxyIndex => $proxy) {
                     if ($proxy['ssl'] == 1) {
                         if (empty($proxy['ssl_certificate_id'])) {
                             $this->setBuildError(Scalr_Role_Behavior_Nginx::ROLE_PROXIES, ['message' => 'SSL certificate is required', 'invalidIndex' => $proxyIndex], $role['farm_role_id']);
                             break;
                         }
                         if ($proxy['port'] == $proxy['ssl_port']) {
                             $this->setBuildError(Scalr_Role_Behavior_Nginx::ROLE_PROXIES, ['message' => 'HTTP and HTTPS ports cannot be the same', 'invalidIndex' => $proxyIndex], $role['farm_role_id']);
                         }
                     }
                     if (count($proxy['backends']) > 0) {
                         foreach ($proxy['backends'] as $backend) {
                             if (empty($backend['farm_role_id']) && empty($backend['farm_role_alias']) && empty($backend['host'])) {
                                 $this->setBuildError(Scalr_Role_Behavior_Nginx::ROLE_PROXIES, ['message' => 'Destination is required', 'invalidIndex' => $proxyIndex], $role['farm_role_id']);
                                 break;
                             }
                         }
                     }
                 }
             }
             /* Validate scaling */
             if ($role['settings'][Entity\FarmRoleSetting::SCALING_ENABLED] == 1 && !$dbRole->hasBehavior(ROLE_BEHAVIORS::VPC_ROUTER) && !$dbRole->hasBehavior(ROLE_BEHAVIORS::MONGODB)) {
                 $minCount = $this->checkInteger($role['farm_role_id'], Entity\FarmRoleSetting::SCALING_MIN_INSTANCES, $role['settings'][Entity\FarmRoleSetting::SCALING_MIN_INSTANCES], 'Min instances', 0, 400);
                 $maxCount = $this->checkInteger($role['farm_role_id'], Entity\FarmRoleSetting::SCALING_MAX_INSTANCES, $role['settings'][Entity\FarmRoleSetting::SCALING_MAX_INSTANCES], 'Max instances', 0, 400);
                 if ($minCount !== false && $maxCount !== false && $maxCount < $minCount) {
                     $this->setBuildError(Entity\FarmRoleSetting::SCALING_MAX_INSTANCES, 'Max instances should be greater than or equal to Min instances', $role['farm_role_id']);
                 }
                 $this->checkInteger($role['farm_role_id'], Entity\FarmRoleSetting::SCALING_POLLING_INTERVAL, $role['settings'][Entity\FarmRoleSetting::SCALING_POLLING_INTERVAL], 'Polling interval', 1, 50);
                 if (array_key_exists(Entity\FarmRoleSetting::SCALING_UPSCALE_TIMEOUT_ENABLED, $role["settings"]) && $role['settings'][Entity\FarmRoleSetting::SCALING_UPSCALE_TIMEOUT_ENABLED] == 1) {
                     $this->checkInteger($role['farm_role_id'], Entity\FarmRoleSetting::SCALING_UPSCALE_TIMEOUT, $role['settings'][Entity\FarmRoleSetting::SCALING_UPSCALE_TIMEOUT], 'Upscale timeout', 1);
                 }
                 if (array_key_exists(Entity\FarmRoleSetting::SCALING_DOWNSCALE_TIMEOUT_ENABLED, $role["settings"]) && $role['settings'][Entity\FarmRoleSetting::SCALING_DOWNSCALE_TIMEOUT_ENABLED] == 1) {
                     $this->checkInteger($role['farm_role_id'], Entity\FarmRoleSetting::SCALING_DOWNSCALE_TIMEOUT, $role['settings'][Entity\FarmRoleSetting::SCALING_DOWNSCALE_TIMEOUT], 'Downscale timeout', 1);
                 }
                 if (is_array($role['scaling'])) {
                     if (!isset($invertedMetrics)) {
                         $invertedMetrics = $this->db->getCol("\n                                  SELECT id\n                                  FROM scaling_metrics\n                                  WHERE is_invert = 1\n                                    AND (account_id IS NULL AND env_id IS NULL\n                                      OR account_id = ? AND env_id IS NULL\n                                      OR account_id = ? AND env_id = ?)\n                            ", [$this->user->getAccountId(), $this->user->getAccountId(), $this->getEnvironmentId()]) ?: [];
                     }
                     $metricErrors = [];
                     $metricErrorsCount = 0;
                     foreach ($role['scaling'] as $metricId => $metricSettings) {
                         if ($metricId == Entity\ScalingMetric::METRIC_DATE_AND_TIME_ID) {
                             continue;
                         }
                         $metricInverted = in_array($metricId, $invertedMetrics);
                         $validator = 'integer';
                         /* metric-specific parameters */
                         switch ($metricId) {
                             case Entity\ScalingMetric::METRIC_URL_RESPONSE_TIME_ID:
                                 //URL + int MIN,MAX
                                 if (($hasError = Validator::validateUrl($metricSettings['url'])) !== true) {
                                     $metricErrors[$metricId]['url'] = $hasError;
                                     $metricErrorsCount++;
                                 }
                                 break;
                             case Entity\ScalingMetric::METRIC_SQS_QUEUE_SIZE_ID:
                                 //QUEUE_NAME + int MIN,MAX
                                 if (($hasError = Validator::validateNotEmpty($metricSettings['queue_name'])) !== true) {
                                     $metricErrors[$metricId]['queue_name'] = $hasError;
                                     $metricErrorsCount++;
                                 }
                                 break;
                             case Entity\ScalingMetric::METRIC_LOAD_AVERAGES_ID:
                             case Entity\ScalingMetric::METRIC_FREE_RAM_ID:
                             case Entity\ScalingMetric::METRIC_BANDWIDTH_ID:
                             default:
                                 /* custom metrics */
                                 //float MIN,MAX
                                 $validator = 'float';
                         }
                         /* thresholds */
                         if ($validator === 'integer') {
                             $filterValidate = FILTER_VALIDATE_INT;
                             $boundsTypeName = 'integer';
                         } else {
                             $filterValidate = FILTER_VALIDATE_FLOAT;
                             $boundsTypeName = 'number';
                         }
                         $thresholdsErrors = [];
                         if (($minBound = filter_var($metricSettings['min'], $filterValidate)) === false) {
                             $thresholdsErrors['min'] = 'Scale ' . ($metricInverted ? 'up' : 'down') . ' value must be a valid ' . $boundsTypeName . '.';
                             $metricErrorsCount++;
                         }
                         if (($maxBound = filter_var($metricSettings['max'], $filterValidate)) === false) {
                             $thresholdsErrors['max'] = 'Scale ' . ($metricInverted ? 'down' : 'up') . ' value must be a valid ' . $boundsTypeName . '.';
                             $metricErrorsCount++;
                         }
                         if ($metricId != Entity\ScalingMetric::METRIC_SQS_QUEUE_SIZE_ID && $minBound !== false && $maxBound !== false && $this->checkBounds($role['farm_role_id'], 'scaling', $minBound, $maxBound, $validator, null, false) === false) {
                             $thresholdsErrors[$metricInverted ? 'min' : 'max'] = 'Scale up value must be ' . ($metricInverted ? 'less' : 'greater') . ' than Scale down value.';
                             $metricErrorsCount++;
                         }
                         if (!empty($thresholdsErrors)) {
                             if ($metricErrors[$metricId]) {
                                 $metricErrors[$metricId] = array_merge($metricErrors[$metricId], $thresholdsErrors);
                             } else {
                                 $metricErrors[$metricId] = $thresholdsErrors;
                             }
                         }
                     }
                     if (!empty($metricErrors)) {
                         $metricErrors['message'] = 'Scaling metric(s) settings are invalid.';
                         $this->setBuildError('scaling', $metricErrors, $role['farm_role_id']);
                         $this->errors['error_count'] += $metricErrorsCount - 1;
                     }
                     unset($metricErrors, $thresholdsErrors);
                 }
             }
             /* Validate advanced settings */
             if (!$dbRole->hasBehavior(ROLE_BEHAVIORS::VPC_ROUTER)) {
                 if (isset($role['settings'][Scalr_Role_Behavior::ROLE_BASE_API_PORT])) {
                     $this->checkInteger($role['farm_role_id'], Scalr_Role_Behavior::ROLE_BASE_API_PORT, $role['settings'][Scalr_Role_Behavior::ROLE_BASE_API_PORT], 'Scalarizr API port', 1, 65535);
                 }
                 if (isset($role['settings'][Scalr_Role_Behavior::ROLE_BASE_MESSAGING_PORT])) {
                     $this->checkInteger($role['farm_role_id'], Scalr_Role_Behavior::ROLE_BASE_MESSAGING_PORT, $role['settings'][Scalr_Role_Behavior::ROLE_BASE_MESSAGING_PORT], 'Scalarizr control port', 1, 65535);
                 }
                 if (isset($role['settings'][Entity\FarmRoleSetting::SYSTEM_REBOOT_TIMEOUT])) {
                     $this->checkInteger($role['farm_role_id'], Entity\FarmRoleSetting::SYSTEM_REBOOT_TIMEOUT, $role['settings'][Entity\FarmRoleSetting::SYSTEM_REBOOT_TIMEOUT], 'Reboot timeout', 1);
                 }
                 if (isset($role['settings'][Entity\FarmRoleSetting::SYSTEM_LAUNCH_TIMEOUT])) {
                     $this->checkInteger($role['farm_role_id'], Entity\FarmRoleSetting::SYSTEM_LAUNCH_TIMEOUT, $role['settings'][Entity\FarmRoleSetting::SYSTEM_LAUNCH_TIMEOUT], 'Launch timeout', 1);
                 }
             }
             /* Validate chef settings */
             if ($dbRole->hasBehavior(ROLE_BEHAVIORS::CHEF)) {
                 if ($role['settings'][Scalr_Role_Behavior_Chef::ROLE_CHEF_BOOTSTRAP] == 1) {
                     if (empty($role['settings'][Scalr_Role_Behavior_Chef::ROLE_CHEF_COOKBOOK_URL]) && empty($role['settings'][Scalr_Role_Behavior_Chef::ROLE_CHEF_SERVER_ID])) {
                         $this->setBuildError(Scalr_Role_Behavior_Chef::ROLE_CHEF_SERVER_ID, 'Chef Server or Chef Solo must be setup if using Chef to bootstrap Role', $role['farm_role_id']);
                     } elseif ($role['settings'][Scalr_Role_Behavior_Chef::ROLE_CHEF_COOKBOOK_URL_TYPE] == 'http' && !empty($role['settings'][Scalr_Role_Behavior_Chef::ROLE_CHEF_COOKBOOK_URL]) && Validator::validateUrl($role['settings'][Scalr_Role_Behavior_Chef::ROLE_CHEF_COOKBOOK_URL]) !== true) {
                         $this->setBuildError(Scalr_Role_Behavior_Chef::ROLE_CHEF_COOKBOOK_URL, 'Cookbook URL is invalid.', $role['farm_role_id']);
                     }
                 } elseif ($dbRole->getProperty(Scalr_Role_Behavior_Chef::ROLE_CHEF_BOOTSTRAP) == 1 && $dbRole->getProperty(Scalr_Role_Behavior_Chef::ROLE_CHEF_SERVER_ID)) {
                     if (strpos($role['farm_role_id'], "virtual_") !== false) {
                         $chefGovernance = $governance->getValue(Scalr_Governance::CATEGORY_GENERAL, Scalr_Governance::GENERAL_CHEF, 'servers');
                         if ($chefGovernance !== null && !isset($chefGovernance[$dbRole->getProperty(Scalr_Role_Behavior_Chef::ROLE_CHEF_SERVER_ID)])) {
                             $this->setBuildError(Scalr_Role_Behavior_Chef::ROLE_CHEF_SERVER_ID, 'Chef server is not allowed by Governance.', $role['farm_role_id']);
                         }
                     }
                     if (empty($dbRole->getProperty(Scalr_Role_Behavior_Chef::ROLE_CHEF_ENVIRONMENT)) && empty($role['settings'][Scalr_Role_Behavior_Chef::ROLE_CHEF_ENVIRONMENT])) {
                         $this->setBuildError(Scalr_Role_Behavior_Chef::ROLE_CHEF_ENVIRONMENT, 'Chef Environment is required', $role['farm_role_id']);
                     }
                 }
             }
             /** Validate platform specified settings **/
             switch ($role['platform']) {
                 case SERVER_PLATFORMS::EC2:
                     if (!empty($role['settings'][Entity\FarmRoleSetting::AWS_TAGS_LIST])) {
                         $reservedBaseCustomTags = ['scalr-meta', 'Name'];
                         $baseCustomTags = @explode("\n", $role['settings'][Entity\FarmRoleSetting::AWS_TAGS_LIST]);
                         foreach ((array) $baseCustomTags as $tag) {
                             $tag = trim($tag);
                             $tagChunks = explode("=", $tag);
                             if (in_array(trim($tagChunks[0]), $reservedBaseCustomTags)) {
                                 $this->setBuildError(Entity\FarmRoleSetting::AWS_TAGS_LIST, "Avoid using Scalr-reserved tag names.", $role['farm_role_id']);
                             }
                         }
                     }
                     if ($dbRole->hasBehavior(ROLE_BEHAVIORS::MYSQL)) {
                         if ($role['settings'][Entity\FarmRoleSetting::MYSQL_DATA_STORAGE_ENGINE] == MYSQL_STORAGE_ENGINE::EBS) {
                             if ($dbRole->generation != 2 && isset($role['settings'][Entity\FarmRoleSetting::AWS_AVAIL_ZONE])) {
                                 if ($role['settings'][Entity\FarmRoleSetting::AWS_AVAIL_ZONE] == "" || $role['settings'][Entity\FarmRoleSetting::AWS_AVAIL_ZONE] == "x-scalr-diff" || stristr($role['settings'][Entity\FarmRoleSetting::AWS_AVAIL_ZONE], 'x-scalr-custom')) {
                                     $this->setBuildError(Entity\FarmRoleSetting::AWS_AVAIL_ZONE, 'Requirement for EBS MySQL data storage is specific \'Placement\' parameter', $role['farm_role_id']);
                                 }
                             }
                         }
                     }
                     if ($dbRole->getDbMsrBehavior()) {
                         if ($role['settings'][Scalr_Db_Msr::DATA_STORAGE_ENGINE] == MYSQL_STORAGE_ENGINE::EPH) {
                             if (!$role['settings'][Scalr_Db_Msr::DATA_STORAGE_EPH_DISK] && !$role['settings'][Scalr_Db_Msr::DATA_STORAGE_EPH_DISKS]) {
                                 $this->setBuildError(Scalr_Db_Msr::DATA_STORAGE_EPH_DISK, 'Ephemeral disk settings is required', $role['farm_role_id']);
                             }
                         } elseif ($role['settings'][Scalr_Db_Msr::DATA_STORAGE_ENGINE] == MYSQL_STORAGE_ENGINE::EBS) {
                             if (array_key_exists(Scalr_Db_Msr::DATA_STORAGE_EBS_TYPE, $role["settings"])) {
                                 if ($role['settings'][Scalr_Db_Msr::DATA_STORAGE_EBS_TYPE] == CreateVolumeRequestData::VOLUME_TYPE_STANDARD) {
                                     $this->checkInteger($role['farm_role_id'], Scalr_Db_Msr::DATA_STORAGE_EBS_SIZE, $role['settings'][Scalr_Db_Msr::DATA_STORAGE_EBS_SIZE], 'Storage size', 1, 1024);
                                 } elseif (in_array($role['settings'][Scalr_Db_Msr::DATA_STORAGE_EBS_TYPE], [CreateVolumeRequestData::VOLUME_TYPE_GP2, CreateVolumeRequestData::VOLUME_TYPE_ST1, CreateVolumeRequestData::VOLUME_TYPE_SC1])) {
                                     $this->checkInteger($role['farm_role_id'], Scalr_Db_Msr::DATA_STORAGE_EBS_SIZE, $role['settings'][Scalr_Db_Msr::DATA_STORAGE_EBS_SIZE], 'Storage size', 1, 16384);
                                 } elseif ($role['settings'][Scalr_Db_Msr::DATA_STORAGE_EBS_TYPE] == CreateVolumeRequestData::VOLUME_TYPE_IO1) {
                                     $this->checkInteger($role['farm_role_id'], Scalr_Db_Msr::DATA_STORAGE_EBS_SIZE, $role['settings'][Scalr_Db_Msr::DATA_STORAGE_EBS_SIZE], 'Storage size', 4, 16384);
                                     $this->checkInteger($role['farm_role_id'], Scalr_Db_Msr::DATA_STORAGE_EBS_IOPS, $role['settings'][Scalr_Db_Msr::DATA_STORAGE_EBS_IOPS], 'IOPS', 100, 20000);
                                 }
                             }
                         }
                         if (array_key_exists(Scalr_Db_Msr::DATA_STORAGE_EBS_ENABLE_ROTATION, $role["settings"]) && $role['settings'][Scalr_Db_Msr::DATA_STORAGE_EBS_ENABLE_ROTATION] == 1) {
                             $this->checkInteger($role['farm_role_id'], Scalr_Db_Msr::DATA_STORAGE_EBS_ROTATE, $role['settings'][Scalr_Db_Msr::DATA_STORAGE_EBS_ROTATE], 'Snapshot rotation limit', 1);
                         }
                         if ($role['settings'][Scalr_Db_Msr::DATA_STORAGE_ENGINE] == MYSQL_STORAGE_ENGINE::LVM) {
                             if (!$role['settings'][Scalr_Role_DbMsrBehavior::ROLE_DATA_STORAGE_LVM_VOLUMES]) {
                                 $this->setBuildError(Scalr_Role_DbMsrBehavior::ROLE_DATA_STORAGE_LVM_VOLUMES, 'LVM storage settings is required', $role['farm_role_id']);
                             }
                         }
                     }
                     if ($role['settings'][Entity\FarmRoleSetting::AWS_AVAIL_ZONE] == 'x-scalr-custom=') {
                         $this->setBuildError(Entity\FarmRoleSetting::AWS_AVAIL_ZONE, 'Availability zone should be selected', $role['farm_role_id']);
                     }
                     if (!empty($farmSettings['vpc_id'])) {
                         $sgs = @json_decode($role['settings'][Entity\FarmRoleSetting::AWS_SECURITY_GROUPS_LIST]);
                         if (!$governance->getValue(SERVER_PLATFORMS::EC2, Scalr_Governance::AWS_SECURITY_GROUPS) && !$dbRole->hasBehavior(ROLE_BEHAVIORS::VPC_ROUTER) && empty($sgs) && empty($role['settings'][Entity\FarmRoleSetting::AWS_SG_LIST])) {
                             $this->setBuildError(Entity\FarmRoleSetting::AWS_SECURITY_GROUPS_LIST, 'Security group(s) should be selected', $role['farm_role_id']);
                         }
                         $subnets = @json_decode($role['settings'][Entity\FarmRoleSetting::AWS_VPC_SUBNET_ID]);
                         if (empty($subnets)) {
                             $this->setBuildError(Entity\FarmRoleSetting::AWS_VPC_SUBNET_ID, 'VPC Subnet(s) should be selected', $role['farm_role_id']);
                         }
                         if (\Scalr::config('scalr.instances_connection_policy') != 'local' && empty($role['settings'][Scalr_Role_Behavior_Router::ROLE_VPC_SCALR_ROUTER_ID])) {
                             try {
                                 if (!empty($subnets[0])) {
                                     $platform = PlatformFactory::NewPlatform(SERVER_PLATFORMS::EC2);
                                     $info = $platform->listSubnets($this->getEnvironment(), $role['cloud_location'], $farmSettings['vpc_id'], true, $subnets[0]);
                                     if (!empty($info["type"]) && $info['type'] == 'private') {
                                         $vpcRouterRequired = $role['farm_role_id'];
                                     }
                                 }
                             } catch (Exception $e) {
                             }
                         }
                     }
                     break;
                 case SERVER_PLATFORMS::GCE:
                     if ($dbRole->getDbMsrBehavior()) {
                         if ($role['settings'][Scalr_Db_Msr::DATA_STORAGE_ENGINE] == MYSQL_STORAGE_ENGINE::GCE_PERSISTENT) {
                             $this->checkInteger($role['farm_role_id'], Scalr_Db_Msr::DATA_STORAGE_GCED_SIZE, $role['settings'][Scalr_Db_Msr::DATA_STORAGE_GCED_SIZE], 'Storage size', 1);
                         }
                     }
                     if (!empty($role['settings'][Entity\FarmRoleSetting::GCE_INSTANCE_PERMISSIONS])) {
                         $instancePermissions = json_decode($role['settings'][Entity\FarmRoleSetting::GCE_INSTANCE_PERMISSIONS]);
                         if (!is_array($instancePermissions)) {
                             throw new BadRequestException('GCE service account permissions configuration is malformed');
                         }
                         foreach ($instancePermissions as $value) {
                             if (Validator::validateUrl($value) !== true) {
                                 $this->setBuildError(Entity\FarmRoleSetting::GCE_INSTANCE_PERMISSIONS, 'Invalid GCE service account permission(s)', $role['farm_role_id']);
                                 break;
                             }
                         }
                         unset($instancePermissions);
                     }
                     break;
             }
             if ($dbRole->getDbMsrBehavior()) {
                 if (array_key_exists(Scalr_Db_Msr::DATA_BUNDLE_ENABLED, $role["settings"]) && $role['settings'][Scalr_Db_Msr::DATA_BUNDLE_ENABLED] == 1) {
                     $this->checkInteger($role['farm_role_id'], Scalr_Db_Msr::DATA_BUNDLE_EVERY, $role['settings'][Scalr_Db_Msr::DATA_BUNDLE_EVERY], 'Bundle period', 1);
                     $this->checkString($role['farm_role_id'], Scalr_Db_Msr::DATA_BUNDLE_TIMEFRAME_START_HH, $role['settings'][Scalr_Db_Msr::DATA_BUNDLE_TIMEFRAME_START_HH], 'Preferred bundle window start HH is invalid', '/^([0-1][0-9])|(2[0-4])$/');
                     $this->checkString($role['farm_role_id'], Scalr_Db_Msr::DATA_BUNDLE_TIMEFRAME_START_MM, $role['settings'][Scalr_Db_Msr::DATA_BUNDLE_TIMEFRAME_START_MM], 'Preferred bundle window start MM is invalid', '/^[0-5][0-9]$/');
                     $this->checkString($role['farm_role_id'], Scalr_Db_Msr::DATA_BUNDLE_TIMEFRAME_END_HH, $role['settings'][Scalr_Db_Msr::DATA_BUNDLE_TIMEFRAME_END_HH], 'Preferred bundle window end HH is invalid', '/^([0-1][0-9])|(2[0-4])$/');
                     $this->checkString($role['farm_role_id'], Scalr_Db_Msr::DATA_BUNDLE_TIMEFRAME_END_MM, $role['settings'][Scalr_Db_Msr::DATA_BUNDLE_TIMEFRAME_END_MM], 'Preferred bundle window end MM is invalid', '/^[0-5][0-9]$/');
                 }
                 if (array_key_exists(Scalr_Db_Msr::DATA_BACKUP_ENABLED, $role["settings"]) && $role['settings'][Scalr_Db_Msr::DATA_BACKUP_ENABLED] == 1) {
                     $this->checkInteger($role['farm_role_id'], Scalr_Db_Msr::DATA_BACKUP_EVERY, $role['settings'][Scalr_Db_Msr::DATA_BACKUP_EVERY], 'Backup period', 1);
                     $this->checkString($role['farm_role_id'], Scalr_Db_Msr::DATA_BACKUP_TIMEFRAME_START_HH, $role['settings'][Scalr_Db_Msr::DATA_BACKUP_TIMEFRAME_START_HH], 'Preferred backup window start HH is invalid', '/^([0-1][0-9])|(2[0-4])$/');
                     $this->checkString($role['farm_role_id'], Scalr_Db_Msr::DATA_BACKUP_TIMEFRAME_START_MM, $role['settings'][Scalr_Db_Msr::DATA_BACKUP_TIMEFRAME_START_MM], 'Preferred backup window start MM is invalid', '/^[0-5][0-9]$/');
                     $this->checkString($role['farm_role_id'], Scalr_Db_Msr::DATA_BACKUP_TIMEFRAME_END_HH, $role['settings'][Scalr_Db_Msr::DATA_BACKUP_TIMEFRAME_END_HH], 'Preferred backup window end HH is invalid', '/^([0-1][0-9])|(2[0-4])$/');
                     $this->checkString($role['farm_role_id'], Scalr_Db_Msr::DATA_BACKUP_TIMEFRAME_END_MM, $role['settings'][Scalr_Db_Msr::DATA_BACKUP_TIMEFRAME_END_MM], 'Preferred backup window end MM is invalid', '/^[0-5][0-9]$/');
                 }
             }
             if (!empty($role['settings'][Scalr_Role_Behavior::ROLE_BASE_CUSTOM_TAGS]) && PlatformFactory::isOpenstack($role['platform'])) {
                 $reservedBaseCustomTags = ['scalr-meta', 'farmid', 'role', 'httpproto', 'region', 'hash', 'realrolename', 'szr_key', 'serverid', 'p2p_producer_endpoint', 'queryenv_url', 'behaviors', 'farm_roleid', 'roleid', 'env_id', 'platform', 'server_index', 'cloud_server_id', 'cloud_location_zone', 'owner_email'];
                 $baseCustomTags = @explode("\n", $role['settings'][Scalr_Role_Behavior::ROLE_BASE_CUSTOM_TAGS]);
                 foreach ((array) $baseCustomTags as $tag) {
                     $tag = trim($tag);
                     $tagChunks = explode("=", $tag);
                     if (in_array(trim($tagChunks[0]), $reservedBaseCustomTags)) {
                         $this->setBuildError(Scalr_Role_Behavior::ROLE_BASE_CUSTOM_TAGS, "Avoid using Scalr-reserved metadata names.", $role['farm_role_id']);
                     }
                 }
             }
             if (!empty($role['settings'][Scalr_Role_Behavior::ROLE_BASE_HOSTNAME_FORMAT])) {
                 if (!preg_match('/^[\\w\\{\\}\\.-]+$/', $role['settings'][Scalr_Role_Behavior::ROLE_BASE_HOSTNAME_FORMAT])) {
                     $this->setBuildError(Scalr_Role_Behavior::ROLE_BASE_HOSTNAME_FORMAT, "server hostname format for role'{$dbRole->name}' should contain only [a-z0-9-] chars. First char should not be hypen.", $role['farm_role_id']);
                 }
             }
             if (!empty($role['settings'][Entity\FarmRoleSetting::DNS_CREATE_RECORDS])) {
                 if ($role['settings'][Entity\FarmRoleSetting::DNS_EXT_RECORD_ALIAS]) {
                     if (!preg_match('/^[\\w\\{\\}\\.-]+$/', $role['settings'][Entity\FarmRoleSetting::DNS_EXT_RECORD_ALIAS])) {
                         $this->setBuildError(Entity\FarmRoleSetting::DNS_EXT_RECORD_ALIAS, "ext- record alias for role '{$dbRole->name}' should contain only [A-Za-z0-9-] chars. First and last char should not be hypen.", $role['farm_role_id']);
                     }
                 }
                 if ($role['settings'][Entity\FarmRoleSetting::DNS_INT_RECORD_ALIAS]) {
                     if (!preg_match('/^[\\w\\{\\}\\.-]+$/', $role['settings'][Entity\FarmRoleSetting::DNS_INT_RECORD_ALIAS])) {
                         $this->setBuildError(Entity\FarmRoleSetting::DNS_INT_RECORD_ALIAS, "int- record alias for role '{$dbRole->name}' should contain only [A-Za-z0-9-] chars. First and last char should not by hypen.", $role['farm_role_id']);
                     }
                 }
             }
             // Validate Global variables
             if (!strstr($role['farm_role_id'], 'virtual_')) {
                 $farmRole = DBFarmRole::LoadByID($role['farm_role_id']);
             } else {
                 $farmRole = null;
                 if ($dbRole->isDeprecated == 1) {
                     $this->setBuildError('roleId', 'This role has been deprecated and cannot be added', $role['farm_role_id']);
                 }
                 if (!empty($envs = $dbRole->__getNewRoleObject()->getAllowedEnvironments())) {
                     if (!in_array($this->getEnvironmentId(), $envs)) {
                         $this->setBuildError('roleId', "You don't have access to this role", $role['farm_role_id']);
                     }
                 }
             }
             if (isset($role['storages']['configs'])) {
                 // TODO: refactor, get rid of using DBFarmRole in constructor
                 $fr = $farmRole ? $farmRole : new DBFarmRole(0);
                 foreach ($fr->getStorage()->validateConfigs($role['storages']['configs']) as $index => $message) {
                     $this->setBuildError('storages', ['message' => $message, 'invalidIndex' => $index], $role['farm_role_id']);
                     break;
                 }
             }
             $result = $farmRoleVariables->validateValues(is_array($role['variables']) ? $role['variables'] : [], $dbRole->id, $farmId, $farmRole ? $farmRole->ID : 0);
             if ($result !== TRUE) {
                 $this->setBuildError('variables', $result, $role['farm_role_id']);
             }
         }
         unset($invertedMetrics);
     }
     if ($farmSettings['vpc_id']) {
         if (!$hasVpcRouter && $vpcRouterRequired) {
             $this->setBuildError(Entity\FarmRoleSetting::AWS_VPC_SUBNET_ID, 'You must select a VPC Router for Farm Roles launched in a Private VPC Subnet', $vpcRouterRequired);
         }
     }
     if ($this->getContainer()->analytics->enabled) {
         if ($farmSettings['projectId']) {
             $project = $this->getContainer()->analytics->projects->get($farmSettings['projectId']);
             if (!$project) {
                 $this->setBuildError('projectId', 'Project not found', null);
             } else {
                 if ($project->ccId != $this->getEnvironment()->getPlatformConfigValue(Scalr_Environment::SETTING_CC_ID)) {
                     $this->setBuildError('projectId', 'Invalid project identifier. Project should correspond to the Environment\'s cost center.', null);
                 }
             }
         } else {
             $this->setBuildError('projectId', 'Project field is required', null);
         }
     }
     return $this->errors['error_count'] == 0 ? true : false;
 }