public function LaunchServer(DBServer $DBServer, \Scalr_Server_LaunchOptions $launchOptions = null)
{
$environment = $DBServer->GetEnvironmentObject();
$ccProps = $environment->keychain(SERVER_PLATFORMS::GCE)->properties;
$governance = new \Scalr_Governance($environment->id);
$rootDeviceSettings = null;
$ssdDisks = array();
$scopes = ["https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/compute", "https://www.googleapis.com/auth/devstorage.full_control"];
if (!$launchOptions) {
$launchOptions = new \Scalr_Server_LaunchOptions();
$DBRole = $DBServer->GetFarmRoleObject()->GetRoleObject();
$launchOptions->imageId = $DBRole->__getNewRoleObject()->getImage(\SERVER_PLATFORMS::GCE, $DBServer->GetProperty(\GCE_SERVER_PROPERTIES::CLOUD_LOCATION))->imageId;
$launchOptions->serverType = $DBServer->GetFarmRoleObject()->GetSetting(Entity\FarmRoleSetting::INSTANCE_TYPE);
$launchOptions->cloudLocation = $DBServer->GetFarmRoleObject()->CloudLocation;
$userData = $DBServer->GetCloudUserData();
$launchOptions->architecture = 'x86_64';
$networkName = $DBServer->GetFarmRoleObject()->GetSetting(Entity\FarmRoleSetting::GCE_NETWORK);
$subnet = $DBServer->GetFarmRoleObject()->GetSetting(Entity\FarmRoleSetting::GCE_SUBNET);
$onHostMaintenance = $DBServer->GetFarmRoleObject()->GetSetting(Entity\FarmRoleSetting::GCE_ON_HOST_MAINTENANCE);
$osType = $DBRole->getOs()->family == 'windows' ? 'windows' : 'linux';
$rootDevice = json_decode($DBServer->GetFarmRoleObject()->GetSetting(\Scalr_Role_Behavior::ROLE_BASE_ROOT_DEVICE_CONFIG), true);
if ($rootDevice && $rootDevice['settings']) {
$rootDeviceSettings = $rootDevice['settings'];
}
$storage = new FarmRoleStorage($DBServer->GetFarmRoleObject());
$volumes = $storage->getVolumesConfigs($DBServer);
if (!empty($volumes)) {
foreach ($volumes as $volume) {
if ($volume->type == FarmRoleStorageConfig::TYPE_GCE_EPHEMERAL) {
array_push($ssdDisks, $volume);
}
}
}
if ($governance->isEnabled(\Scalr_Governance::CATEGORY_GENERAL, \Scalr_Governance::GENERAL_HOSTNAME_FORMAT)) {
$hostNameFormat = $governance->getValue(\Scalr_Governance::CATEGORY_GENERAL, \Scalr_Governance::GENERAL_HOSTNAME_FORMAT);
} else {
$hostNameFormat = $DBServer->GetFarmRoleObject()->GetSetting(\Scalr_Role_Behavior::ROLE_BASE_HOSTNAME_FORMAT);
}
$hostname = !empty($hostNameFormat) ? $DBServer->applyGlobalVarsToValue($hostNameFormat) : '';
if ($hostname != '') {
$DBServer->SetProperty(\Scalr_Role_Behavior::SERVER_BASE_HOSTNAME, $hostname);
}
$userScopes = json_decode($DBServer->GetFarmRoleObject()->GetSetting(Entity\FarmRoleSetting::GCE_INSTANCE_PERMISSIONS));
if (!empty($userScopes) && is_array($userScopes)) {
$scopes = array_merge($scopes, $userScopes);
}
} else {
$userData = array();
$networkName = 'default';
$osType = 'linux';
$hostname = '';
}
if (!$onHostMaintenance) {
$onHostMaintenance = 'MIGRATE';
}
if ($DBServer->status == \SERVER_STATUS::TEMPORARY) {
$keyName = "SCALR-ROLESBUILDER-" . SCALR_ID;
} else {
$keyName = "FARM-{$DBServer->farmId}-" . SCALR_ID;
}
$sshKey = (new SshKey())->loadGlobalByName($DBServer->envId, \SERVER_PLATFORMS::GCE, "", $keyName);
if (!$sshKey) {
$sshKey = new SshKey();
$keys = $sshKey->generateKeypair();
if ($keys['public']) {
$sshKey->farmId = $DBServer->farmId;
$sshKey->envId = $DBServer->envId;
$sshKey->type = SshKey::TYPE_GLOBAL;
$sshKey->platform = \SERVER_PLATFORMS::GCE;
$sshKey->cloudLocation = "";
$sshKey->cloudKeyName = $keyName;
$sshKey->save();
$publicKey = $keys['public'];
} else {
throw new Exception("Scalr unable to generate ssh keypair");
}
} else {
$publicKey = $sshKey->publicKey;
}
$gce = $this->getClient($environment);
$projectId = $ccProps[Entity\CloudCredentialsProperty::GCE_PROJECT_ID];
// Check firewall
$firewalls = $gce->firewalls->listFirewalls($projectId);
$firewallFound = false;
foreach ($firewalls->getItems() as $f) {
if ($f->getName() == 'scalr-system') {
$firewallFound = true;
break;
}
}
// Create scalr firewall
if (!$firewallFound) {
$firewall = new \Google_Service_Compute_Firewall();
$firewall->setName('scalr-system');
$firewall->setNetwork($this->getObjectUrl($networkName, 'networks', $projectId));
//Get scalr IP-pool IP list and set source ranges
$firewall->setSourceRanges(\Scalr::config('scalr.aws.ip_pool'));
// Set ports
$tcp = new \Google_Service_Compute_FirewallAllowed();
$tcp->setIPProtocol('tcp');
$tcp->setPorts(array('1-65535'));
$udp = new \Google_Service_Compute_FirewallAllowed();
$udp->setIPProtocol('udp');
$udp->setPorts(array('1-65535'));
$firewall->setAllowed(array($tcp, $udp));
// Set target tags
$firewall->setTargetTags(array('scalr'));
$gce->firewalls->insert($projectId, $firewall);
}
$instance = new \Google_Service_Compute_Instance();
$instance->setKind("compute#instance");
// Set scheduling
$scheduling = new \Google_Service_Compute_Scheduling();
$scheduling->setAutomaticRestart(true);
$scheduling->setOnHostMaintenance($onHostMaintenance);
$instance->setScheduling($scheduling);
$accessConfig = new \Google_Service_Compute_AccessConfig();
$accessConfig->setName("External NAT");
$accessConfig->setType("ONE_TO_ONE_NAT");
$network = new \Google_Service_Compute_NetworkInterface();
$network->setNetwork($this->getObjectUrl($networkName, 'networks', $projectId));
if (!empty($subnet)) {
$network->setSubnetwork($this->getObjectUrl($subnet, 'subnetworks', $projectId, $DBServer->GetFarmRoleObject()->GetSetting(Entity\FarmRoleSetting::GCE_REGION)));
}
$network->setAccessConfigs(array($accessConfig));
$instance->setNetworkInterfaces(array($network));
$serviceAccount = new \Google_Service_Compute_ServiceAccount();
$serviceAccount->setEmail("default");
$serviceAccount->setScopes($scopes);
$instance->setServiceAccounts(array($serviceAccount));
if ($launchOptions->cloudLocation != 'x-scalr-custom') {
$availZone = $launchOptions->cloudLocation;
} else {
$location = $DBServer->GetFarmRoleObject()->GetSetting(Entity\FarmRoleSetting::GCE_CLOUD_LOCATION);
$availZones = array();
if (stristr($location, "x-scalr-custom")) {
$zones = explode("=", $location);
foreach (explode(":", $zones[1]) as $zone) {
if ($zone != "") {
array_push($availZones, $zone);
}
}
}
sort($availZones);
$availZones = array_reverse($availZones);
$servers = $DBServer->GetFarmRoleObject()->GetServersByFilter(array("status" => array(\SERVER_STATUS::RUNNING, \SERVER_STATUS::INIT, \SERVER_STATUS::PENDING)));
$availZoneDistribution = array();
foreach ($servers as $cDbServer) {
if ($cDbServer->serverId != $DBServer->serverId) {
$availZoneDistribution[$cDbServer->GetProperty(\GCE_SERVER_PROPERTIES::CLOUD_LOCATION)]++;
}
}
$sCount = 1000000;
foreach ($availZones as $zone) {
if ((int) $availZoneDistribution[$zone] <= $sCount) {
$sCount = (int) $availZoneDistribution[$zone];
$availZone = $zone;
}
}
$aZones = implode(",", $availZones);
// Available zones
$dZones = "";
// Zones distribution
foreach ($availZoneDistribution as $zone => $num) {
$dZones .= "({$zone}:{$num})";
}
}
$instance->setZone($this->getObjectUrl($availZone, 'zones', $projectId));
$instance->setMachineType($this->getObjectUrl($launchOptions->serverType, 'machineTypes', $projectId, $availZone));
//Create root disk
$image = $this->getObjectUrl($launchOptions->imageId, 'images', $projectId);
$disks = array();
$diskName = "root-{$DBServer->serverId}";
$initializeParams = new \Google_Service_Compute_AttachedDiskInitializeParams();
$initializeParams->sourceImage = $image;
$initializeParams->diskName = $diskName;
if ($rootDeviceSettings) {
$initializeParams->diskType = $this->getObjectUrl($rootDeviceSettings[FarmRoleStorageConfig::SETTING_GCE_PD_TYPE] ? $rootDeviceSettings[FarmRoleStorageConfig::SETTING_GCE_PD_TYPE] : 'pd-standard', 'diskTypes', $projectId, $availZone);
$initializeParams->diskSizeGb = $rootDeviceSettings[FarmRoleStorageConfig::SETTING_GCE_PD_SIZE];
}
$attachedDisk = new \Google_Service_Compute_AttachedDisk();
$attachedDisk->setKind("compute#attachedDisk");
$attachedDisk->setBoot(true);
$attachedDisk->setMode("READ_WRITE");
$attachedDisk->setType("PERSISTENT");
$attachedDisk->setDeviceName("root");
$attachedDisk->setAutoDelete(true);
$attachedDisk->setInitializeParams($initializeParams);
array_push($disks, $attachedDisk);
if (count($ssdDisks) > 0) {
foreach ($ssdDisks as $disk) {
$attachedDisk = new \Google_Service_Compute_AttachedDisk();
$attachedDisk->setKind("compute#attachedDisk");
$attachedDisk->setBoot(false);
$attachedDisk->setMode("READ_WRITE");
$attachedDisk->setType("SCRATCH");
$attachedDisk->setDeviceName(str_replace("google-", "", $disk->name));
$attachedDisk->setInterface('SCSI');
$attachedDisk->setAutoDelete(true);
$initializeParams = new \Google_Service_Compute_AttachedDiskInitializeParams();
$initializeParams->diskType = $this->getObjectUrl('local-ssd', 'diskTypes', $projectId, $availZone);
$attachedDisk->setInitializeParams($initializeParams);
array_push($disks, $attachedDisk);
}
}
$instance->setDisks($disks);
$instance->setName($DBServer->serverId);
$tags = array('scalr', "env-{$DBServer->envId}");
if ($DBServer->farmId) {
$tags[] = "farm-{$DBServer->farmId}";
}
if ($DBServer->farmRoleId) {
$tags[] = "farmrole-{$DBServer->farmRoleId}";
}
$gTags = new \Google_Service_Compute_Tags();
$gTags->setItems($tags);
$instance->setTags($gTags);
$metadata = new \Google_Service_Compute_Metadata();
$items = array();
// Set user data
$uData = '';
foreach ($userData as $k => $v) {
$uData .= "{$k}={$v};";
}
$uData = trim($uData, ";");
if ($uData) {
$item = new \Google_Service_Compute_MetadataItems();
$item->setKey('scalr');
$item->setValue($uData);
$items[] = $item;
}
if ($osType == 'windows') {
// Add Windows credentials
$item = new \Google_Service_Compute_MetadataItems();
$item->setKey("gce-initial-windows-user");
$item->setValue("scalr");
$items[] = $item;
$item = new \Google_Service_Compute_MetadataItems();
$item->setKey("gce-initial-windows-password");
$item->setValue(\Scalr::GenerateRandomKey(16) . rand(0, 9));
$items[] = $item;
} else {
// Add SSH Key
$item = new \Google_Service_Compute_MetadataItems();
$item->setKey("sshKeys");
$item->setValue("scalr:{$publicKey}");
$items[] = $item;
}
//Set hostname
if ($hostname != '') {
$item = new \Google_Service_Compute_MetadataItems();
$item->setKey("hostname");
$item->setValue($hostname);
$items[] = $item;
}
$metadata->setItems($items);
$instance->setMetadata($metadata);
try {
$result = $gce->instances->insert($projectId, $availZone, $instance);
} catch (Exception $e) {
$json = json_decode($e->getMessage());
if (!empty($json->error->message)) {
$message = $json->error->message;
} else {
$message = $e->getMessage();
}
throw new Exception(sprintf(_("Cannot launch new instance. %s (%s, %s)"), $message, $image, $launchOptions->serverType));
}
if ($result->id) {
$instanceTypeInfo = $this->getInstanceType($launchOptions->serverType, $environment, $availZone);
/* @var $instanceTypeInfo CloudInstanceType */
$DBServer->SetProperties([\GCE_SERVER_PROPERTIES::PROVISIONING_OP_ID => $result->name, \GCE_SERVER_PROPERTIES::SERVER_NAME => $DBServer->serverId, \GCE_SERVER_PROPERTIES::CLOUD_LOCATION => $availZone, \GCE_SERVER_PROPERTIES::CLOUD_LOCATION_ZONE => $availZone, \SERVER_PROPERTIES::ARCHITECTURE => $launchOptions->architecture, 'debug.region' => $result->region, 'debug.zone' => $result->zone, \SERVER_PROPERTIES::INFO_INSTANCE_VCPUS => $instanceTypeInfo ? $instanceTypeInfo->vcpus : null]);
$DBServer->setOsType($osType);
$DBServer->cloudLocation = $availZone;
$DBServer->cloudLocationZone = $availZone;
$DBServer->imageId = $launchOptions->imageId;
$DBServer->update(['type' => $launchOptions->serverType, 'instanceTypeName' => $launchOptions->serverType]);
// we set server history here
$DBServer->getServerHistory()->update(['cloudServerId' => $DBServer->serverId]);
return $DBServer;
} else {
throw new Exception(sprintf(_("Cannot launch new instance. %s (%s, %s)"), serialize($result), $launchOptions->imageId, $launchOptions->serverType));
}
}