protected function run1($stage) { $this->console->out("Initializing instance_type_name field in servers_history table"); $result = $this->db->Execute("\n SELECT sh.* FROM servers_history sh\n JOIN servers s USING(server_id)\n WHERE sh.instance_type_name IS NULL\n AND sh.type IS NOT NULL\n AND sh.cloud_location IS NOT NULL\n ORDER BY sh.env_id, sh.platform DESC\n "); $env = null; $platform = null; $this->db->BeginTrans(); try { $sql = "UPDATE servers_history sh SET sh.instance_type_name = ? WHERE sh.server_id = ?"; while ($record = $result->FetchRow()) { if (!isset($env) || $env->id != $record['env_id']) { $env = Scalr_Environment::init()->loadById($record['env_id']); $platform = null; } if (in_array($record['platform'], [\SERVER_PLATFORMS::EC2, \SERVER_PLATFORMS::GCE])) { $this->db->Execute($sql, [$record['type'], $record['server_id']]); continue; } if (!isset($platform) || $platform != $record['platform']) { $platform = $record['platform']; $platformModule = PlatformFactory::NewPlatform($record['platform']); $url = $platformModule->getEndpointUrl($env); } $cloudLocationId = CloudLocation::calculateCloudLocationId($record['platform'], $record['cloud_location'], $url); $instanceTypeEntity = CloudInstanceType::findPk($cloudLocationId, $record['type']); /* @var $instanceTypeEntity CloudInstanceType */ if ($instanceTypeEntity) { $this->db->Execute($sql, [$instanceTypeEntity->name, $record['server_id']]); } } $this->db->CommitTrans(); } catch (Exception $e) { $this->db->RollbackTrans(); } }
/** * Gets a normalized url for an each platform * * @param string $platform Cloud platform * @return string Returns url */ public function getUrl($platform) { if (!isset($this->aUrl[$platform])) { if ($platform == \SERVER_PLATFORMS::EC2 || $platform == \SERVER_PLATFORMS::GCE || $platform == \SERVER_PLATFORMS::AZURE) { $value = ''; } else { if (PlatformFactory::isOpenstack($platform)) { $value = CloudLocation::normalizeUrl($this->env->keychain($platform)->properties[CloudCredentialsProperty::OPENSTACK_KEYSTONE_URL]); } else { if (PlatformFactory::isCloudstack($platform)) { $value = CloudLocation::normalizeUrl($this->env->keychain($platform)->properties[CloudCredentialsProperty::CLOUDSTACK_API_URL]); } } } $this->aUrl[$platform] = $value; } return $this->aUrl[$platform]; }
/** * SCALRCORE-951 we should avoid ON DUPLICATE KEY UPDATE clause on tables with multiple unique indexes * @test * @functional */ public function testFunctionalSeveralUniqueKeys() { $db = \Scalr::getDb(); //Removes previously created records if they exist $this->cleanupCloudLocations(); $cl2Identifier = CloudLocation::calculateCloudLocationId(self::CL_PLATFORM, self::CL_NAME, self::CL_URL); $cl3Identifier = CloudLocation::calculateCloudLocationId(self::CL_PLATFORM, self::CL_NAME . '3', self::CL_URL); //Creates the first record $cl = $this->getCloudLocationEntity(); $cl->save(); unset($cl); //Checks if it has been saved properly $cl = CloudLocation::findPk(self::CL_LOC_ID); $this->assertInstanceOf('Scalr\\Model\\Entity\\CloudLocation', $cl); $this->assertEquals(self::CL_LOC_ID, $cl->cloudLocationId); $this->assertEquals(self::CL_URL, $cl->url); $this->assertEquals(self::CL_NAME, $cl->cloudLocation); $this->assertEquals(self::CL_PLATFORM, $cl->platform); $cl3 = $this->getCloudLocationEntity($cl3Identifier); $cl3->cloudLocation = self::CL_NAME . '3'; $cl3->save(); //Record with this unique key already exists $cl3->cloudLocation = self::CL_NAME; //Saving record with existing unique key $ex = false; try { $cl3->save(); $this->cleanupCloudLocations(); } catch (Exception $e) { //"Duplicate entry 'test--test' for key 'idx_unique'" $ex = true; $this->assertContains("Duplicate entry", $e->getMessage()); } $this->assertTrue($ex, "Duplicate entry 'test--test' for key 'idx_unique' must be thrown here (3)"); //Trying to create with the same unique key as $cl but different primary key $cl2 = $this->getCloudLocationEntity($cl2Identifier); //Checks they're different $this->assertNotEquals(self::CL_LOC_ID, $cl2Identifier); //unique key should cause error, and should not be just ignored $ex = false; try { $cl2->save(); $this->cleanupCloudLocations(); } catch (Exception $e) { $ex = true; $this->assertContains("Duplicate entry", $e->getMessage()); } $this->assertTrue($ex, "Duplicate entry 'test--test' for key 'idx_unique' must be thrown here (2)"); $this->assertNull(CloudLocation::findPk($cl2Identifier)); $this->cleanupCloudLocations(); }
/** * {@inheritdoc} * @see \Scalr\Modules\PlatformModuleInterface::getInstanceType() */ public function getInstanceType($instanceTypeId, \Scalr_Environment $env, $cloudLocation = null) { $cloudLocationId = CloudLocation::calculateCloudLocationId(SERVER_PLATFORMS::GCE, $cloudLocation, $this->getEndpointUrl($env)); $cit = CloudInstanceType::findPk($cloudLocationId, $instanceTypeId); if ($cit === null) { $instanceTypes = $this->getInstanceTypes($env, $cloudLocation, true); if (!empty($instanceTypes[$instanceTypeId])) { $cit = CloudInstanceType::findPk($cloudLocationId, $instanceTypeId); } } return $cit; }
/** * {@inheritdoc} * @see \Scalr\Modules\PlatformModuleInterface::getInstanceTypes() */ public function getInstanceTypes(\Scalr_Environment $env = null, $cloudLocation = null, $details = false) { if (!$env instanceof \Scalr_Environment) { throw new \InvalidArgumentException(sprintf("Method %s requires environment to be specified.", __METHOD__)); } $ret = []; $detailed = []; //Trying to retrieve instance types from the cache $url = $this->getConfigVariable(static::API_URL, $env); $collection = $this->getCachedInstanceTypes($this->platform, $url, $cloudLocation ?: ''); if ($collection === false || $collection->count() == 0) { //No cache. Fetching data from the cloud $client = $env->cloudstack($this->platform); foreach ($client->listServiceOfferings() as $offering) { $detailed[(string) $offering->id] = array('name' => (string) $offering->name, 'ram' => (string) $offering->memory, 'vcpus' => (string) $offering->cpunumber, 'disk' => "", 'type' => strtoupper((string) $offering->storagetype)); if (!$details) { $ret[(string) $offering->id] = (string) $offering->name; } else { $ret[(string) $offering->id] = $detailed[(string) $offering->id]; } } //Refreshes/creates a cache CloudLocation::updateInstanceTypes($this->platform, $url, $cloudLocation ?: '', $detailed); } else { //Takes data from cache foreach ($collection as $cloudInstanceType) { /* @var $cloudInstanceType \Scalr\Model\Entity\CloudInstanceType */ if (!$details) { $ret[$cloudInstanceType->instanceTypeId] = $cloudInstanceType->name; } else { $ret[$cloudInstanceType->instanceTypeId] = $cloudInstanceType->getProperties(); } } } return $ret; }
/** * {@inheritdoc} * @see \Scalr\Modules\PlatformModuleInterface::getInstanceTypes() */ public function getInstanceTypes(\Scalr_Environment $env = null, $cloudLocation = null, $details = false) { if (!$env instanceof \Scalr_Environment || empty($cloudLocation)) { throw new \InvalidArgumentException(sprintf("Method %s requires both environment object and cloudLocation to be specified.", __METHOD__)); } $ret = []; $detailed = []; //Trying to retrieve instance types from the cache $url = $env->cloudCredentials($this->platform)->properties[Entity\CloudCredentialsProperty::OPENSTACK_KEYSTONE_URL]; $collection = $this->getCachedInstanceTypes($this->platform, $url, $cloudLocation); if ($collection === false || $collection->count() == 0) { //No cache. Fetching data from the cloud $client = $env->openstack($this->platform, $cloudLocation); foreach ($client->servers->listFlavors() as $flavor) { $detailed[(string) $flavor->id] = array('name' => (string) $flavor->name, 'ram' => (string) $flavor->ram, 'vcpus' => (string) $flavor->vcpus, 'disk' => (string) $flavor->disk, 'type' => 'HDD'); if (!$details) { $ret[(string) $flavor->id] = (string) $flavor->name; } else { $ret[(string) $flavor->id] = $detailed[(string) $flavor->id]; } } //Refreshes/creates a cache CloudLocation::updateInstanceTypes($this->platform, $url, $cloudLocation, $detailed); } else { //Takes data from cache foreach ($collection as $cloudInstanceType) { /* @var $cloudInstanceType \Scalr\Model\Entity\CloudInstanceType */ if (!$details) { $ret[$cloudInstanceType->instanceTypeId] = $cloudInstanceType->name; } else { $ret[$cloudInstanceType->instanceTypeId] = $cloudInstanceType->getProperties(); } } } return $ret; }
/** * Normalizes an url from environment settings to use for price_history table * * @param string $url Original url * @return string Returns normalized url to use in price_history database table */ public function normalizeUrl($url) { return CloudLocation::normalizeUrl($url); }
/** * {@inheritdoc} * @see \Scalr\Modules\PlatformModuleInterface::getInstanceTypes() */ public function getInstanceTypes(\Scalr_Environment $env = null, $cloudLocation = null, $details = false) { if (!$env instanceof \Scalr_Environment || empty($cloudLocation)) { throw new \InvalidArgumentException(sprintf("Method %s requires both environment object and cloudLocation to be specified.", __METHOD__)); } $ret = []; $detailed = []; //Trying to retrieve instance types from the cache $url = $this->getConfigVariable(static::EC2_URL, $env, false, $cloudLocation); $collection = $this->getCachedInstanceTypes(\SERVER_PLATFORMS::EUCALYPTUS, $url, $cloudLocation); if ($collection === false || $collection->count() == 0) { //No cache. Fetching data from the cloud $client = $env->eucalyptus($cloudLocation); foreach ($client->describeInstanceTypes() as $item) { $detailed[(string) $item->name] = ['name' => (string) $item->name, 'ram' => (string) $item->memory, 'vcpus' => (string) $item->cpu, 'disk' => (string) $item->disk, 'type' => 'hdd']; if (!$details) { $ret[(string) $item->name] = sprintf("%s (CPUs: %d DISK: %d RAM: %d)", $item->name, $item->cpu, $item->disk, $item->memory); } else { $ret[(string) $item->name] = $detailed[(string) $item->name]; } } //Refreshes/creates a cache CloudLocation::updateInstanceTypes(\SERVER_PLATFORMS::EUCALYPTUS, $url, $cloudLocation, $detailed); } else { //Takes data from cache foreach ($collection as $cloudInstanceType) { /* @var $cloudInstanceType \Scalr\Model\Entity\CloudInstanceType */ if (!$details) { $ret[$cloudInstanceType->instanceTypeId] = sprintf("%s (CPUs: %d DISK: %d RAM: %d)", $cloudInstanceType->name, $cloudInstanceType->cpu, $cloudInstanceType->disk, $cloudInstanceType->ram); } else { $ret[$cloudInstanceType->instanceTypeId] = $cloudInstanceType->getProperties(); } } } return $ret; }
/** * {@inheritdoc} * @see \Scalr\Modules\PlatformModuleInterface::getInstanceTypes() */ public function getInstanceTypes(\Scalr_Environment $env = null, $cloudLocation = null, $details = false) { if (!$env instanceof \Scalr_Environment || empty($cloudLocation)) { throw new \InvalidArgumentException(sprintf("Method %s requires both environment object and cloudLocation to be specified.", __METHOD__)); } $collection = $this->getCachedInstanceTypes(\SERVER_PLATFORMS::AZURE, '', $cloudLocation); if ($collection === false || $collection->count() == 0) { $instanceTypesResult = $env->azure()->compute->location->getInstanceTypesList($env->keychain(SERVER_PLATFORMS::AZURE)->properties[CloudCredentialsProperty::AZURE_SUBSCRIPTION_ID], $cloudLocation); $ret = []; foreach ($instanceTypesResult as $instanceType) { $detailed[$instanceType->name] = ['name' => $instanceType->name, 'ram' => $instanceType->memoryInMB, 'vcpus' => $instanceType->numberOfCores, 'disk' => $instanceType->resourceDiskSizeInMB / 1024, 'type' => '', 'maxdatadiskcount' => $instanceType->maxDataDiskCount, 'rootdevicesize' => $instanceType->osDiskSizeInMB / 1024]; if (!$details) { $ret[$instanceType->name] = array($instanceType->name => $instanceType->name); } else { $ret[$instanceType->name] = $detailed[$instanceType->name]; } } CloudLocation::updateInstanceTypes(\SERVER_PLATFORMS::AZURE, '', $cloudLocation, $detailed); } else { //Takes data from cache foreach ($collection as $cloudInstanceType) { /* @var $cloudInstanceType \Scalr\Model\Entity\CloudInstanceType */ if (!$details) { $ret[$cloudInstanceType->instanceTypeId] = $cloudInstanceType->name; } else { $ret[$cloudInstanceType->instanceTypeId] = $cloudInstanceType->getProperties(); } } } return $ret; }
/** * Gets active instance types for the specified cloud platform, url and location * from the cache * * @param string $platform A cloud platform * @param string $url A cloud endpoint url * @param string $cloudLocation A cloud location * * @return \Scalr\Model\Collections\ArrayCollection|boolean * Returns collection of the CloudInstanceType entities on success or false otherwise */ protected function getCachedInstanceTypes($platform, $url, $cloudLocation) { //Gets a lifetime of the cached data from the config $lifetime = (int) \Scalr::config('scalr.cache.instance_types.lifetime'); //If this lifetime equals zero it means we have to warm-up cache if ($lifetime === 0) { CloudLocation::warmUp(); //We have no cached instance types return false; } //Checks whether there are active instance types in the cache taking lifetime into account. if (!CloudLocation::hasInstanceTypes($platform, $url, $cloudLocation, $lifetime)) { //No cached data return false; } //Fetches cloud location entity from the cache $cl = CloudLocation::findPk(CloudLocation::calculateCloudLocationId($platform, $cloudLocation, $url)); //It is possible that it might have been already removed if (!$cl instanceof CloudLocation) { //No cached data return false; } //Retrieves an instance types for this cloud location. //We should actually return only active types. return $cl->getActiveInstanceTypes(); }
/** * xGetPlatformInstanceTypesAction * * @param string $platform The name of the cloud platform * @param string $cloudLocation The cloud location * @param string $envId optional The identifier of the environment * @param string $effectiveDate optional The date on which prices should be applied YYYY-MM-DD * @throws \Exception */ public function xGetPlatformInstanceTypesAction($platform, $cloudLocation, $envId = null, $effectiveDate = null) { list($curDate, $effectiveDate) = $this->handleEffectiveDate($effectiveDate); $pm = PlatformFactory::NewPlatform($platform); $env = null; $url = ''; try { if (!empty($envId)) { $env = Scalr_Environment::init()->loadById($envId); if (PlatformFactory::isOpenstack($platform)) { $key = $platform . '.' . OpenstackPlatformModule::KEYSTONE_URL; } else { if (PlatformFactory::isCloudstack($platform)) { $key = $platform . '.' . CloudstackPlatformModule::API_URL; } else { if ($platform == SERVER_PLATFORMS::EUCALYPTUS) { $key = EucalyptusPlatformModule::EC2_URL; $url = $this->getContainer()->analytics->prices->normalizeUrl($env->getPlatformConfigValue($key, false, $cloudLocation)); } else { throw new Exception('This action is not yet supported for the specified cloud platform.'); } } } if (empty($url)) { $url = $this->getContainer()->analytics->prices->normalizeUrl($env->getPlatformConfigValue($key)); } } else { if ($platform == SERVER_PLATFORMS::EC2 || $platform == SERVER_PLATFORMS::GCE) { $gcenvid = $this->getPlatformEnvId($platform); $env = Scalr_Environment::init()->loadById($gcenvid); } } } catch (Exception $e) { if (stristr($e->getMessage(), 'not found')) { //Tries to find url from the cloud_locations table if (empty($url) && (PlatformFactory::isOpenstack($platform) || PlatformFactory::isCloudstack($platform))) { $clEntity = CloudLocation::findOne([['platform' => $platform], ['cloudLocation' => $cloudLocation]], ['updated' => false]); if ($clEntity instanceof CloudLocation) { $url = $clEntity->url; } } } else { throw $e; } } $result = $this->getTypesWithPrices($cloudLocation, $url, $pm, $platform, $effectiveDate, $env); $this->response->data(['data' => $result]); }
/** * Gets cloud instance type's name * * @param string $instanceTypeId Instance type identifier * @param string $envId Environment identifier * @param string $platform Platform * @param string $cloudLocation Cloud location * @return string */ public function getInstanceTypeName($instanceTypeId, $envId, $platform, $cloudLocation) { static $instanceTypeNames = []; static $urlCache = []; if ($platform == \SERVER_PLATFORMS::EC2) { return $instanceTypeId; } if (!isset($instanceTypeNames[$envId][$platform][$cloudLocation][$instanceTypeId])) { try { if (!isset($urlCache[$envId][$platform])) { $curEnv = Scalr_Environment::init()->loadById($envId); $platformObj = PlatformFactory::NewPlatform($platform); $urlCache[$envId][$platform] = $platformObj->getEndpointUrl($curEnv); } $cloudLocationId = CloudLocation::calculateCloudLocationId($platform, $cloudLocation, $urlCache[$envId][$platform]); $rows = $this->db->Execute("\n SELECT cit.instance_type_id, cit.name\n FROM cloud_instance_types cit\n WHERE cit.cloud_location_id = UNHEX(?)\n ", [str_replace('-', '', $cloudLocationId)]); while ($row = $rows->FetchRow()) { $instanceTypeNames[$envId][$platform][$cloudLocation][$row['instance_type_id']] = $row['name']; } } catch (Exception $e) { } } return !empty($instanceTypeNames[$envId][$platform][$cloudLocation][$instanceTypeId]) ? $instanceTypeNames[$envId][$platform][$cloudLocation][$instanceTypeId] : $instanceTypeId; }
<?php require_once __DIR__ . '/../src/prepend.inc.php'; use Scalr\Model\Entity\CloudLocation; //Cleans out cloud locations cache CloudLocation::warmUp();
/** * xGetPlatformInstanceTypesAction * * @param string $platform The name of the cloud platform * @param string $cloudLocation The cloud location * @param string $envId optional The identifier of the environment * @param string $effectiveDate optional The date on which prices should be applied YYYY-MM-DD * @throws \Exception */ public function xGetPlatformInstanceTypesAction($platform, $cloudLocation, $envId = null, $effectiveDate = null) { list($curDate, $effectiveDate) = $this->handleEffectiveDate($effectiveDate); $pm = PlatformFactory::NewPlatform($platform); $env = null; $url = ''; try { if (!empty($envId)) { $env = Scalr_Environment::init()->loadById($envId); if (PlatformFactory::isOpenstack($platform)) { $key = Entity\CloudCredentialsProperty::OPENSTACK_KEYSTONE_URL; } else { if (PlatformFactory::isCloudstack($platform)) { $key = Entity\CloudCredentialsProperty::CLOUDSTACK_API_URL; } else { throw new Exception('This action is not yet supported for the specified cloud platform.'); } } if (empty($url)) { $url = $env->keychain($platform)->properties[$key]; } } else { if ($platform == SERVER_PLATFORMS::EC2 || $platform == SERVER_PLATFORMS::GCE) { $gcenvid = $this->getEnvIdByPlatform($platform); $env = Scalr_Environment::init()->loadById($gcenvid); } } } catch (Exception $e) { if (stristr($e->getMessage(), 'not found')) { //Tries to find url from the cloud_locations table if (empty($url) && (PlatformFactory::isOpenstack($platform) || PlatformFactory::isCloudstack($platform))) { $clEntity = CloudLocation::findOne([['platform' => $platform], ['cloudLocation' => $cloudLocation]], null, ['updated' => false]); if ($clEntity instanceof CloudLocation) { $url = $clEntity->url; } } } else { throw $e; } } $result = $this->getTypesWithPrices($cloudLocation, $url, $pm, $platform, $effectiveDate, $env); $this->response->data(['data' => $result]); }
/** * {@inheritdoc} * @see \Scalr\Modules\PlatformModuleInterface::getInstanceTypes() */ public function getInstanceTypes(\Scalr_Environment $env = null, $cloudLocation = null, $details = false) { if (!$env instanceof \Scalr_Environment || empty($cloudLocation)) { throw new \InvalidArgumentException(sprintf("Method %s requires both environment object and cloudLocation to be specified.", __METHOD__)); } $gceClient = $this->getClient($env); $projectId = $env->getPlatformConfigValue(self::PROJECT_ID); $ret = []; $detailed = []; //Trying to retrieve instance types from the cache $collection = $this->getCachedInstanceTypes(\SERVER_PLATFORMS::GCE, '', $cloudLocation); if ($collection === false || $collection->count() == 0) { //No cache. Fetching data from the cloud $types = $gceClient->machineTypes->listMachineTypes($projectId, $cloudLocation); foreach ($types->items as $item) { $isEphemeral = substr($item->name, -2) == '-d'; if (!$isEphemeral) { $detailed[(string) $item->name] = ['name' => (string) $item->name, 'description' => (string) $item->description, 'ram' => (string) $item->memoryMb, 'vcpus' => (string) $item->guestCpus, 'disk' => (string) $item->imageSpaceGb, 'type' => "HDD"]; if (!$details) { $ret[(string) $item->name] = "{$item->name} ({$item->description})"; } else { $ret[(string) $item->name] = $detailed[(string) $item->name]; } } } //Refreshes/creates a cache CloudLocation::updateInstanceTypes(\SERVER_PLATFORMS::GCE, '', $cloudLocation, $detailed); } else { //Takes data from cache foreach ($collection as $cloudInstanceType) { /* @var $cloudInstanceType \Scalr\Model\Entity\CloudInstanceType */ if (!$details) { $ret[$cloudInstanceType->instanceTypeId] = $cloudInstanceType->name . "(" . $cloudInstanceType->options->description . ")"; } else { $ret[$cloudInstanceType->instanceTypeId] = $cloudInstanceType->getProperties(); } } } return $ret; }
/** * Gets a normalized url for an each platform * * @param string $platform Cloud platform * @return string Returns url */ public function getUrl($platform) { if (!isset($this->aUrl[$platform])) { if ($platform == \SERVER_PLATFORMS::EC2) { $value = ''; } else { if (PlatformFactory::isOpenstack($platform)) { $value = CloudLocation::normalizeUrl($this->env->getPlatformConfigValue($platform . '.' . OpenstackPlatformModule::KEYSTONE_URL)); } else { if (PlatformFactory::isCloudstack($platform)) { $value = CloudLocation::normalizeUrl($this->env->getPlatformConfigValue($platform . '.' . CloudstackPlatformModule::API_URL)); } else { if ($platform == \SERVER_PLATFORMS::GCE) { $value = ''; } } } } $this->aUrl[$platform] = $value; } return $this->aUrl[$platform]; }
/** * @test */ public function testWarmUp() { CloudLocation::warmUp(); }