Beispiel #1
0
 /**
  * Save metric.
  *
  * @param  string $name
  * @param  string $retrieveMethod
  * @param  string $calcFunction
  * @param  int    $metricId       optional
  * @param  string $filePath       optional
  * @param  bool   $isInvert       optional
  * @throws Exception
  * @throws Scalr_Exception_Core
  * @throws Scalr_Exception_InsufficientPermissions
  * @throws \Scalr\Exception\ModelException
  */
 public function xSaveAction($name, $retrieveMethod, $calcFunction = null, $metricId = null, $filePath = null, $isInvert = false)
 {
     $this->request->restrictAccess(Acl::RESOURCE_GENERAL_CUSTOM_SCALING_METRICS, Acl::PERM_GENERAL_CUSTOM_SCALING_METRICS_MANAGE);
     $validator = new Validator();
     if ($metricId) {
         /* @var $metric Entity\ScalingMetric */
         $metric = Entity\ScalingMetric::findPk($metricId);
         if (!$metric) {
             throw new Scalr_UI_Exception_NotFound();
         }
         $this->user->getPermissions()->validate($metric);
     } else {
         $metric = new Entity\ScalingMetric();
         $metric->accountId = $this->user->getAccountId();
         $metric->envId = $this->getEnvironmentId();
         $metric->alias = 'custom';
         $metric->algorithm = Entity\ScalingMetric::ALGORITHM_SENSOR;
     }
     if (!preg_match('/^' . Entity\ScalingMetric::NAME_REGEXP . '$/', $name)) {
         $validator->addError('name', 'Metric name should be both alphanumeric and greater than 5 chars');
     }
     if ($retrieveMethod == Entity\ScalingMetric::RETRIEVE_METHOD_URL_REQUEST) {
         $validator->addErrorIf($validator->validateUrl($filePath) !== true, 'filePath', 'Invalid URL');
     } else {
         $validator->addErrorIf($validator->validateNotEmpty($calcFunction) !== true, 'calcFunction', 'Calculation function is required');
     }
     $criteria = [];
     $criteria[] = ['name' => $name];
     if ($metricId) {
         $criteria[] = ['id' => ['$ne' => $metricId]];
     }
     if (Entity\ScalingMetric::findOne($criteria)) {
         $validator->addError('name', 'Metric with the same name already exists');
     }
     if ($validator->isValid($this->response)) {
         $metric->name = $name;
         $metric->filePath = $filePath;
         $metric->retrieveMethod = $retrieveMethod;
         $metric->calcFunction = $calcFunction;
         $metric->isInvert = $isInvert;
         $metric->save();
         $this->response->success('Scaling metric has been successfully saved.');
         $this->response->data(['metric' => get_object_vars($metric)]);
     }
 }
Beispiel #2
0
 /**
  *
  * @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');
     }
     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_numeric($farmSettings['teamOwner']) && $farmSettings['teamOwner'] > 0) {
         if ($this->user->canManageAcl()) {
             $teams = $this->db->getAll('SELECT id, name FROM account_teams WHERE account_id = ?', array($this->user->getAccountId()));
         } else {
             $teams = $this->user->getTeams();
         }
         if (!in_array($farmSettings['teamOwner'], array_map(function ($t) {
             return $t['id'];
         }, $teams))) {
             if ($this->db->GetOne('SELECT team_id FROM farms WHERE id = ?', [$farmId]) != $farmSettings['teamOwner']) {
                 $this->setBuildError('teamOwner', 'Team not found');
             }
         }
     }
     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->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 ($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']);
                 }
             }
             // Validate deployments
             if (isset($role[Scalr_Role_Behavior::ROLE_DM_APPLICATION_ID])) {
                 $application = Scalr_Dm_Application::init()->loadById($role[Scalr_Role_Behavior::ROLE_DM_APPLICATION_ID]);
                 $this->user->getPermissions()->validate($application);
                 if (!$role[Scalr_Role_Behavior::ROLE_DM_REMOTE_PATH]) {
                     $this->setBuildError(Scalr_Role_Behavior::ROLE_DM_REMOTE_PATH, 'Remote path is required for deployment', $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 (!$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', 1, 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'])) {
                     foreach ($role['scaling'] as $metricId => $metricSettings) {
                         $hasError = false;
                         if ($metricId == Entity\ScalingMetric::METRIC_URL_RESPONSE_TIME_ID) {
                             $hasError = Validator::validateUrl($metricSettings['url']) !== true || Validator::validateInteger($metricSettings['min']) !== true || Validator::validateInteger($metricSettings['max']) !== true;
                         } elseif ($metricId == Entity\ScalingMetric::METRIC_SQS_QUEUE_SIZE_ID) {
                             $hasError = Validator::validateNotEmpty($metricSettings['queue_name']) !== true || Validator::validateInteger($metricSettings['min']) !== true || Validator::validateInteger($metricSettings['max']) !== true;
                         } elseif (in_array($metricId, [Entity\ScalingMetric::METRIC_LOAD_AVERAGES_ID, Entity\ScalingMetric::METRIC_FREE_RAM_ID, Entity\ScalingMetric::METRIC_BANDWIDTH_ID])) {
                             $hasError = Validator::validateFloat($metricSettings['min']) !== true || Validator::validateFloat($metricSettings['max']) !== true;
                         }
                         if ($hasError) {
                             $this->setBuildError('scaling', ['message' => 'Scaling metric settings are invalid', 'invalidIndex' => $metricId], $role['farm_role_id']);
                             break;
                         }
                     }
                 }
             }
             /* 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 ($role['settings'][Scalr_Db_Msr::DATA_STORAGE_EBS_TYPE] == CreateVolumeRequestData::VOLUME_TYPE_GP2) {
                                     $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::CLOUDSTACK:
                     if (!$role['settings'][Entity\FarmRoleSetting::CLOUDSTACK_SERVICE_OFFERING_ID]) {
                         $this->setBuildError(Entity\FarmRoleSetting::CLOUDSTACK_SERVICE_OFFERING_ID, 'Service offering should be selected', $role['farm_role_id']);
                     }
                     break;
                 case SERVER_PLATFORMS::RACKSPACE:
                     if (!$role['settings'][Entity\FarmRoleSetting::RS_FLAVOR_ID]) {
                         $this->setBuildError(Entity\FarmRoleSetting::CLOUDSTACK_SERVICE_OFFERING_ID, 'Flavor should be selected', $role['farm_role_id']);
                     }
                     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);
                         }
                     }
                     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']);
             }
         }
     }
     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;
 }