PageRenderTime 64ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/scalr-2/tags/scalr-2.1.0/app/src/Modules/Platforms/Ec2/Observers/Eip.php

http://scalr.googlecode.com/
PHP | 316 lines | 239 code | 37 blank | 40 comment | 26 complexity | dd0a833a65220bb8c28d5478c7480857 MD5 | raw file
Possible License(s): LGPL-2.1, Apache-2.0, GPL-3.0
  1. <?php
  2. class Modules_Platforms_Ec2_Observers_Eip extends EventObserver
  3. {
  4. public $ObserverName = 'Elastic IPs';
  5. function __construct()
  6. {
  7. parent::__construct();
  8. $this->Crypto = Core::GetInstance("Crypto", CONFIG::$CRYPTOKEY);
  9. }
  10. /**
  11. * Return new instance of AmazonEC2 object
  12. *
  13. * @return AmazonEC2
  14. */
  15. private function GetAmazonEC2ClientObject(Scalr_Environment $environment, $region)
  16. {
  17. // Return new instance of AmazonEC2 object
  18. $AmazonEC2Client = Scalr_Service_Cloud_Aws::newEc2(
  19. $region,
  20. $environment->getPlatformConfigValue(Modules_Platforms_Ec2::PRIVATE_KEY),
  21. $environment->getPlatformConfigValue(Modules_Platforms_Ec2::CERTIFICATE)
  22. );
  23. return $AmazonEC2Client;
  24. }
  25. /**
  26. * Release used elastic IPs if farm terminated
  27. *
  28. * @param FarmTerminatedEvent $event
  29. */
  30. public function OnFarmTerminated(FarmTerminatedEvent $event)
  31. {
  32. $this->Logger->info(sprintf(_("Keep elastic IPs: %s"), $event->KeepElasticIPs));
  33. if ($event->KeepElasticIPs == 1)
  34. return;
  35. $DBFarm = DBFarm::LoadByID($this->FarmID);
  36. $ips = $this->DB->GetAll("SELECT * FROM elastic_ips WHERE farmid=?", array($this->FarmID));
  37. if (count($ips) > 0)
  38. {
  39. foreach ($ips as $ip)
  40. {
  41. try
  42. {
  43. $DBFarmRole = DBFarmRole::LoadByID($ip['farm_roleid']);
  44. $EC2Client = $this->GetAmazonEC2ClientObject($DBFarm->GetEnvironmentObject(), $DBFarmRole->GetSetting(DBFarmRole::SETTING_CLOUD_LOCATION));
  45. $EC2Client->ReleaseAddress($ip["ipaddress"]);
  46. }
  47. catch(Exception $e)
  48. {
  49. if (!stristr($e->getMessage(), "does not belong to you"))
  50. {
  51. $this->Logger->error(sprintf(_("Cannot release elastic IP %s from farm %s: %s"),
  52. $ip['ipaddress'], $DBFarm->Name, $e->getMessage()
  53. ));
  54. continue;
  55. }
  56. }
  57. $this->DB->Execute("DELETE FROM elastic_ips WHERE ipaddress=?", array($ip['ipaddress']));
  58. }
  59. }
  60. }
  61. /**
  62. * Check Elastic IP availability
  63. *
  64. */
  65. private function CheckElasticIP($ipaddress, $EC2Client)
  66. {
  67. $this->Logger->debug(sprintf(_("Checking IP: %s"), $ipaddress));
  68. $DescribeAddressesType = new DescribeAddressesType();
  69. $DescribeAddressesType->AddAddress($ipaddress);
  70. try
  71. {
  72. $info = $EC2Client->DescribeAddresses($DescribeAddressesType);
  73. if ($info && $info->addressesSet->item)
  74. return true;
  75. else
  76. return false;
  77. }
  78. catch(Exception $e)
  79. {
  80. return false;
  81. }
  82. }
  83. /**
  84. * Allocate and Assign Elastic IP to instance if role use it.
  85. *
  86. * @param HostUpEvent $event
  87. */
  88. public function OnHostUp(HostUpEvent $event)
  89. {
  90. if ($event->DBServer->replaceServerID)
  91. return;
  92. try
  93. {
  94. $DBFarm = DBFarm::LoadByID($this->FarmID);
  95. $DBFarmRole = $event->DBServer->GetFarmRoleObject();
  96. if (!$DBFarmRole->GetSetting(DBFarmRole::SETTING_AWS_USE_ELASIC_IPS))
  97. return;
  98. $EC2Client = $this->GetAmazonEC2ClientObject($DBFarm->GetEnvironmentObject(), $DBFarmRole->GetSetting(DBFarmRole::SETTING_CLOUD_LOCATION));
  99. }
  100. catch(Exeption $e)
  101. {
  102. return;
  103. }
  104. // Check for already allocated and free elastic IP in database
  105. $ip = $this->DB->GetRow("SELECT * FROM elastic_ips WHERE farmid=? AND ((farm_roleid=? AND instance_index=?) OR server_id = ?)",
  106. array($this->FarmID, $DBFarmRole->ID, $event->DBServer->index, $event->DBServer->serverId)
  107. );
  108. $this->Logger->debug(sprintf(_("Found IP address: %s"), $ip['ipaddress']));
  109. //
  110. // Check IP address
  111. //
  112. if ($ip['ipaddress'])
  113. {
  114. if (!$this->CheckElasticIP($ip['ipaddress'], $EC2Client))
  115. {
  116. Logger::getLogger(LOG_CATEGORY::FARM)->warn(new FarmLogMessage(
  117. $this->FarmID,
  118. sprintf(_("Elastic IP '%s' does not belong to you. Allocating new one."),
  119. $ip['ipaddress']
  120. )
  121. ));
  122. $this->DB->Execute("DELETE FROM elastic_ips WHERE ipaddress=?", array($ip['ipaddress']));
  123. $ip = false;
  124. }
  125. }
  126. // If free IP not found we must allocate new IP
  127. if (!$ip)
  128. {
  129. $this->Logger->debug(sprintf(_("Farm role: %s, %s, %s"),
  130. $DBFarmRole->GetRoleObject()->name, $DBFarmRole->AMIID, $DBFarmRole->ID
  131. ));
  132. $alocated_ips = $this->DB->GetOne("SELECT COUNT(*) FROM elastic_ips WHERE farm_roleid=?",
  133. array($DBFarmRole->ID)
  134. );
  135. $this->Logger->debug(sprintf(_("Allocated IPs: %s, MaxInstances: %s"),
  136. $alocated_ips, $DBFarmRole->GetSetting(DBFarmRole::SETTING_SCALING_MAX_INSTANCES)
  137. ));
  138. // Check elastic IPs limit. We cannot allocate more than 'Max instances' option for role
  139. if ($alocated_ips < $DBFarmRole->GetSetting(DBFarmRole::SETTING_SCALING_MAX_INSTANCES))
  140. {
  141. try
  142. {
  143. // Alocate new IP address
  144. $address = $EC2Client->AllocateAddress();
  145. }
  146. catch (Exception $e)
  147. {
  148. Logger::getLogger(LOG_CATEGORY::FARM)->error(new FarmLogMessage(
  149. $this->FarmID,
  150. sprintf(_("Cannot allocate new elastic ip for instance '%s': %s"),
  151. $event->DBServer->serverId,
  152. $e->getMessage()
  153. )
  154. ));
  155. return;
  156. }
  157. // Add allocated IP address to database
  158. $this->DB->Execute("INSERT INTO elastic_ips SET farmid=?, farm_roleid=?, ipaddress=?, state='0', server_id='', clientid=?, instance_index=?",
  159. array($this->FarmID, $DBFarmRole->ID, $address->publicIp, $DBFarm->ClientID, $event->DBServer->index)
  160. );
  161. $ip['ipaddress'] = $address->publicIp;
  162. Logger::getLogger(LOG_CATEGORY::FARM)->info(new FarmLogMessage(
  163. $this->FarmID,
  164. sprintf(_("Allocated new IP: %s"),
  165. $ip['ipaddress']
  166. )
  167. ));
  168. // Waiting...
  169. $this->Logger->debug(_("Waiting 5 seconds..."));
  170. sleep(5);
  171. }
  172. else
  173. $this->Logger->fatal(_("Limit for elastic IPs reached. Check zomby records in database."));
  174. }
  175. // If we have ip address
  176. if ($ip['ipaddress'])
  177. {
  178. $assign_retries = 1;
  179. try
  180. {
  181. while (true)
  182. {
  183. try
  184. {
  185. // Associate elastic ip address with instance
  186. $EC2Client->AssociateAddress(
  187. $event->DBServer->GetProperty(EC2_SERVER_PROPERTIES::INSTANCE_ID),
  188. $ip['ipaddress']
  189. );
  190. }
  191. catch(Exception $e)
  192. {
  193. if (!stristr($e->getMessage(), "does not belong to you") || $assign_retries == 3)
  194. throw new Exception($e->getMessage());
  195. else
  196. {
  197. // Waiting...
  198. $this->Logger->debug(_("Waiting 2 seconds..."));
  199. sleep(2);
  200. $assign_retries++;
  201. continue;
  202. }
  203. }
  204. break;
  205. }
  206. }
  207. catch(Exception $e)
  208. {
  209. Logger::getLogger(LOG_CATEGORY::FARM)->error(new FarmLogMessage(
  210. $this->FarmID,
  211. sprintf(_("Cannot associate elastic ip with instance: %s"),
  212. $e->getMessage()
  213. )
  214. ));
  215. return;
  216. }
  217. $this->Logger->info("IP: {$ip['ipaddress']} assigned to instance '{$event->DBServer->serverId}'");
  218. // Update leastic IPs table
  219. $this->DB->Execute("UPDATE elastic_ips SET state='1', server_id=? WHERE ipaddress=?",
  220. array($event->DBServer->serverId, $ip['ipaddress'])
  221. );
  222. Scalr::FireEvent($this->FarmID, new IPAddressChangedEvent($event->DBServer, $ip['ipaddress']));
  223. }
  224. else
  225. {
  226. Logger::getLogger(LOG_CATEGORY::FARM)->fatal(new FarmLogMessage(
  227. $this->FarmID,
  228. sprintf(_("Cannot allocate elastic ip address for instance %s on farm %s"),
  229. $event->DBServer->serverId,
  230. $DBFarm->Name
  231. )
  232. ));
  233. }
  234. }
  235. /**
  236. * Release IP address when instance terminated
  237. *
  238. * @param HostDownEvent $event
  239. */
  240. public function OnHostDown(HostDownEvent $event)
  241. {
  242. if ($event->DBServer->IsRebooting())
  243. return;
  244. try
  245. {
  246. $DBFarm = DBFarm::LoadByID($this->FarmID);
  247. if ($event->replacementDBServer)
  248. {
  249. $ip = $this->DB->GetRow("SELECT * FROM elastic_ips WHERE server_id=?", array($event->DBServer->serverId));
  250. if ($ip)
  251. {
  252. $EC2Client = $this->GetAmazonEC2ClientObject($DBFarm->GetEnvironmentObject(), $event->DBServer->GetProperty(EC2_SERVER_PROPERTIES::REGION));
  253. try
  254. {
  255. // Associate elastic ip address with instance
  256. $EC2Client->AssociateAddress(
  257. $event->replacementDBServer->GetProperty(EC2_SERVER_PROPERTIES::INSTANCE_ID),
  258. $ip['ipaddress']
  259. );
  260. }
  261. catch(Exception $e)
  262. {
  263. if (!stristr($e->getMessage(), "does not belong to you") || $assign_retries == 3)
  264. throw new Exception($e->getMessage());
  265. }
  266. }
  267. }
  268. else
  269. {
  270. $this->DB->Execute("UPDATE elastic_ips SET state='0', server_id='' WHERE server_id=?", array($event->DBServer->serverId));
  271. }
  272. }
  273. catch(Exception $e)
  274. {
  275. //
  276. }
  277. }
  278. }
  279. ?>