PageRenderTime 57ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/Resources/Companion/AdminRole/VMManagerService/WindowsAzureVMManager.cs

https://bitbucket.org/zgramana/azure-accelerators-project
C# | 2914 lines | 2394 code | 260 blank | 260 comment | 224 complexity | 04c8234ddac225de300c1450463802fe MD5 | raw file
Possible License(s): LGPL-2.0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Runtime.Serialization;
  5. using System.ServiceModel;
  6. using System.Text;
  7. using Microsoft.WindowsAzure.ServiceRuntime;
  8. using System.Xml;
  9. using System.ServiceModel.Syndication;
  10. using System.IO;
  11. using System.Collections.Specialized;
  12. using System.Diagnostics;
  13. using Microsoft.WindowsAzure;
  14. using Microsoft.WindowsAzure.StorageClient;
  15. using Microsoft.WindowsAzure.StorageClient.Protocol;
  16. using System.Runtime.Serialization.Formatters.Soap;
  17. using System.Net;
  18. using System.Xml.Linq;
  19. using System.Threading;
  20. using System.IdentityModel.Selectors;
  21. using System.IdentityModel.Tokens;
  22. using System.ServiceModel.Description;
  23. using System.ServiceModel.Activation;
  24. using Microsoft.Web.Administration;
  25. using System.Security.Cryptography.X509Certificates;
  26. using System.Net.Security;
  27. namespace WindowsAzureCompanion.VMManagerService
  28. {
  29. public class WindowsAzureVMManagerUsernamePasswordValidator : UserNamePasswordValidator
  30. {
  31. // Validate credentials
  32. public override void Validate(string userName, string password)
  33. {
  34. if (!(userName.Equals(RoleEnvironment.GetConfigurationSettingValue("AdminUserName"))
  35. && password.Equals(RoleEnvironment.GetConfigurationSettingValue("AdminPassword"))))
  36. {
  37. throw new SecurityTokenValidationException("Invalid credentials. Access denied.");
  38. }
  39. }
  40. }
  41. [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
  42. public class WindowsAzureVMManager : IVMManager
  43. {
  44. // Constants
  45. public static string PHPRuntimeProductID = "PHP_Runtime";
  46. public static string MySQLBasedDBCategory = "MySQL_BASED_DB";
  47. public static string MySQLCommunityServerProductID = "MySQL_Community_Server";
  48. public static string MariaDBProductID = "MariaDB";
  49. public static string LibraryFolderForPHP = "includes";
  50. public static string FrameworkSDKCategoryForPHP = "Frameworks and SDKs";
  51. public static string CustomExtensionCategoryForPHP = "PHP_Custom_Extension";
  52. public static string WebApplicationCategoryForPHP = "Web Applications";
  53. public static string MySQLCommunityServerFolder = "mysql";
  54. public static string MariaDBServerFolder = "mariadb";
  55. public static string RuntimeFolderForPHP = "php";
  56. public static string ExtensionsFolderForPHP = "ext";
  57. public static string ApplicationsFolder = "applications";
  58. public static string ApplicationsDownloadFolder = "downloads";
  59. public static string ApplicationsUnzipUtility = "UnzipUtility.vbs";
  60. public static string ApplicationsUntarUtility = "ExtractUtility.php";
  61. public static string SecondaryWebSiteName = "PHPWebSite";
  62. public static string AdminWebSiteNameInServiceDefinition = "Web";
  63. private string applicationsAndRuntimeResourceFolder = null;
  64. // Storage account and backup container refereences
  65. private CloudBlobClient blobClient = null;
  66. private CloudBlobContainer container = null;
  67. // Mounted Windows Azure drive
  68. private CloudDrive drive = null;
  69. private CloudPageBlob xdrivePageBlob = null;
  70. // Installation Status Collection and corresponding blob
  71. private NameValueCollection installationStatusCollection = null;
  72. private CloudBlob installationStatusBlob = null;
  73. // Cron job information (ProductID => Thread)
  74. private Dictionary<string, List<Thread>> cronJobs = new Dictionary<string, List<Thread>>();
  75. // Progress information
  76. private CloudBlob progressInformationBlob = null;
  77. private static object locker = new object();
  78. // Constructor for the service
  79. public WindowsAzureVMManager()
  80. {
  81. // Create HTTPS storage endpoint
  82. CloudStorageAccount storageAccount = WindowsAzureVMManager.GetStorageAccount(true);
  83. // Get backup contain reference
  84. blobClient = storageAccount.CreateCloudBlobClient();
  85. string containerName = RoleEnvironment.GetConfigurationSettingValue("PHPApplicationsBackupContainerName");
  86. container = blobClient.GetContainerReference(containerName);
  87. if (container.CreateIfNotExist())
  88. {
  89. // TODO: Finally do not provide public access
  90. BlobContainerPermissions containerPermissions = new BlobContainerPermissions();
  91. containerPermissions.PublicAccess = BlobContainerPublicAccessType.Container;
  92. container.SetPermissions(containerPermissions);
  93. }
  94. // Initialize installation status information and corresponding blob
  95. InitializeInstallationStatus();
  96. // Initialize progress information and corresponding blob
  97. InitializeProgressInformation();
  98. }
  99. // Initialize installation status information and corresponding blob
  100. private void InitializeInstallationStatus()
  101. {
  102. string blobName = RoleEnvironment.GetConfigurationSettingValue("InstallationStatusConfigFileBlob");
  103. installationStatusBlob = container.GetBlobReference(blobName);
  104. if (!WindowsAzureVMManager.BlobExists(installationStatusBlob))
  105. {
  106. // Create empty NameValueCollection and serialize to blob
  107. installationStatusCollection = new NameValueCollection();
  108. SerializeNameValueCollectionToBlob(installationStatusCollection, installationStatusBlob);
  109. }
  110. else
  111. {
  112. // Deserialize NameValueCollection to blob
  113. installationStatusCollection = DeserializeNameValueCollectionFromBlob(installationStatusBlob);
  114. }
  115. }
  116. // Initialize progress information and corresponding blob
  117. private void InitializeProgressInformation()
  118. {
  119. string blobName = RoleEnvironment.GetConfigurationSettingValue("ProgressInformationFileBlob");
  120. progressInformationBlob = container.GetBlobReference(blobName);
  121. if (!WindowsAzureVMManager.BlobExists(progressInformationBlob))
  122. {
  123. // Serialize empty NameValueCollection to blob
  124. NameValueCollection progressInformation = new NameValueCollection();
  125. SerializeNameValueCollectionToBlob(progressInformation,
  126. progressInformationBlob);
  127. }
  128. }
  129. // Install specified applications
  130. public bool InstallApplications(IDictionary<string, string> applicationsToInstall)
  131. {
  132. try
  133. {
  134. // Update progress information
  135. UpdateProgressInformation("Installing Platform/Applications", false);
  136. // Create a seperate thread for installing applications
  137. ThreadStart starter = delegate { InstallApplicationsOnAnotherThread(applicationsToInstall); };
  138. Thread thread = new Thread(starter);
  139. thread.Start();
  140. }
  141. catch (Exception ex)
  142. {
  143. UpdateProgressInformation("Unable to start application installation. Error: " + ex.Message, true);
  144. return false;
  145. }
  146. return true;
  147. }
  148. // Install specified applications
  149. private bool InstallApplicationsOnAnotherThread(IDictionary<string, string> applicationsToInstall)
  150. {
  151. try
  152. {
  153. XmlReader reader = XmlReader.Create(RoleEnvironment.GetConfigurationSettingValue("ProductListXmlFeed"));
  154. SyndicationFeed feed = SyndicationFeed.Load(reader);
  155. // Get list of selected products and their download URLs
  156. var varProducts = from item in feed.Items
  157. where applicationsToInstall.Keys.Contains(
  158. item.ElementExtensions.ReadElementExtensions<string>("productId", "http://www.w3.org/2005/Atom")[0])
  159. select item;
  160. // Create top level folders for PHP Runtime and Applications
  161. string phpRunTimeFolder = Path.Combine(applicationsAndRuntimeResourceFolder, WindowsAzureVMManager.RuntimeFolderForPHP);
  162. if (!Directory.Exists(phpRunTimeFolder))
  163. {
  164. Directory.CreateDirectory(phpRunTimeFolder);
  165. }
  166. string phpLibraryFolder = Path.Combine(phpRunTimeFolder, WindowsAzureVMManager.LibraryFolderForPHP);
  167. if (!Directory.Exists(phpLibraryFolder))
  168. {
  169. Directory.CreateDirectory(phpLibraryFolder);
  170. }
  171. if (!Directory.Exists(Path.Combine(applicationsAndRuntimeResourceFolder, WindowsAzureVMManager.ApplicationsFolder)))
  172. {
  173. Directory.CreateDirectory(Path.Combine(applicationsAndRuntimeResourceFolder, WindowsAzureVMManager.ApplicationsFolder));
  174. }
  175. string downloadFolder = Path.Combine(applicationsAndRuntimeResourceFolder, WindowsAzureVMManager.ApplicationsDownloadFolder);
  176. if (!Directory.Exists(downloadFolder))
  177. Directory.CreateDirectory(downloadFolder);
  178. // Whether to start Hosted Web Core for PHP
  179. bool startPHPWebSite = false;
  180. // Install Applications
  181. foreach (var product in varProducts)
  182. {
  183. string productId = product.ElementExtensions.ReadElementExtensions<string>("productId", "http://www.w3.org/2005/Atom")[0];
  184. if (installationStatusCollection.AllKeys.Contains(productId))
  185. {
  186. Trace.TraceWarning("Application {0} already installed.", productId);
  187. continue;
  188. }
  189. string installCategory = product.ElementExtensions.ReadElementExtensions<string>("installCategory", "http://www.w3.org/2005/Atom")[0];
  190. string[] applicationsToInstallInfo = applicationsToInstall[productId].ToString().Split(',');
  191. string productVersion = applicationsToInstallInfo[0];
  192. // Get product properties passed from UI
  193. NameValueCollection productProperties = new NameValueCollection();
  194. for (int i = 1; i < applicationsToInstallInfo.Length; i++)
  195. {
  196. string[] property = applicationsToInstallInfo[i].Split('=');
  197. productProperties.Add(property[0], property[1]);
  198. }
  199. string installPath = "/";
  200. if (productProperties.AllKeys.Contains("installPath"))
  201. {
  202. installPath = productProperties["installPath"];
  203. }
  204. // Set other readonly properties (if any)
  205. SyndicationElementExtension productPropertiesElementExtension =
  206. product.ElementExtensions.Where<SyndicationElementExtension>
  207. (x => x.OuterName == "productProperties").FirstOrDefault();
  208. if (productPropertiesElementExtension != null)
  209. {
  210. foreach (XElement productPropertyExtension in productPropertiesElementExtension.GetObject<XElement>().Elements())
  211. {
  212. XAttribute captionAttribute = productPropertyExtension.Attribute("caption");
  213. if (captionAttribute == null)
  214. {
  215. XAttribute nameAttribute = productPropertyExtension.Attribute("name");
  216. XAttribute valueAttribute = productPropertyExtension.Attribute("value");
  217. if ((nameAttribute != null) && (valueAttribute != null))
  218. {
  219. productProperties.Add(nameAttribute.Value, valueAttribute.Value);
  220. }
  221. }
  222. }
  223. }
  224. IInstaller installer = null;
  225. if (productId.Equals(WindowsAzureVMManager.PHPRuntimeProductID))
  226. {
  227. // PHP Runtime
  228. installer = new PHPRuntimeInstaller(
  229. Path.Combine(applicationsAndRuntimeResourceFolder, WindowsAzureVMManager.RuntimeFolderForPHP),
  230. downloadFolder,
  231. product,
  232. productVersion);
  233. startPHPWebSite = true;
  234. }
  235. else if (installCategory.Equals(WindowsAzureVMManager.MySQLBasedDBCategory))
  236. {
  237. // Allow only one MySQL based database on the VM
  238. if ((installationStatusCollection.AllKeys.Contains(WindowsAzureVMManager.MySQLCommunityServerProductID))
  239. || (installationStatusCollection.AllKeys.Contains(WindowsAzureVMManager.MariaDBProductID)))
  240. {
  241. Trace.TraceError("Only one MySQL based database can be installed and {0} is alreadyinstalled.",
  242. MySQLBasedDBInstaller.GetMySQLBasedDBName());
  243. }
  244. else
  245. {
  246. // Root password for MySQL based DB
  247. string rootPassword = "";
  248. if (productProperties.AllKeys.Contains("rootPassword"))
  249. {
  250. rootPassword = productProperties["rootPassword"];
  251. }
  252. if (productId.Equals(WindowsAzureVMManager.MySQLCommunityServerProductID))
  253. {
  254. // MySQL Community Server
  255. installer = new MySQLCommunityServerInstaller(
  256. Path.Combine(applicationsAndRuntimeResourceFolder, WindowsAzureVMManager.MySQLCommunityServerFolder),
  257. downloadFolder,
  258. product,
  259. productVersion,
  260. productProperties);
  261. }
  262. else if (productId.Equals(WindowsAzureVMManager.MariaDBProductID))
  263. {
  264. // MariaDB server
  265. installer = new MariaDBInstaller(
  266. Path.Combine(applicationsAndRuntimeResourceFolder, WindowsAzureVMManager.MariaDBServerFolder),
  267. downloadFolder,
  268. product,
  269. productVersion,
  270. productProperties);
  271. }
  272. else
  273. {
  274. Trace.TraceError("Invalid MySQL based database in applications feed");
  275. }
  276. }
  277. }
  278. else if (installCategory.Equals(WindowsAzureVMManager.CustomExtensionCategoryForPHP))
  279. {
  280. // PHP Custom extension
  281. installer = new PHPExtensionInstaller(
  282. Path.Combine(applicationsAndRuntimeResourceFolder, WindowsAzureVMManager.RuntimeFolderForPHP),
  283. downloadFolder,
  284. product,
  285. productVersion);
  286. startPHPWebSite = true;
  287. }
  288. else if (installCategory.Equals(WindowsAzureVMManager.FrameworkSDKCategoryForPHP))
  289. {
  290. // PHP Framework or SDK
  291. installer = new PHPFrameworkSDKInstaller(
  292. Path.Combine(
  293. Path.Combine(applicationsAndRuntimeResourceFolder,
  294. WindowsAzureVMManager.RuntimeFolderForPHP),
  295. WindowsAzureVMManager.LibraryFolderForPHP),
  296. downloadFolder,
  297. product,
  298. productVersion);
  299. }
  300. else if (installCategory.Equals(WindowsAzureVMManager.WebApplicationCategoryForPHP))
  301. {
  302. // PHP Web Application
  303. installer = new PHPApplicationInstaller(
  304. Path.Combine(applicationsAndRuntimeResourceFolder, WindowsAzureVMManager.ApplicationsFolder),
  305. installPath,
  306. downloadFolder,
  307. product,
  308. productVersion,
  309. productProperties);
  310. }
  311. else
  312. {
  313. Trace.TraceWarning("Invalid installation type.");
  314. continue;
  315. }
  316. // Install the product
  317. if (installer != null)
  318. {
  319. try
  320. {
  321. installer.Install();
  322. // Set product as instaleld into installedProductIds and update status in blob
  323. string[] installStatusInfo = null;
  324. if (installCategory.Equals(WindowsAzureVMManager.MySQLBasedDBCategory))
  325. {
  326. installStatusInfo = new string[] {
  327. DateTime.Now.ToString(),
  328. installPath,
  329. productVersion,
  330. productProperties["iniFileName"]
  331. };
  332. }
  333. else
  334. {
  335. installStatusInfo = new string[] {
  336. DateTime.Now.ToString(),
  337. installPath,
  338. productVersion
  339. };
  340. }
  341. // Setup cron job (if any)
  342. if (productProperties.AllKeys.Contains("cronJobs"))
  343. {
  344. string applicationInstallPath = Path.Combine(applicationsAndRuntimeResourceFolder,
  345. WindowsAzureVMManager.ApplicationsFolder);
  346. if (!installPath.Equals("/"))
  347. {
  348. applicationInstallPath = Path.Combine(applicationInstallPath,
  349. installPath.Replace("/", "\\").Trim('\\'));
  350. }
  351. SetupCronJobs(productId, productProperties["cronJobs"], applicationInstallPath);
  352. }
  353. installationStatusCollection.Add(productId, string.Join(",", installStatusInfo));
  354. WindowsAzureVMManager.SerializeNameValueCollectionToBlob(installationStatusCollection, installationStatusBlob);
  355. }
  356. catch (Exception ex)
  357. {
  358. UpdateProgressInformation(string.Format("Failed to install {0}. Error: {1}", product.Title.Text , ex.Message), true);
  359. return false;
  360. }
  361. }
  362. // Check if explicit web site restart is requested
  363. if (productProperties.AllKeys.Contains("restartWebSite"))
  364. {
  365. if (productProperties["restartWebSite"].ToLower().Equals("true"))
  366. {
  367. startPHPWebSite = true;
  368. }
  369. }
  370. }
  371. // Start or Restart Hosted Web Core if PHP Runtime or extension is installed
  372. if (startPHPWebSite)
  373. {
  374. // Re-configure PHP runtime
  375. string phpInstallFolder =
  376. Path.Combine(applicationsAndRuntimeResourceFolder, WindowsAzureVMManager.RuntimeFolderForPHP);
  377. PHPRuntimeInstaller.ReConfigurePHPRuntime(phpInstallFolder);
  378. // Restart PHP Web Site
  379. // Get latest handle to PHP web site
  380. ServerManager serverManager = new ServerManager();
  381. Site secondaryWebSite = serverManager.Sites[WindowsAzureVMManager.SecondaryWebSiteName];
  382. if (secondaryWebSite != null)
  383. {
  384. if (secondaryWebSite.State == ObjectState.Started)
  385. {
  386. RestartPHPWebSite();
  387. }
  388. else
  389. {
  390. StartPHPWebSite();
  391. }
  392. }
  393. else
  394. {
  395. StartPHPWebSite();
  396. }
  397. }
  398. }
  399. catch (Exception ex)
  400. {
  401. UpdateProgressInformation("Installation failed. Error: " + ex.Message, true);
  402. return false;
  403. }
  404. ClearProgressInformation();
  405. return true;
  406. }
  407. // Get cron job property for specified index
  408. private string GetCronJobProperty(string productId, int cronJobIndex)
  409. {
  410. XmlReader reader = XmlReader.Create(RoleEnvironment.GetConfigurationSettingValue("ProductListXmlFeed"));
  411. SyndicationFeed feed = SyndicationFeed.Load(reader);
  412. string[] cronJobsInfoArray = GetCronJobsProperty(feed, productId).Split(',');
  413. if (cronJobIndex < 0 || cronJobIndex >= cronJobsInfoArray.Length)
  414. {
  415. return null;
  416. }
  417. else
  418. {
  419. return cronJobsInfoArray[cronJobIndex];
  420. }
  421. }
  422. // Get cron job properties
  423. private string GetCronJobsProperty(string productId)
  424. {
  425. XmlReader reader = XmlReader.Create(RoleEnvironment.GetConfigurationSettingValue("ProductListXmlFeed"));
  426. SyndicationFeed feed = SyndicationFeed.Load(reader);
  427. return GetCronJobsProperty(feed, productId);
  428. }
  429. // Get cron job properties
  430. private string GetCronJobsProperty(SyndicationFeed feed, string productId)
  431. {
  432. SyndicationItem product = (from item in feed.Items
  433. where item.ElementExtensions.ReadElementExtensions<string>("productId", "http://www.w3.org/2005/Atom")[0].Equals(productId)
  434. select item).SingleOrDefault();
  435. // Check if cronjob is defined for this product
  436. SyndicationElementExtension productPropertiesElementExtension =
  437. product.ElementExtensions.Where<SyndicationElementExtension>
  438. (x => x.OuterName == "productProperties").FirstOrDefault();
  439. if (productPropertiesElementExtension != null)
  440. {
  441. foreach (XElement productPropertyExtension in productPropertiesElementExtension.GetObject<XElement>().Elements())
  442. {
  443. XAttribute nameAttribute = productPropertyExtension.Attribute("name");
  444. if (nameAttribute != null)
  445. {
  446. if (nameAttribute.Value.Equals("cronJobs"))
  447. {
  448. XAttribute valueAttribute = productPropertyExtension.Attribute("value");
  449. if (valueAttribute != null)
  450. {
  451. return valueAttribute.Value;
  452. }
  453. }
  454. }
  455. }
  456. }
  457. // Not found
  458. return null;
  459. }
  460. // Setup cron jobs for installed applications (if any)
  461. private void SetupCronJobsForInstalledApplications()
  462. {
  463. try
  464. {
  465. XmlReader reader = XmlReader.Create(RoleEnvironment.GetConfigurationSettingValue("ProductListXmlFeed"));
  466. SyndicationFeed feed = SyndicationFeed.Load(reader);
  467. // Get list of installed products
  468. var varProducts = from item in feed.Items
  469. where installationStatusCollection.AllKeys.Contains(
  470. item.ElementExtensions.ReadElementExtensions<string>("productId", "http://www.w3.org/2005/Atom")[0])
  471. select item;
  472. foreach (var product in varProducts)
  473. {
  474. // Check if cronjob is defined for this product
  475. SyndicationElementExtension productPropertiesElementExtension =
  476. product.ElementExtensions.Where<SyndicationElementExtension>
  477. (x => x.OuterName == "productProperties").FirstOrDefault();
  478. if (productPropertiesElementExtension != null)
  479. {
  480. foreach (XElement productPropertyExtension in productPropertiesElementExtension.GetObject<XElement>().Elements())
  481. {
  482. XAttribute nameAttribute = productPropertyExtension.Attribute("name");
  483. if (nameAttribute != null)
  484. {
  485. if (nameAttribute.Value.Equals("cronJobs"))
  486. {
  487. XAttribute valueAttribute = productPropertyExtension.Attribute("value");
  488. if (valueAttribute != null)
  489. {
  490. string productId = product.ElementExtensions.ReadElementExtensions<string>("productId", "http://www.w3.org/2005/Atom")[0];
  491. string[] installInfo = installationStatusCollection[productId].Split(',');
  492. string installPath = installInfo[1];
  493. string applicationInstallPath = Path.Combine(applicationsAndRuntimeResourceFolder,
  494. WindowsAzureVMManager.ApplicationsFolder);
  495. if (!installPath.Equals("/"))
  496. {
  497. applicationInstallPath = Path.Combine(applicationInstallPath,
  498. installPath.Replace("/", "\\").Trim('\\'));
  499. }
  500. SetupCronJobs(productId, valueAttribute.Value, applicationInstallPath);
  501. }
  502. }
  503. }
  504. }
  505. }
  506. }
  507. }
  508. catch (Exception ex)
  509. {
  510. Trace.TraceError("Error in cron jobs setup. Error: {0}", ex.Message);
  511. }
  512. }
  513. // Setup cron jobs
  514. private void SetupCronJobs(string productId, string cronJobsInfo, string applicationInstallPath)
  515. {
  516. try
  517. {
  518. if (!cronJobs.Keys.Contains(productId))
  519. {
  520. string phpInstallFolder =
  521. Path.Combine(applicationsAndRuntimeResourceFolder, WindowsAzureVMManager.RuntimeFolderForPHP);
  522. string phpExeFileName = Path.Combine(phpInstallFolder, "php.exe");
  523. List<Thread> threadList = new List<Thread>();
  524. string[] cronJobsInfoArray = cronJobsInfo.Split(',');
  525. foreach (string cronJobInfo in cronJobsInfoArray)
  526. {
  527. string[] cronJobInfoArray = cronJobInfo.Split(';');
  528. string cronJobInitialStatus = cronJobInfoArray[4];
  529. // Start the cron job if specified in the feed
  530. if (cronJobInitialStatus.ToLower().Equals("true"))
  531. {
  532. string cronJobFileName = Path.Combine(applicationInstallPath,
  533. cronJobInfoArray[2].Replace("/", "\\").Trim('\\'));
  534. int cronJobFrequencyInSecond = int.Parse(cronJobInfoArray[3]);
  535. // Create a thread for cron job
  536. ThreadStart starter = delegate { CronJobThreadRoutine(phpExeFileName, cronJobFileName, cronJobFrequencyInSecond); };
  537. Thread thread = new Thread(starter);
  538. threadList.Add(thread);
  539. thread.Start();
  540. }
  541. else
  542. {
  543. threadList.Add(null);
  544. }
  545. }
  546. // Add thread list to dictionary
  547. cronJobs.Add(productId, threadList);
  548. }
  549. }
  550. catch (Exception ex)
  551. {
  552. Trace.TraceError("Error in cron job {0} setup. Error: {1}", cronJobsInfo, ex.Message);
  553. }
  554. }
  555. // Cron job thread routine
  556. private void CronJobThreadRoutine(string processFileName, string processArguments, int cronJobFrequencyInSecond)
  557. {
  558. Trace.TraceInformation("Scheduling {0} as cron job with frequency {1} s.", processArguments, cronJobFrequencyInSecond);
  559. while (true)
  560. {
  561. try
  562. {
  563. Trace.TraceInformation("Running {0} as cron job.", processArguments);
  564. Process process = new Process();
  565. process.StartInfo.UseShellExecute = false;
  566. process.StartInfo.RedirectStandardInput = true;
  567. process.StartInfo.RedirectStandardOutput = true;
  568. // Output data received handler external process
  569. process.OutputDataReceived += new DataReceivedEventHandler(OutputDataReceivedHandler);
  570. // setting the file name and arguments
  571. process.StartInfo.FileName = processFileName;
  572. process.StartInfo.Arguments = processArguments;
  573. process.Start();
  574. // Start the asynchronous read of the output stream.
  575. process.BeginOutputReadLine();
  576. // Wait for cron job to exit
  577. process.WaitForExit();
  578. // Sleep for specified duration
  579. Thread.Sleep(cronJobFrequencyInSecond * 1000);
  580. }
  581. catch (ThreadAbortException)
  582. {
  583. Trace.TraceError("Cron job {0} {1} being aborted", processFileName, processArguments);
  584. }
  585. catch (Exception ex)
  586. {
  587. Trace.TraceError("Cron job {0} {1} failed: {2}", processFileName, processArguments, ex.Message);
  588. }
  589. }
  590. }
  591. // Stop all cron jobs
  592. public void StopAllCronJobs()
  593. {
  594. try
  595. {
  596. if (cronJobs.Count > 0)
  597. {
  598. Trace.TraceInformation("Stopping all cron jobs...");
  599. foreach (KeyValuePair<string, List<Thread>> pair in cronJobs)
  600. {
  601. List<Thread> threadList = pair.Value;
  602. foreach (Thread thread in threadList)
  603. {
  604. if (thread != null)
  605. {
  606. try
  607. {
  608. thread.Abort();
  609. thread.Join();
  610. }
  611. catch (ThreadStateException)
  612. {
  613. thread.Resume();
  614. }
  615. }
  616. }
  617. // Clear List
  618. threadList.Clear();
  619. }
  620. // Clear Dictionary
  621. cronJobs.Clear();
  622. Trace.TraceInformation("Stopped all cron jobs");
  623. }
  624. }
  625. catch (Exception ex)
  626. {
  627. Trace.TraceError("Error stopping all cron jobs. Error: {0}", ex.Message);
  628. }
  629. }
  630. // Get all cron jobs with their status
  631. public List<string> GetCronJobs()
  632. {
  633. List<string> allCronJobs = null;
  634. try
  635. {
  636. if (cronJobs.Count > 0)
  637. {
  638. allCronJobs = new List<string>();
  639. XmlReader reader = XmlReader.Create(RoleEnvironment.GetConfigurationSettingValue("ProductListXmlFeed"));
  640. SyndicationFeed feed = SyndicationFeed.Load(reader);
  641. foreach (KeyValuePair<string, List<Thread>> pair in cronJobs)
  642. {
  643. string productId = pair.Key;
  644. List<Thread> threadList = pair.Value;
  645. string cronJobsInfo = GetCronJobsProperty(feed, productId);
  646. string[] cronJobsInfoArray = cronJobsInfo.Split(',');
  647. List<string> cronJobsInfoStatus = new List<string>();
  648. for (int i = 0; i < cronJobsInfoArray.Length; i++)
  649. {
  650. Thread thread = threadList[i];
  651. string isJobStarted = (thread != null).ToString();
  652. string[] cronJobInfoArray = cronJobsInfoArray[i].Split(';');
  653. // Set actual thread staus in cronJobInfoArray
  654. cronJobInfoArray[4] = isJobStarted;
  655. cronJobsInfoStatus.Add(string.Join(";", cronJobInfoArray));
  656. }
  657. allCronJobs.Add(productId + "," + string.Join(",", cronJobsInfoStatus.ToArray()));
  658. }
  659. }
  660. }
  661. catch (Exception ex)
  662. {
  663. Trace.TraceError("Error in listing all cron jobs. Error: {0}", ex.Message);
  664. }
  665. return allCronJobs;
  666. }
  667. // Is Cron Job started for specified product id and cron job index?
  668. public bool IsCronJobStarted(string productId, int cronJobIndex)
  669. {
  670. if (cronJobs.Keys.Contains(productId))
  671. {
  672. List<Thread> threadList = cronJobs[productId];
  673. if (cronJobIndex >= 0 && cronJobIndex < threadList.Count)
  674. {
  675. Thread thread = threadList[cronJobIndex];
  676. if (thread != null)
  677. {
  678. return true;
  679. }
  680. }
  681. }
  682. return false;
  683. }
  684. // Start cron job for specified product id and cron job index
  685. public bool StartCronJob(string productId, int cronJobIndex)
  686. {
  687. if (cronJobs.Keys.Contains(productId))
  688. {
  689. List<Thread> threadList = cronJobs[productId];
  690. if (cronJobIndex >= 0 && cronJobIndex < threadList.Count)
  691. {
  692. Thread thread = threadList[cronJobIndex];
  693. if (thread == null)
  694. {
  695. string cronJobInfo = GetCronJobProperty(productId, cronJobIndex);
  696. if (string.IsNullOrEmpty(cronJobInfo))
  697. {
  698. return false;
  699. }
  700. string[] installInfo = installationStatusCollection[productId].Split(',');
  701. string installPath = installInfo[1];
  702. string applicationInstallPath = Path.Combine(applicationsAndRuntimeResourceFolder,
  703. WindowsAzureVMManager.ApplicationsFolder);
  704. if (!installPath.Equals("/"))
  705. {
  706. applicationInstallPath = Path.Combine(applicationInstallPath,
  707. installPath.Replace("/", "\\").Trim('\\'));
  708. }
  709. string[] cronJobInfoArray = cronJobInfo.Split(';');
  710. string cronJobFileName = Path.Combine(applicationInstallPath,
  711. cronJobInfoArray[2].Replace("/", "\\").Trim('\\'));
  712. int cronJobFrequencyInSecond = int.Parse(cronJobInfoArray[3]);
  713. // Create a thread for cron job
  714. string phpInstallFolder =
  715. Path.Combine(applicationsAndRuntimeResourceFolder, WindowsAzureVMManager.RuntimeFolderForPHP);
  716. string phpExeFileName = Path.Combine(phpInstallFolder, "php.exe");
  717. ThreadStart starter = delegate { CronJobThreadRoutine(phpExeFileName, cronJobFileName, cronJobFrequencyInSecond); };
  718. thread = new Thread(starter);
  719. // Replace null with new thread
  720. threadList[cronJobIndex] = thread;
  721. thread.Start();
  722. return true;
  723. }
  724. else
  725. {
  726. Trace.TraceError("Cron job already started.");
  727. return false;
  728. }
  729. }
  730. }
  731. return false;
  732. }
  733. // Stop cron job for specified product id and cron job index
  734. public bool StopCronJob(string productId, int cronJobIndex)
  735. {
  736. if (cronJobs.Keys.Contains(productId))
  737. {
  738. List<Thread> threadList = cronJobs[productId];
  739. if (cronJobIndex >= 0 && cronJobIndex < threadList.Count)
  740. {
  741. Thread thread = threadList[cronJobIndex];
  742. if (thread != null)
  743. {
  744. try
  745. {
  746. thread.Abort();
  747. thread.Join();
  748. }
  749. catch (ThreadStateException)
  750. {
  751. thread.Resume();
  752. }
  753. // Set null into threadlist
  754. threadList[cronJobIndex] = null;
  755. return true;
  756. }
  757. else
  758. {
  759. Trace.TraceError("Cron job already stopped.");
  760. return false;
  761. }
  762. }
  763. }
  764. return false;
  765. }
  766. // Restart cron job for specified product id and cron job index
  767. public bool RestartCronJob(string productId, int cronJobIndex)
  768. {
  769. if (cronJobs.Keys.Contains(productId))
  770. {
  771. if (StopCronJob(productId, cronJobIndex))
  772. {
  773. return StartCronJob(productId, cronJobIndex);
  774. }
  775. else
  776. {
  777. return false;
  778. }
  779. }
  780. else
  781. {
  782. return false;
  783. }
  784. }
  785. // Uninstall specified applications
  786. public bool UninstallApplications(List<string> applications)
  787. {
  788. return false;
  789. }
  790. // Set as application in IIS
  791. public bool SetAsApplicationInIIS(string installPath, string applicationPath)
  792. {
  793. try
  794. {
  795. // Get latest handle to PHP web site
  796. ServerManager serverManager = new ServerManager();
  797. Site secondaryWebSite = serverManager.Sites[WindowsAzureVMManager.SecondaryWebSiteName];
  798. if (secondaryWebSite != null)
  799. {
  800. Trace.TraceInformation("Trying to set IIS Application at {0}.", applicationPath);
  801. Application app = secondaryWebSite.Applications.Add(installPath, applicationPath);
  802. if (app != null)
  803. {
  804. // Set application pool name as that of main web site
  805. app.ApplicationPoolName = serverManager.Sites.First().Applications.First().ApplicationPoolName;
  806. serverManager.CommitChanges();
  807. serverManager.ApplicationPools[app.ApplicationPoolName].Recycle();
  808. // Put some delay for IIS
  809. Thread.Sleep(5000);
  810. return true;
  811. }
  812. else
  813. {
  814. return false;
  815. }
  816. }
  817. else
  818. {
  819. return false;
  820. }
  821. }
  822. catch (Exception ex)
  823. {
  824. Trace.TraceError("Unable to set application in IIS. Error: {0}.", ex.Message);
  825. return false;
  826. }
  827. }
  828. // Get list of Windows Azure Drive Snapshots
  829. public List<string> GetWindowsAzureDriveSnapshots()
  830. {
  831. List<string> snapshotUris = new List<string>();
  832. try
  833. {
  834. NameValueCollection metadata = xdrivePageBlob.Metadata;
  835. foreach (string key in metadata.AllKeys)
  836. {
  837. snapshotUris.Add(string.Join(",", new string[] { key, metadata[key] }));
  838. }
  839. }
  840. catch (Exception ex)
  841. {
  842. Trace.TraceError("Could not get list of Windows Azure Drive snapshots. Error: {0}", ex.Message);
  843. }
  844. return snapshotUris;
  845. }
  846. // Create Windows Azure Drive Snapshot
  847. public string CreateWindowsAzureDriveSnapshot(string snapshotComment)
  848. {
  849. string uristring = null;
  850. try
  851. {
  852. if (IsDriveMounted())
  853. {
  854. if (drive != null)
  855. {
  856. Uri uri = drive.Snapshot();
  857. uristring = uri.ToString();
  858. // Get snapshot timestamp
  859. string timestamp = uristring.Split('?')[1].Split('=')[1];
  860. // Get serialized status.xml
  861. byte[] bytesToEncode = Encoding.UTF8.GetBytes(WindowsAzureVMManager.
  862. SerializeNameValueCollectionToString(installationStatusCollection));
  863. string status = Convert.ToBase64String(bytesToEncode);
  864. // Add Uri to metadata of page blob
  865. string metadata = string.Join(",", new string[] { snapshotComment, uristring, status });
  866. xdrivePageBlob.Metadata.Add(timestamp, metadata);
  867. Trace.TraceInformation("Created Windows Azure Drive snapshot {0}", uristring);
  868. }
  869. else
  870. {
  871. Trace.TraceError("Windows Azure Drive not mounted.");
  872. }
  873. }
  874. else
  875. {
  876. Trace.TraceError("Windows Azure Drive not mounted.");
  877. }
  878. }
  879. catch (Exception ex)
  880. {
  881. Trace.TraceError(ex.Message);
  882. }
  883. return uristring;
  884. }
  885. // Promote Windows Azure Drive Snapshot
  886. public bool PromoteWindowsAzureDriveSnapshot(string uri)
  887. {
  888. if (string.IsNullOrEmpty(uri))
  889. {
  890. Trace.TraceError("Invalid snapshot uri {0}", uri);
  891. return false;
  892. }
  893. try
  894. {
  895. CloudBlob snapshotBlob = xdrivePageBlob.ServiceClient.GetBlobReference(uri);
  896. if (snapshotBlob.SnapshotTime.HasValue == false)
  897. {
  898. Trace.TraceError("Invalid snapshot uri {0}", uri);
  899. return false;
  900. }
  901. // Update progress information
  902. UpdateProgressInformation("Promoting Windows Azure Drive snapshot...", false);
  903. // Create a seperate thread for promoting blob snapshot
  904. ThreadStart starter = delegate { PromoteWindowsAzureDriveSnapshotOnAnotherThread(uri, snapshotBlob); };
  905. Thread thread = new Thread(starter);
  906. thread.Start();
  907. }
  908. catch (Exception ex)
  909. {
  910. UpdateProgressInformation("Could not promote Windows Azure Drive Snapshot. Error: " + ex.Message, true);
  911. return false;
  912. }
  913. return true;
  914. }
  915. // Promote Windows Azure Drive Snapshot on another thread
  916. private bool PromoteWindowsAzureDriveSnapshotOnAnotherThread(string uri, CloudBlob snapshotBlob)
  917. {
  918. try
  919. {
  920. // Get snapshot timestamp
  921. string timestamp = uri.Split('?')[1].Split('=')[1];
  922. // Get snapshot properties from metadata
  923. NameValueCollection metadata = new NameValueCollection(xdrivePageBlob.Metadata);
  924. string snapshotProperties = metadata[timestamp];
  925. // Stop all runtimes
  926. if (StopAllRuntimes() == false)
  927. {
  928. UpdateProgressInformation("Unbale to stop all runtime and failed to promote Windows Azure Drive Snapshot.", true);
  929. return false;
  930. }
  931. // Unmount the Windows Azure Drive
  932. if (IsDriveMounted())
  933. {
  934. if (drive != null)
  935. {
  936. UpdateProgressInformation("Unmounting Windows Azure Drive...", false);
  937. UnmountXDrive();
  938. UpdateProgressInformation("Unmounted Windows Azure Drive.", false);
  939. Thread.Sleep(5000);
  940. }
  941. }
  942. if (WindowsAzureVMManager.BlobExists(xdrivePageBlob))
  943. {
  944. // In cloud, blob snapshot delete works. It does not work in devfabric
  945. xdrivePageBlob.CopyFromBlob(snapshotBlob);
  946. }
  947. else
  948. {
  949. // In devfabric
  950. }
  951. // Restore status.xml
  952. UpdateProgressInformation("Restoring status.xml file...", false);
  953. if (string.IsNullOrEmpty(snapshotProperties))
  954. {
  955. Trace.TraceError("Could not find metadata for Windows Azure Drive snapshot with timestamp={0}.", timestamp);
  956. }
  957. else
  958. {
  959. string[] snapshotPropertiesArray = snapshotProperties.Split(',');
  960. if (snapshotPropertiesArray.Length != 3)
  961. {
  962. Trace.TraceError("Could not find status information in metadata for Windows Azure Drive snapshot with timestamp={0}.", timestamp);
  963. }
  964. else
  965. {
  966. string base64StatusContent = snapshotPropertiesArray[2];
  967. byte[] bytesToEncode = Convert.FromBase64String(base64StatusContent);
  968. if (bytesToEncode == null)
  969. {
  970. Trace.TraceError("Could not read status information as bytearray in metadata for Windows Azure Drive snapshot.");
  971. }
  972. else
  973. {
  974. string statusContent = Encoding.UTF8.GetString(bytesToEncode);
  975. installationStatusCollection = WindowsAzureVMManager.DeserializeNameValueCollectionFromString(statusContent);
  976. WindowsAzureVMManager.SerializeNameValueCollectionToBlob(installationStatusCollection, installationStatusBlob);
  977. UpdateProgressInformation("Restored status.xml file.", false);
  978. }
  979. }
  980. }
  981. // Mount drive again
  982. Thread.Sleep(5000);
  983. UpdateProgressInformation("Mounting Windows Azure Drive again...", false);
  984. if (MountXDrive())
  985. {
  986. UpdateProgressInformation("Mounted Windows Azure Drive again.", false);
  987. // Now reset metadata, as copyblob clears existing metadata
  988. xdrivePageBlob.Metadata.Clear();
  989. foreach (string key in metadata.AllKeys)
  990. {
  991. xdrivePageBlob.Metadata.Add(key, metadata[key]);
  992. }
  993. UpdateProgressInformation("Restored Windows Azure Drive snapshot metadata.", false);
  994. // Setup Runtime Servers
  995. SetupRuntimeServers();
  996. ClearProgressInformation();
  997. return true;
  998. }
  999. else
  1000. {
  1001. UpdateProgressInformation("Could not promote Windows Azure Drive Snapshot as Widnows Azure Drive could not be remounted.", true);
  1002. return false;
  1003. }
  1004. }
  1005. catch (Exception ex)
  1006. {
  1007. UpdateProgressInformation("Could not promote Windows Azure Drive Snapshot. Error: " + ex.Message, true);
  1008. return false;
  1009. }
  1010. }
  1011. // Delete Windows Azure Drive Snapshot
  1012. public bool DeleteWindowsAzureDriveSnapshot(string uri)
  1013. {
  1014. if (string.IsNullOrEmpty(uri))
  1015. {
  1016. return false;
  1017. }
  1018. try
  1019. {
  1020. CloudBlob snapshotBlob = xdrivePageBlob.ServiceClient.GetBlobReference(uri);
  1021. if (snapshotBlob.SnapshotTime.HasValue)
  1022. {
  1023. // Get snapshot timestamp
  1024. string timestamp = uri.Split('?')[1].Split('=')[1];
  1025. // Get snapshot properties from metadata
  1026. NameValueCollection metadata = new NameValueCollection(xdrivePageBlob.Metadata);
  1027. string snapshotProperties = xdrivePageBlob.Metadata[timestamp];
  1028. if (WindowsAzureVMManager.BlobExists(xdrivePageBlob))
  1029. {
  1030. // In cloud, blob snapshot delete works. It does not work in devfabric
  1031. if (!snapshotBlob.DeleteIfExists())
  1032. {
  1033. return false;
  1034. }
  1035. }
  1036. else
  1037. {
  1038. string snapshotFolder = string.Empty;
  1039. // This is Windows Azure SDK 1.3 Specific, so might break in future
  1040. string csrunEnv = Environment.GetEnvironmentVariable("_CSRUN_STATE_DIRECTORY");
  1041. if (string.IsNullOrEmpty(csrunEnv))
  1042. {
  1043. snapshotFolder = Path.Combine(
  1044. Environment.GetEnvironmentVariable("LOCALAPPDATA"),
  1045. @"dftmp\wadd\devstoreaccount1\");
  1046. }
  1047. else
  1048. {
  1049. snapshotFolder = Path.Combine(csrunEnv, @"wadd\devstoreaccount1\");
  1050. }
  1051. // Get Windows Azure Drive container and blob names from service configuration file
  1052. string xdriveContainerName = RoleEnvironment.GetConfigurationSettingValue("PHPApplicationsBackupContainerName");
  1053. string xdriveBlobName = RoleEnvironment.GetConfigurationSettingValue("XDrivePageBlobName");
  1054. snapshotFolder = Path.Combine(snapshotFolder, xdriveContainerName);
  1055. snapshotFolder = Path.Combine(snapshotFolder, xdriveBlobName);
  1056. snapshotFolder += "!" + timestamp.Replace(":", "_");
  1057. // Delete the snapshot blob folder in devfabric
  1058. if (Directory.Exists(snapshotFolder))
  1059. {
  1060. Directory.Delete(snapshotFolder, true);
  1061. }
  1062. }
  1063. // Now reset metadata, as delete snapshot clears existing metadata
  1064. metadata.Remove(timestamp);
  1065. xdrivePageBlob.Metadata.Clear();
  1066. foreach (string key in metadata.AllKeys)
  1067. {
  1068. xdrivePageBlob.Metadata.Add(key, metadata[key]);
  1069. }
  1070. }
  1071. }
  1072. catch (Exception ex)
  1073. {
  1074. Trace.TraceError("Could not delete blob snapshot. Error: {0}", ex.Message);
  1075. return false;
  1076. }
  1077. return true;
  1078. }
  1079. // Unmount Windows Azure Drive
  1080. public void UnmountXDrive()
  1081. {
  1082. if (IsDriveMounted())
  1083. {
  1084. if (drive != null)
  1085. {
  1086. string uriString = drive.Uri.ToString();
  1087. Trace.TraceInformation("Unmounting Windows Azure Drive...");
  1088. drive.Unmount();
  1089. Trace.TraceInformation("Successfully unmounted Windows Azure Drive at Uri {0}", uriString);
  1090. }
  1091. }
  1092. }
  1093. // Mount XDrive
  1094. public bool MountXDrive()
  1095. {
  1096. // Create HTTP storage endpoint, needed by Windows Azure Drive
  1097. CloudStorageAccount xdriveStorageAccount = WindowsAzureVMManager.GetStorageAccount(false);
  1098. // Initialize local cache
  1099. LocalResource localCache = RoleEnvironment.GetLocalResource("CloudDriveCache");
  1100. Char[] backSlash = { '\\' };
  1101. String localCachePath = localCache.RootPath.TrimEnd(backSlash);
  1102. CloudDrive.InitializeCache(localCachePath, localCache.MaximumSizeInMegabytes);
  1103. // Get Windows Azure Drive container and blob names from service configuration file
  1104. string xdriveContainerName = RoleEnvironment.GetConfigurationSettingValue("PHPApplicationsBackupContainerName");
  1105. string xdriveBlobName = RoleEnvironment.GetConfigurationSettingValue("XDrivePageBlobName");
  1106. // Create blob container, if it does not exist
  1107. CloudBlobClient blobClient = xdriveStorageAccount.CreateCloudBlobClient();
  1108. blobClient.GetContainerReference(xdriveContainerName).CreateIfNotExist();
  1109. // Get Windows Azure Drive page blob reference
  1110. xdrivePageBlob = blobClient
  1111. .GetContainerReference(xdriveContainerName)
  1112. .GetPageBlobReference(xdriveBlobName);
  1113. // Get a reference to the requested Windows Azure Drive
  1114. drive = xdriveStorageAccount.CreateCloudDrive(
  1115. xdrivePageBlob.Uri.ToString()
  1116. );
  1117. // Create drive
  1118. try
  1119. {
  1120. drive.Create(int.Parse(RoleEnvironment.GetConfigurationSettingValue("XDriveSizeInMB")));
  1121. }
  1122. catch (CloudDriveException)
  1123. {
  1124. // exception is also thrown if all is well but the drive already exists, hence ignore exception
  1125. }
  1126. try
  1127. {
  1128. // This is 1 VM solution only, so always mount drive in write mode.
  1129. string xdriveLetter = drive.Mount(
  1130. int.Parse(RoleEnvironment.GetConfigurationSettingValue("XDriveCacheSizeInMB")),
  1131. DriveMountOptions.None);
  1132. Trace.TraceInformation("Mounted Windows Azure Drive at uri {0}", drive.Uri);
  1133. Trace.TraceInformation("Applications are durable to Windows Azure Page Blob.");
  1134. // Use different mechanism for devfabric and cloud to determine applicationsAndRuntimeResourceFolder
  1135. if (RoleEnvironment.DeploymentId.StartsWith("deployment"))
  1136. {
  1137. // This is Windows Azure SDK 1.3 Specific, so might break in future
  1138. string csrunEnv = Environment.GetEnvironmentVariable("_CSRUN_STATE_DIRECTORY");
  1139. if (string.IsNullOrEmpty(csrunEnv))
  1140. {
  1141. applicationsAndRuntimeResourceFolder = Path.Combine(
  1142. Environment.GetEnvironmentVariable("LOCALAPPDATA"),
  1143. @"dftmp\wadd\devstoreaccount1\");
  1144. }
  1145. else
  1146. {
  1147. applicationsAndRuntimeResourceFolder = Path.Combine(csrunEnv, @"wadd\devstoreaccount1\");
  1148. }
  1149. // Get Windows Azure Drive container and blob names from service configuration file
  1150. applicationsAndRuntimeResourceFolder = Path.Combine(applicationsAndRuntimeResourceFolder, xdriveContainerName);
  1151. applicationsAndRuntimeResourceFolder = Path.Combine(applicationsAndRuntimeResourceFolder, xdriveBlobName);
  1152. }
  1153. else
  1154. {
  1155. applicationsAndRuntimeResourceFolder = xdriveLetter;
  1156. }
  1157. return true;
  1158. }
  1159. catch (Exception ex)
  1160. {
  1161. applicationsAndRuntimeResourceFolder = null;
  1162. Trace.TraceError("Unable to Mount Windows Azure Drive. Error: {0}, StackTrace: {1}", ex.Message, ex.StackTrace);
  1163. return false;
  1164. }
  1165. }
  1166. // Get drive mount status
  1167. public bool IsDriveMounted()
  1168. {
  1169. if (applicationsAndRuntimeResourceFolder != null)
  1170. return true;
  1171. else
  1172. return false;
  1173. }
  1174. // Reset Windows Azure Drive
  1175. public bool ResetWindowsAzureDrive()
  1176. {
  1177. try
  1178. {
  1179. // Update progress information
  1180. UpdateProgressInformation("Resetting Windows Azure Drive", false);
  1181. // Create a seperate thread for installing applications
  1182. ThreadStart starter = delegate { ResetWindowsAzureDriveOnAnotherThread(); };
  1183. Thread thread = new Thread(starter);
  1184. thread.Start();
  1185. }
  1186. catch (Exception ex)
  1187. {
  1188. UpdateProgressInformation("Unable to start resetting Windows Azure Drive. Error: " + ex.Message, true);
  1189. return false;
  1190. }
  1191. return true;
  1192. }
  1193. // Reset Windows Azure Drive On Another Thread
  1194. public bool ResetWindowsAzureDriveOnAnotherThread()
  1195. {
  1196. Trace.TraceInformation("Started Reset for Windows Azure Drive...");
  1197. try
  1198. {
  1199. // Stop all runtimes
  1200. if (StopAllRuntimes() == false)
  1201. {
  1202. UpdateProgressInformation("Unbale to stop all runtime and failed to reset Windows Azure Drive.", true);
  1203. return false;
  1204. }
  1205. // Delete PHP Web site from IIS (if any)
  1206. ServerManager serverManager = new ServerManager();
  1207. Site secondaryWebSite = serverManager.Sites[WindowsAzureVMManager.SecondaryWebSiteName];
  1208. if (secondaryWebSite != null)
  1209. {
  1210. UpdateProgressInformation("Deleting PHP Web site...", false);
  1211. DeletePHPWebSite();
  1212. UpdateProgressInformation("Deleted PHP Web site.", false);
  1213. }
  1214. // Unmount and delete Windows Azure Drive
  1215. if (IsDriveMounted())
  1216. {
  1217. if (drive != null)
  1218. {
  1219. UpdateProgressInformation("Unmounting Windows Azure Drive...", false);
  1220. UnmountXDrive();
  1221. UpdateProgressInformation("Unmounted Windows Azure Drive.", false);
  1222. Thread.Sleep(5000);
  1223. // Delete all snapshots and parent page blob
  1224. if (WindowsAzureVMManager.BlobExists(xdrivePageBlob))
  1225. {
  1226. xdrivePageBlob.Delete(new BlobRequestOptions()
  1227. {
  1228. DeleteSnapshotsOption = DeleteSnapshotsOption.IncludeSnapshots
  1229. });
  1230. UpdateProgressInformation("Deleted pageblob and associated snapshots for the Windows Azure Drive.", false);
  1231. }
  1232. else
  1233. {
  1234. // In devfabric, delete the drive
  1235. drive.Delete();
  1236. UpdateProgressInformation("Deleted Windows Azure Drive.", false);
  1237. }
  1238. // Reset references
  1239. xdrivePageBlob = null;
  1240. drive = null;
  1241. }
  1242. }
  1243. // Reset status.xml file
  1244. UpdateProgressInformation("Resetting status.xml file...", false);
  1245. installationStatusCollection.Clear();
  1246. SerializeNameValueCollectionToBlob(installationStatusCollection, installationStatusBlob);
  1247. UpdateProgressInformation("Reset status.xml file completed.", false);
  1248. // Mount drive again
  1249. UpdateProgressInformation("Mounting Windows Azure Drive again...", false);
  1250. if (MountXDrive())
  1251. {
  1252. UpdateProgressInformation("Reset Windows Azure Drive completed.", false);
  1253. }
  1254. else
  1255. {
  1256. UpdateProgressInformation("Unable to reset Windows Azure Drive as Windows Azure Drive could not be remounted", false);
  1257. return false;
  1258. }
  1259. }
  1260. catch (Exception ex)
  1261. {
  1262. UpdateProgressInformation("Unable to reset Windows Azure Drive. Error: " + ex.Message, true);
  1263. return false;
  1264. }
  1265. ClearProgressInformation();
  1266. return true;
  1267. }
  1268. // Stop all runtimes
  1269. private bool StopAllRuntimes()
  1270. {
  1271. try
  1272. {
  1273. // Stop PHP Web Site
  1274. UpdateProgressInformation("Stopping PHP Web Site.", false);
  1275. StopPHPWebSite();
  1276. UpdateProgressInformation("Stopped PHP Web Site.", false);
  1277. Thread.Sleep(5000);
  1278. // Stop MySQL based database sever
  1279. if ((installationStatusCollection.AllKeys.Contains(WindowsAzureVMManager.MySQLCommunityServerProductID))
  1280. || (installationStatusCollection.AllKeys.Contains(WindowsAzureVMManager.MariaDBProductID)))
  1281. {
  1282. UpdateProgressInformation("Stopping MySQL based database server.", false);
  1283. if (MySQLCommunityServerInstaller.StopMySQLBasedDB())
  1284. {
  1285. UpdateProgressInformation("Stopped MySQL based database server.", false);
  1286. }
  1287. else
  1288. {
  1289. UpdateProgressInformation("Failed to stop MySQL based database server.", false);
  1290. }
  1291. }
  1292. }
  1293. catch (Exception ex)
  1294. {
  1295. Trace.TraceError("Unable to stop all runtimes. Error: {0}", ex.Message);
  1296. return false;
  1297. }
  1298. return true;
  1299. }
  1300. // Clear progress information
  1301. public void ClearProgressInformation()
  1302. {
  1303. // Serialize progress information to the blob
  1304. string blobName = RoleEnvironment.GetConfigurationSettingValue("ProgressInformationFileBlob");
  1305. NameValueCollection progressInformation = new NameValueCollection();
  1306. CloudBlob progressInformationBlob = GetCloudBlobReference(blobName);
  1307. WindowsAzureVMManager.SerializeNameValueCollectionToBlob(
  1308. progressInformation,
  1309. progressInformationBlob);
  1310. }
  1311. // Is background task running (installation/reset)
  1312. public bool IsBackgroundTaskRunning()
  1313. {
  1314. IDictionary<string, string> progressInformation = GetProgressInformation();
  1315. if (progressInformation == null || progressInformation.Count == 0)
  1316. {
  1317. return false;
  1318. }
  1319. else
  1320. {
  1321. // Remove title
  1322. string titleKey = progressInformation.Keys.First();
  1323. progressInformation.Remove(titleKey);
  1324. if (progressInformation.Count > 0)
  1325. {
  1326. // Check if last message is an error
  1327. string lastKey = progressInformation.Keys.Last();
  1328. string[] messageInfo = progressInformation[lastKey].Split('|');
  1329. if (messageInfo[2].ToLower().Equals("true"))
  1330. {
  1331. // Error found indicating async operation failed
  1332. throw new Exception(messageInfo[1]);
  1333. }
  1334. else
  1335. {
  1336. // No error, background task is still running
  1337. return true;
  1338. }
  1339. }
  1340. else
  1341. {
  1342. return true;
  1343. }
  1344. }
  1345. }
  1346. // Get progress information of current activity
  1347. public IDictionary<string, string> GetProgressInformation()
  1348. {
  1349. NameValueCollection progressInformation = null;
  1350. lock (WindowsAzureVMManager.locker)
  1351. {
  1352. // Deserialize NameValueCollection from blob
  1353. string blobName = RoleEnvironment.GetConfigurationSettingValue("ProgressInformationFileBlob");
  1354. CloudBlob progressInformationBlob = GetCloudBlobReference(blobName);
  1355. progressInformation =
  1356. DeserializeNameValueCollectionFromBlob(progressInformationBlob);
  1357. }
  1358. // Transform NameValueCollection to Dictionary.
  1359. // TODO: Need to get rid of all this dirty work
  1360. Dictionary<string, string> dicProgressInformation = new Dictionary<string, string>();
  1361. if (progressInformation != null)
  1362. {
  1363. foreach (string key in progressInformation.AllKeys)
  1364. {
  1365. dicProgressInformation.Add(key, progressInformation[key]);
  1366. }
  1367. }
  1368. return dicProgressInformation;
  1369. }
  1370. // Add progress information
  1371. public void UpdateProgressInformation(string message, bool isError)
  1372. {
  1373. lock (WindowsAzureVMManager.locker)
  1374. {
  1375. // Add to trace logs
  1376. if (isError)
  1377. {
  1378. Trace.TraceError(message);
  1379. }
  1380. else
  1381. {
  1382. Trace.TraceInformation(message);
  1383. }
  1384. // Deserialize NameValueCollection from blob
  1385. string blobName = RoleEnvironment.GetConfigurationSettingValue("ProgressInformationFileBlob");
  1386. CloudBlob progressInformationBlob = GetCloudBlobReference(blobName);
  1387. NameValueCollection progressInformation =
  1388. DeserializeNameValueCollectionFromBlob(progressInformationBlob);
  1389. progressInformation.Add(Guid.NewGuid().ToString(),
  1390. string.Join("|", new string[] {
  1391. DateTime.Now.ToString(),
  1392. message,
  1393. isError.ToString().ToLower()
  1394. })
  1395. );
  1396. // Serialize progress information to the blob
  1397. WindowsAzureVMManager.SerializeNameValueCollectionToBlob(
  1398. progressInformation,
  1399. progressInformationBlob);
  1400. }
  1401. }
  1402. // Get downlolad url from the version
  1403. public static string GetDownloadUrlFromProductVersion(SyndicationItem product, string productVersion)
  1404. {
  1405. SyndicationElementExtension elementExtension =
  1406. product.ElementExtensions.Where<SyndicationElementExtension>
  1407. (x => x.OuterName == "installerFileChoices").FirstOrDefault();
  1408. if (elementExtension != null)
  1409. {
  1410. XElement element = elementExtension.GetObject<XElement>();
  1411. // TODO: Use Linq Query instead of foreach loop
  1412. foreach (XElement extension in element.Elements())
  1413. {
  1414. if (extension.Attribute("version").Value.Equals(productVersion))
  1415. {
  1416. return extension.Attribute("url").Value;
  1417. }
  1418. }
  1419. }
  1420. // download url not found
  1421. return null;
  1422. }
  1423. // Get attribute value from product version
  1424. public static string GetAttributeValueFromProductVersion(SyndicationItem product, string productVersion, string attributeName)
  1425. {
  1426. SyndicationElementExtension elementExtension =
  1427. product.ElementExtensions.Where<SyndicationElementExtension>
  1428. (x => x.OuterName == "installerFileChoices").FirstOrDefault();
  1429. if (elementExtension != null)
  1430. {
  1431. XElement element = elementExtension.GetObject<XElement>();
  1432. // TODO: Use Linq Query instead of foreach loop
  1433. foreach (XElement extension in element.Elements())
  1434. {
  1435. if (extension.Attribute("version").Value.Equals(productVersion))
  1436. {
  1437. if (extension.Elements().Count() > 0)
  1438. {
  1439. foreach (XElement propertyElement in extension.Elements().First().Elements())
  1440. {
  1441. if (propertyElement.Attribute("name").Value.Equals(attributeName))
  1442. {
  1443. XAttribute valueAttribute = propertyElement.Attribute("value");
  1444. if (valueAttribute != null)
  1445. {
  1446. return valueAttribute.Value;
  1447. }
  1448. else
  1449. {
  1450. // Attribute not found
  1451. return null;
  1452. }
  1453. }
  1454. }
  1455. }
  1456. }
  1457. }
  1458. }
  1459. // Attribute not found
  1460. return null;
  1461. }
  1462. // Whether PHP Web Site is started
  1463. public bool IsPHPWebSiteStarted()
  1464. {
  1465. ServerManager serverManager = new ServerManager();
  1466. Site secondaryWebSite = serverManager.Sites[WindowsAzureVMManager.SecondaryWebSiteName];
  1467. if (secondaryWebSite != null)
  1468. {
  1469. if (secondaryWebSite.State == ObjectState.Started)
  1470. {
  1471. return true;
  1472. }
  1473. else
  1474. {
  1475. return false;
  1476. }
  1477. }
  1478. else
  1479. {
  1480. return false;
  1481. }
  1482. }
  1483. // Register SSL endpoint with http.sys
  1484. private void RegisterSSLWithHTTPSys(string endpointName, string sslCertificateSHA1Thumbprint)
  1485. {
  1486. try
  1487. {
  1488. Trace.TraceInformation(string.Format("Enabling SSL for {0} ...", endpointName));
  1489. IPEndPoint endpoint = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints[endpointName].IPEndpoint;
  1490. string processArguments = string.Format("http add sslcert ipport={0}:{1} certstorename=MY certhash={2} appid={{{3}}}",
  1491. endpoint.Address.ToString(),
  1492. endpoint.Port.ToString(),
  1493. sslCertificateSHA1Thumbprint,
  1494. Guid.NewGuid()
  1495. );
  1496. // Launch self extracting MSI
  1497. Process process = new Process();
  1498. process.StartInfo.UseShellExecute = false;
  1499. process.StartInfo.RedirectStandardOutput = true;
  1500. process.StartInfo.FileName = "netsh.exe";
  1501. process.StartInfo.Arguments = processArguments;
  1502. // Output data received handler for cscript netsh.exe command
  1503. process.OutputDataReceived += new DataReceivedEventHandler(OutputDataReceivedHandler);
  1504. Trace.TraceInformation(string.Format("Executing command: {0} {1}",
  1505. process.StartInfo.FileName,
  1506. process.StartInfo.Arguments));
  1507. process.Start();
  1508. // Start the asynchronous read of the output stream.
  1509. process.BeginOutputReadLine();
  1510. // Wait until process is over
  1511. process.WaitForExit();
  1512. Trace.TraceInformation(string.Format("Registerd SSL for input endpoint {0}.", endpointName));
  1513. }
  1514. catch (Exception ex)
  1515. {
  1516. Trace.TraceError(string.Format("Failed to register SSL for input endpoint {0}. Error: {1}", endpointName, ex.Message));
  1517. }
  1518. }
  1519. // Enable SSL for web site
  1520. private void EnableSSLForWebSite(Site webSite, string endpointName, string sslCertificateSHA1Thumbprint)
  1521. {
  1522. try
  1523. {
  1524. //For some reason this created a new store called 'Personal' - we'll have to figure that out
  1525. X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
  1526. store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadWrite);
  1527. //Looks like we can create this from a byte array as well
  1528. X509Certificate2Collection col = store.Certificates.Find(
  1529. X509FindType.FindByThumbprint,
  1530. sslCertificateSHA1Thumbprint,
  1531. false);
  1532. if (col.Count == 1)
  1533. {
  1534. IPEndPoint endpoint = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints[endpointName].IPEndpoint;
  1535. ServerManager serverManager = new ServerManager();
  1536. webSite.Bindings.Add(
  1537. endpoint.Address.ToString() + ":" + endpoint.Port.ToString() + ":",
  1538. col[0].GetCertHash(),
  1539. store.Name);
  1540. serverManager.CommitChanges();
  1541. // Put some delay for IIS
  1542. Thread.Sleep(5000);
  1543. Trace.TraceInformation("Enabled SSL for input endpoint {0}.", endpointName);
  1544. }
  1545. else
  1546. {
  1547. Trace.TraceError("SSL sertificate not found for input endpoint {0}.", endpointName);
  1548. }
  1549. store.Close();
  1550. }
  1551. catch (Exception ex)
  1552. {
  1553. Trace.TraceError("Failed to enable SSL for input endpoint {0}. Error: {1}", endpointName, ex.Message);
  1554. }
  1555. }
  1556. // Setup Runtime Servers
  1557. public void SetupRuntimeServers()
  1558. {
  1559. if (StartPHPWebSite())
  1560. {
  1561. Trace.TraceInformation("Started the PHP Web Site.");
  1562. }
  1563. if (StartMySQLServer())
  1564. {
  1565. Trace.TraceInformation("Started the {0}", MySQLBasedDBInstaller.GetMySQLBasedDBName());
  1566. }
  1567. }
  1568. // Has MySQL Server installed?
  1569. public bool IsMySQLServerInstalled()
  1570. {
  1571. if ((installationStatusCollection.AllKeys.Contains(WindowsAzureVMManager.MySQLCommunityServerProductID))
  1572. || (installationStatusCollection.AllKeys.Contains(WindowsAzureVMManager.MariaDBProductID)))
  1573. {
  1574. return true;
  1575. }
  1576. else
  1577. {
  1578. return false;
  1579. }
  1580. }
  1581. // Has MySQL Server started?
  1582. public bool IsMySQLServerStarted()
  1583. {
  1584. if ((installationStatusCollection.AllKeys.Contains(WindowsAzureVMManager.MySQLCommunityServerProductID))
  1585. || (installationStatusCollection.AllKeys.Contains(WindowsAzureVMManager.MariaDBProductID)))
  1586. {
  1587. return MySQLCommunityServerInstaller.IsMySQLServerStarted();
  1588. }
  1589. else
  1590. {
  1591. return false;
  1592. }
  1593. }
  1594. // Get MySQL based database server port number
  1595. public string GetMySQLBasedDBServerPortNumber()
  1596. {
  1597. if ((installationStatusCollection.AllKeys.Contains(WindowsAzureVMManager.MySQLCommunityServerProductID))
  1598. || (installationStatusCollection.AllKeys.Contains(WindowsAzureVMManager.MariaDBProductID)))
  1599. {
  1600. return MySQLCommunityServerInstaller.GetMySQLBasedDBServerPortNumber();
  1601. }
  1602. else
  1603. {
  1604. return null;
  1605. }
  1606. }
  1607. // Get MySQL based database name
  1608. public string GetMySQLBasedDBName()
  1609. {
  1610. return MySQLCommunityServerInstaller.GetMySQLBasedDBName();
  1611. }
  1612. // Get MySQL based database server IP address
  1613. public string GetMySQLBasedDBServerIPAddress()
  1614. {
  1615. if ((installationStatusCollection.AllKeys.Contains(WindowsAzureVMManager.MySQLCommunityServerProductID))
  1616. || (installationStatusCollection.AllKeys.Contains(WindowsAzureVMManager.MariaDBProductID)))
  1617. {
  1618. return MySQLCommunityServerInstaller.GetMySQLBasedDBServerIPAddress();
  1619. }
  1620. else
  1621. {
  1622. return null;
  1623. }
  1624. }
  1625. // Start MySQL based database server
  1626. public bool StartMySQLServer()
  1627. {
  1628. // Start MySQL based database server (only if installed)
  1629. if ((installationStatusCollection.AllKeys.Contains(WindowsAzureVMManager.MySQLCommunityServerProductID))
  1630. || (installationStatusCollection.AllKeys.Contains(WindowsAzureVMManager.MariaDBProductID)))
  1631. {
  1632. try
  1633. {
  1634. // Set MySQL based database name
  1635. string installationFolder = null;
  1636. string mySqlBasedDBProductID = null;
  1637. string mysqlInstallInfo = null;
  1638. if (installationStatusCollection.AllKeys.Contains(WindowsAzureVMManager.MySQLCommunityServerProductID))
  1639. {
  1640. mysqlInstallInfo = installationStatusCollection[WindowsAzureVMManager.MySQLCommunityServerProductID];
  1641. mySqlBasedDBProductID = WindowsAzureVMManager.MySQLCommunityServerProductID;
  1642. installationFolder = Path.Combine(applicationsAndRuntimeResourceFolder, WindowsAzureVMManager.MySQLCommunityServerFolder);
  1643. }
  1644. else if (installationStatusCollection.AllKeys.Contains(WindowsAzureVMManager.MariaDBProductID))
  1645. {
  1646. mysqlInstallInfo = installationStatusCollection[WindowsAzureVMManager.MariaDBProductID];
  1647. mySqlBasedDBProductID = WindowsAzureVMManager.MariaDBProductID;
  1648. installationFolder = Path.Combine(applicationsAndRuntimeResourceFolder, WindowsAzureVMManager.MariaDBServerFolder);
  1649. }
  1650. else
  1651. {
  1652. Trace.TraceError("Invalid product id for MySQL based database server.");
  1653. return false;
  1654. }
  1655. // Get MySQL based database name from product id
  1656. XmlReader reader = XmlReader.Create(RoleEnvironment.GetConfigurationSettingValue("ProductListXmlFeed"));
  1657. SyndicationFeed feed = SyndicationFeed.Load(reader);
  1658. var varSqlBasedDBName = from item in feed.Items
  1659. where item.ElementExtensions.ReadElementExtensions<string>("productId", "http://www.w3.org/2005/Atom")[0].Equals(mySqlBasedDBProductID)
  1660. select item.Title.Text.ToString();
  1661. // Start MySQL based DB using installation info
  1662. string iniFileName = mysqlInstallInfo.Split(',')[3];
  1663. MySQLCommunityServerInstaller.StartConfiguredMySQL(installationFolder,
  1664. iniFileName, varSqlBasedDBName.FirstOrDefault().ToString());
  1665. return true;
  1666. }
  1667. catch (Exception ex)
  1668. {
  1669. Trace.TraceError("StartMySQLServer Error: {0}, StackTrace: {1}", ex.Message, ex.StackTrace);
  1670. return false;
  1671. }
  1672. }
  1673. else
  1674. {
  1675. return false;
  1676. }
  1677. }
  1678. // Stop MySQL based database server
  1679. public bool StopMySQLServer()
  1680. {
  1681. if ((installationStatusCollection.AllKeys.Contains(WindowsAzureVMManager.MySQLCommunityServerProductID))
  1682. || (installationStatusCollection.AllKeys.Contains(WindowsAzureVMManager.MariaDBProductID)))
  1683. {
  1684. return MySQLCommunityServerInstaller.StopMySQLBasedDB();
  1685. }
  1686. else
  1687. {
  1688. Trace.TraceError("MySQL based database server is not installed.");
  1689. return false;
  1690. }
  1691. }
  1692. // Restart MySQL based database server
  1693. public bool RestartMySQLServer()
  1694. {
  1695. if ((installationStatusCollection.AllKeys.Contains(WindowsAzureVMManager.MySQLCommunityServerProductID))
  1696. || (installationStatusCollection.AllKeys.Contains(WindowsAzureVMManager.MariaDBProductID)))
  1697. {
  1698. if (MySQLCommunityServerInstaller.StopMySQLBasedDB())
  1699. {
  1700. // Start MySQL server with existing data files
  1701. StartMySQLServer();
  1702. return true;
  1703. }
  1704. else
  1705. {
  1706. Trace.TraceError("Could not stop MySQL based database server.");
  1707. return false;
  1708. }
  1709. }
  1710. else
  1711. {
  1712. Trace.TraceError("MySQL based database server is not installed.");
  1713. return false;
  1714. }
  1715. }
  1716. // Start PHP Web Site
  1717. public bool StartPHPWebSite()
  1718. {
  1719. // Start PHP Website only if PHP runtime is installed
  1720. if (installationStatusCollection.AllKeys.Contains(WindowsAzureVMManager.PHPRuntimeProductID))
  1721. {
  1722. try
  1723. {
  1724. // Re-configure PHP Runtime, as php.ini may contain old Windows Azure Drive letter
  1725. PHPRuntimeInstaller.ReConfigurePHPRuntime(
  1726. Path.Combine(applicationsAndRuntimeResourceFolder,
  1727. WindowsAzureVMManager.RuntimeFolderForPHP));
  1728. StartPHPWebSite(applicationsAndRuntimeResourceFolder);
  1729. // Also setup cron jobs for installed applications (if any)
  1730. SetupCronJobsForInstalledApplications();
  1731. return true;
  1732. }
  1733. catch (Exception ex)
  1734. {
  1735. Trace.TraceError("StartPHPWebSite Error: {0}, StackTrave: {1}", ex.Message, ex.StackTrace);
  1736. return false;
  1737. }
  1738. }
  1739. else
  1740. {
  1741. return false;
  1742. }
  1743. }
  1744. // Stop PHP Web Site
  1745. public bool StopPHPWebSite()
  1746. {
  1747. // Get latest handle to PHP web site
  1748. ServerManager serverManager = new ServerManager();
  1749. Site secondaryWebSite = serverManager.Sites[WindowsAzureVMManager.SecondaryWebSiteName];
  1750. if (secondaryWebSite != null)
  1751. {
  1752. try
  1753. {
  1754. if (secondaryWebSite.State == ObjectState.Started)
  1755. {
  1756. // Stop all cron jobs for installed applications (if any)
  1757. StopAllCronJobs();
  1758. secondaryWebSite.Stop();
  1759. return true;
  1760. }
  1761. else
  1762. {
  1763. Trace.TraceError("PHP Website already stopped.");
  1764. return false;
  1765. }
  1766. }
  1767. catch (Exception ex)
  1768. {
  1769. Trace.TraceError("StopPHPWebSite Error: {0}, StackTrace: {1}", ex.Message, ex.StackTrace);
  1770. return false;
  1771. }
  1772. }
  1773. else
  1774. {
  1775. Trace.TraceError("PHP Web site not yet created.");
  1776. return false;
  1777. }
  1778. }
  1779. // Restart PHP Web Site
  1780. public bool RestartPHPWebSite()
  1781. {
  1782. // Get latest handle to PHP web site
  1783. ServerManager serverManager = new ServerManager();
  1784. Site secondaryWebSite = serverManager.Sites[WindowsAzureVMManager.SecondaryWebSiteName];
  1785. if (secondaryWebSite != null)
  1786. {
  1787. try
  1788. {
  1789. if (secondaryWebSite.State == ObjectState.Started)
  1790. {
  1791. // Stop PHP Web Site
  1792. StopPHPWebSite();
  1793. // Restart PHP Web Site
  1794. StartPHPWebSite();
  1795. Trace.TraceInformation("PHP Web site restarted.");
  1796. return true;
  1797. }
  1798. else
  1799. {
  1800. Trace.TraceError("PHP Website was not started.");
  1801. return false;
  1802. }
  1803. }
  1804. catch (Exception ex)
  1805. {
  1806. Trace.TraceError("RestartPHPWebSite Error: {0}, StackTrace: {1}", ex.Message, ex.StackTrace);
  1807. return false;
  1808. }
  1809. }
  1810. else
  1811. {
  1812. return false;
  1813. }
  1814. }
  1815. // Delete PHP Web Site
  1816. public bool DeletePHPWebSite()
  1817. {
  1818. // Get latest handle to PHP web site
  1819. ServerManager serverManager = new ServerManager();
  1820. Site secondaryWebSite = serverManager.Sites[WindowsAzureVMManager.SecondaryWebSiteName];
  1821. if (secondaryWebSite != null)
  1822. {
  1823. try
  1824. {
  1825. if (secondaryWebSite.State == ObjectState.Started)
  1826. {
  1827. // Stop all cron jobs for installed applications (if any)
  1828. StopAllCronJobs();
  1829. secondaryWebSite.Stop();
  1830. }
  1831. // Kill all php-cgi.exe processes, if running
  1832. WindowsAzureVMManager.FindAndKillProcess("php-cgi");
  1833. // Clear FastCGI handlers
  1834. Configuration config = serverManager.GetApplicationHostConfiguration();
  1835. ConfigurationSection fastCgiSection = config.GetSection("system.webServer/fastCgi");
  1836. ConfigurationElementCollection fastCgiCollection = fastCgiSection.GetCollection();
  1837. fastCgiCollection.Clear();
  1838. serverManager.Sites.Remove(secondaryWebSite);
  1839. serverManager.CommitChanges();
  1840. // Put some delay for IIS
  1841. Thread.Sleep(5000);
  1842. secondaryWebSite = null;
  1843. return true;
  1844. }
  1845. catch (Exception ex)
  1846. {
  1847. Trace.TraceError("DeletePHPWebSite Error: {0}, StackTrace: {1}", ex.Message, ex.StackTrace);
  1848. return false;
  1849. }
  1850. }
  1851. else
  1852. {
  1853. Trace.TraceError("PHP Web site not yet created.");
  1854. return false;
  1855. }
  1856. }
  1857. // Get application root folder
  1858. public string GetApplicationsFolder()
  1859. {
  1860. return Path.Combine(applicationsAndRuntimeResourceFolder, WindowsAzureVMManager.ApplicationsFolder);
  1861. }
  1862. // Get php.ini file name
  1863. public string GetPHPIniFileName()
  1864. {
  1865. // Start PHP Website only if PHP runtime is installed
  1866. if (installationStatusCollection.AllKeys.Contains(WindowsAzureVMManager.PHPRuntimeProductID))
  1867. {
  1868. string phpIniFileName =
  1869. Path.Combine(
  1870. Path.Combine(applicationsAndRuntimeResourceFolder, WindowsAzureVMManager.RuntimeFolderForPHP),
  1871. "php.ini");
  1872. if (File.Exists(phpIniFileName))
  1873. {
  1874. return phpIniFileName;
  1875. }
  1876. else
  1877. {
  1878. return null;
  1879. }
  1880. }
  1881. else
  1882. {
  1883. return null;
  1884. }
  1885. }
  1886. // Get php.exe file name
  1887. public string GetPHPExeFileName()
  1888. {
  1889. // Start PHP Website only if PHP runtime is installed
  1890. if (installationStatusCollection.AllKeys.Contains(WindowsAzureVMManager.PHPRuntimeProductID))
  1891. {
  1892. string phpExeFileName =
  1893. Path.Combine(
  1894. Path.Combine(applicationsAndRuntimeResourceFolder, WindowsAzureVMManager.RuntimeFolderForPHP),
  1895. "php.exe");
  1896. if (File.Exists(phpExeFileName))
  1897. {
  1898. return phpExeFileName;
  1899. }
  1900. else
  1901. {
  1902. return null;
  1903. }
  1904. }
  1905. else
  1906. {
  1907. return null;
  1908. }
  1909. }
  1910. // Get php log file name
  1911. public string GetPHPLogFileName()
  1912. {
  1913. // Start PHP Website only if PHP runtime is installed
  1914. if (installationStatusCollection.AllKeys.Contains(WindowsAzureVMManager.PHPRuntimeProductID))
  1915. {
  1916. // Set log folder and file name
  1917. string logFileName = Path.Combine(
  1918. Path.Combine(
  1919. Path.Combine(applicationsAndRuntimeResourceFolder,
  1920. WindowsAzureVMManager.RuntimeFolderForPHP),
  1921. "logs"),
  1922. "log.txt");
  1923. if (File.Exists(logFileName))
  1924. {
  1925. return logFileName;
  1926. }
  1927. else
  1928. {
  1929. return null;
  1930. }
  1931. }
  1932. else
  1933. {
  1934. return null;
  1935. }
  1936. }
  1937. // Set php.ini content
  1938. public bool SetPHPIniContent(string content)
  1939. {
  1940. if (installationStatusCollection.AllKeys.Contains(WindowsAzureVMManager.PHPRuntimeProductID))
  1941. {
  1942. // Get php.ini file name
  1943. string phpIniFileName = Path.Combine(
  1944. Path.Combine(applicationsAndRuntimeResourceFolder, WindowsAzureVMManager.RuntimeFolderForPHP),
  1945. "php.ini");
  1946. if (File.Exists(phpIniFileName))
  1947. {
  1948. // Update php.ini file
  1949. StreamWriter streamWriter = File.CreateText(phpIniFileName);
  1950. streamWriter.Write(content);
  1951. streamWriter.Close();
  1952. return true;
  1953. }
  1954. else
  1955. {
  1956. return false;
  1957. }
  1958. }
  1959. else
  1960. {
  1961. return false;
  1962. }
  1963. }
  1964. // Start PHP Web Site within specified resource
  1965. /// <summary>
  1966. /// Starts the PHP web site.
  1967. /// </summary>
  1968. /// <param name="resourceFolder">The resource folder.</param>
  1969. private void StartPHPWebSite(string resourceFolder)
  1970. {
  1971. try
  1972. {
  1973. IPEndPoint httpEndpoint = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["HttpIn"].IPEndpoint;
  1974. string phpFullPath = Path.Combine(resourceFolder, WindowsAzureVMManager.RuntimeFolderForPHP + @"\php-cgi.exe");
  1975. string phpArguments = "-c " + Path.Combine(resourceFolder, WindowsAzureVMManager.RuntimeFolderForPHP + @"\php.ini");
  1976. // Get latest handle to PHP web site
  1977. ServerManager serverManager = new ServerManager();
  1978. Site secondaryWebSite = serverManager.Sites[WindowsAzureVMManager.SecondaryWebSiteName];
  1979. // If available, get SSL certificate from service configuration
  1980. string sslCertificateSHA1Thumbprint = null;
  1981. try
  1982. {
  1983. sslCertificateSHA1Thumbprint = RoleEnvironment.GetConfigurationSettingValue("SSLCertificateSHA1Thumbprint");
  1984. }
  1985. catch (Exception)
  1986. {
  1987. // Ignore, it means SSLCertificateSHA1Thumbprint is not defined
  1988. }
  1989. // Get main web site application pool name
  1990. string applicationPoolName = serverManager.Sites.First().Applications.First().ApplicationPoolName;
  1991. if (secondaryWebSite == null)
  1992. {
  1993. Configuration config = serverManager.GetApplicationHostConfiguration();
  1994. ConfigurationSection fastCgiSection = config.GetSection("system.webServer/fastCgi");
  1995. ConfigurationElementCollection fastCgiCollection = fastCgiSection.GetCollection();
  1996. try
  1997. {
  1998. ConfigurationElement applicationElement = fastCgiCollection.CreateElement("application");
  1999. applicationElement["fullPath"] = phpFullPath;
  2000. applicationElement["arguments"] = phpArguments;
  2001. applicationElement["maxInstances"] = 12;
  2002. applicationElement["idleTimeout"] = 1800;
  2003. applicationElement["activityTimeout"] = 1800;
  2004. applicationElement["requestTimeout"] = 1800;
  2005. applicationElement["instanceMaxRequests"] = 10000;
  2006. applicationElement["protocol"] = @"NamedPipe";
  2007. applicationElement["flushNamedPipe"] = false;
  2008. ConfigurationElementCollection environmentVariablesCollection = applicationElement.GetCollection("environmentVariables");
  2009. // Add php to the PATH environment variable
  2010. ConfigurationElement environmentVariableElement = environmentVariablesCollection.CreateElement("environmentVariable");
  2011. environmentVariableElement["name"] = @"PATH";
  2012. string path = Environment.GetEnvironmentVariable(@"PATH");
  2013. environmentVariableElement["value"] = path + ";" + Path.Combine(resourceFolder, WindowsAzureVMManager.RuntimeFolderForPHP);
  2014. environmentVariablesCollection.Add(environmentVariableElement);
  2015. // Set PHP_FCGI_MAX_REQUESTS envirionement variable
  2016. environmentVariableElement = environmentVariablesCollection.CreateElement("environmentVariable");
  2017. environmentVariableElement["name"] = @"PHP_FCGI_MAX_REQUESTS";
  2018. environmentVariableElement["value"] = @"10000";
  2019. environmentVariablesCollection.Add(environmentVariableElement);
  2020. fastCgiCollection.Add(applicationElement);
  2021. }
  2022. catch (Exception ex)
  2023. {
  2024. Trace.TraceWarning("Ignored error: {0}", ex.Message);
  2025. }
  2026. // Prepare web.config for PHP Web Site
  2027. var parameters = new NameValueCollection();
  2028. if (!resourceFolder.EndsWith(@"\"))
  2029. {
  2030. resourceFolder = resourceFolder + @"\";
  2031. }
  2032. parameters["resourceFolder"] = resourceFolder;
  2033. string approot = Environment.GetEnvironmentVariable("RoleRoot") + @"\approot";
  2034. string webConfigFileName = Path.Combine(
  2035. Path.Combine(resourceFolder, WindowsAzureVMManager.ApplicationsFolder),
  2036. "web.config");
  2037. if (File.Exists(webConfigFileName))
  2038. File.Delete(webConfigFileName);
  2039. FileUtils.WriteConfigFile(
  2040. parameters,
  2041. Path.Combine(approot, @"bin\ResourcesForPHP\InnerWeb.config"),
  2042. webConfigFileName);
  2043. // Add phpinfo.php file at the root of web site
  2044. string phpinfoFileName = Path.Combine(Path.Combine(resourceFolder, WindowsAzureVMManager.ApplicationsFolder),
  2045. "phpinfo.php");
  2046. if (!File.Exists(phpinfoFileName))
  2047. {
  2048. File.Copy(Path.Combine(approot, @"bin\ResourcesForPHP\phpinfo.php"),
  2049. phpinfoFileName);
  2050. }
  2051. // Create new PHP Web Site
  2052. secondaryWebSite = serverManager.Sites.Add(WindowsAzureVMManager.SecondaryWebSiteName,
  2053. "http",
  2054. httpEndpoint.Address.ToString() + ":" + httpEndpoint.Port.ToString() + ":",
  2055. Path.Combine(applicationsAndRuntimeResourceFolder, WindowsAzureVMManager.ApplicationsFolder));
  2056. if (!string.IsNullOrEmpty(sslCertificateSHA1Thumbprint))
  2057. {
  2058. EnableSSLForWebSite(secondaryWebSite, "HttpsIn", sslCertificateSHA1Thumbprint);
  2059. }
  2060. // Set PHP web site application pool name
  2061. secondaryWebSite.Applications.First().ApplicationPoolName = applicationPoolName;
  2062. // Set each installed product as application in IIS
  2063. IDictionary<string, string> productsInstalled = GetInstalledProductsInfo();
  2064. foreach (string productId in productsInstalled.Keys)
  2065. {
  2066. string valueInstalledProduct = productsInstalled[productId];
  2067. string[] productInstalledInfo = valueInstalledProduct.Split(',');
  2068. string installPath = productInstalledInfo[1].Trim();
  2069. if (!installPath.Equals("/"))
  2070. {
  2071. string applicationPath = Path.Combine(
  2072. Path.Combine(applicationsAndRuntimeResourceFolder, WindowsAzureVMManager.ApplicationsFolder),
  2073. installPath.Replace("/", "\\").Trim('\\'));
  2074. Application app = secondaryWebSite.Applications.Add(installPath, applicationPath);
  2075. if (app != null)
  2076. {
  2077. // Set application pool name as that of main web site
  2078. app.ApplicationPoolName = applicationPoolName;
  2079. }
  2080. }
  2081. }
  2082. }
  2083. serverManager.CommitChanges();
  2084. // Put some delay for IIS
  2085. Thread.Sleep(5000);
  2086. serverManager.ApplicationPools[applicationPoolName].Recycle();
  2087. // Start the new PHP Web Site
  2088. secondaryWebSite.Start();
  2089. // Put some delay for IIS
  2090. Thread.Sleep(5000);
  2091. if (!string.IsNullOrEmpty(sslCertificateSHA1Thumbprint))
  2092. {
  2093. IPEndPoint httpsEndpoint = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["HttpsIn"].IPEndpoint;
  2094. Trace.TraceInformation("Started PHP Web Site in IIS on http port {0} and https port {1}",
  2095. httpEndpoint.Port.ToString(),
  2096. httpsEndpoint.Port.ToString());
  2097. }
  2098. else
  2099. {
  2100. Trace.TraceInformation("Started PHP Web Site in IIS on http port {0}",
  2101. httpEndpoint.Port.ToString());
  2102. }
  2103. }
  2104. catch (Exception ex)
  2105. {
  2106. // Logging the exceptiom
  2107. Trace.TraceError(ex.Message);
  2108. }
  2109. }
  2110. // Output data received handler for invoked processes
  2111. static void OutputDataReceivedHandler(object sender, System.Diagnostics.DataReceivedEventArgs e)
  2112. {
  2113. Trace.TraceInformation(e.Data);
  2114. }
  2115. // Download and extract web archive
  2116. // TODO: Currently this can only handle .zip and self extracting .exe zip files
  2117. // Handle other archives like .tar, .gz, .bz, .tar.gz or .tgz, .tar.bz or .tbz etc
  2118. public static void DownloadAndExtractWebArchive(string downloadUrl,
  2119. string downloadFileName,
  2120. string downloadFolder,
  2121. string extractFolder,
  2122. string applicationPath)
  2123. {
  2124. // Get underlying VM Manager
  2125. IVMManager vmManager = WindowsAzureVMManager.GetVMManager();
  2126. if (string.IsNullOrEmpty(downloadUrl))
  2127. {
  2128. Trace.TraceError("Invalid download URL");
  2129. throw new InstallException("Invalid download URL");
  2130. }
  2131. try
  2132. {
  2133. // Get file extension
  2134. string extension = null;
  2135. if (downloadUrl.ToLower().EndsWith(".zip"))
  2136. {
  2137. extension = "zip";
  2138. }
  2139. else if (downloadUrl.ToLower().EndsWith(".exe"))
  2140. {
  2141. extension = "exe";
  2142. }
  2143. else if (downloadUrl.ToLower().EndsWith(".tar") ||
  2144. downloadUrl.ToLower().EndsWith(".tgz") ||
  2145. downloadUrl.ToLower().EndsWith(".gz") ||
  2146. downloadUrl.ToLower().EndsWith(".gzip") ||
  2147. downloadUrl.ToLower().EndsWith(".tbz") ||
  2148. downloadUrl.ToLower().EndsWith(".tbz2") ||
  2149. downloadUrl.ToLower().EndsWith(".bz2") ||
  2150. downloadUrl.ToLower().EndsWith(".bzip2"))
  2151. {
  2152. extension = "tar";
  2153. }
  2154. else if (downloadFileName != null)
  2155. {
  2156. // Unknown extension in download url, try to use downloadFileName
  2157. if (downloadFileName.ToLower().EndsWith(".zip"))
  2158. {
  2159. extension = "zip";
  2160. }
  2161. else if (downloadFileName.ToLower().EndsWith(".exe"))
  2162. {
  2163. extension = "exe";
  2164. }
  2165. else
  2166. {
  2167. Trace.TraceError("Unknown file extension: {0}", downloadFileName);
  2168. throw new InstallException(string.Format("Unknown file extension: {0}", downloadFileName));
  2169. }
  2170. }
  2171. else
  2172. {
  2173. Trace.TraceError("Unknown file extension: {0}", downloadUrl);
  2174. throw new InstallException(string.Format("Unknown file extension: {0}", downloadUrl));
  2175. }
  2176. // Download file with name as exe, as this extension will work for zip and exe files both
  2177. vmManager.UpdateProgressInformation("Downloading " + downloadUrl + "...", false);
  2178. if (extension.Equals("tar"))
  2179. {
  2180. string tarFileName = Path.Combine(downloadFolder, "downloadedfile.tar");
  2181. WebClient client = new WebClient();
  2182. client.DownloadFile(downloadUrl, tarFileName);
  2183. vmManager.UpdateProgressInformation(downloadUrl + " download completed.", false);
  2184. }
  2185. if (extension.Equals("zip") || extension.Equals("tar"))
  2186. {
  2187. string archiveFileName = Path.Combine(downloadFolder, "downloadedfile." + extension);
  2188. WebClient client = new WebClient();
  2189. client.DownloadFile(downloadUrl, archiveFileName);
  2190. vmManager.UpdateProgressInformation(downloadUrl + " download completed.", false);
  2191. string processFileName = string.Empty;
  2192. if (extension.Equals("zip"))
  2193. {
  2194. processFileName = Path.Combine(
  2195. Environment.GetEnvironmentVariable("SystemRoot"),
  2196. @"System32\cscript.exe");
  2197. }
  2198. else
  2199. {
  2200. processFileName = GetVMManager().GetPHPExeFileName();
  2201. }
  2202. string processArguments = null;
  2203. if (string.IsNullOrEmpty(applicationPath))
  2204. {
  2205. if (extension.Equals("zip"))
  2206. {
  2207. processArguments = @"/B /Nologo "
  2208. + "\""
  2209. + Environment.GetEnvironmentVariable("RoleRoot") + @"\approot\bin\" + WindowsAzureVMManager.ApplicationsUnzipUtility
  2210. + "\" "
  2211. + archiveFileName + " " + extractFolder;
  2212. }
  2213. else
  2214. {
  2215. processArguments = "\""
  2216. + Environment.GetEnvironmentVariable("RoleRoot") + @"\approot\bin\" + WindowsAzureVMManager.ApplicationsUntarUtility
  2217. + "\" "
  2218. + archiveFileName + " " + extractFolder;
  2219. }
  2220. }
  2221. else
  2222. {
  2223. if (extension.Equals("zip"))
  2224. {
  2225. processArguments = @"/B /Nologo "
  2226. + "\""
  2227. + Environment.GetEnvironmentVariable("RoleRoot") + @"\approot\bin\" + WindowsAzureVMManager.ApplicationsUnzipUtility
  2228. + "\" "
  2229. + archiveFileName + " " + extractFolder + " " + applicationPath;
  2230. }
  2231. else
  2232. {
  2233. processArguments = "\""
  2234. + Environment.GetEnvironmentVariable("RoleRoot") + @"\approot\bin\" + WindowsAzureVMManager.ApplicationsUntarUtility
  2235. + "\" "
  2236. + archiveFileName + " " + extractFolder + " " + applicationPath;
  2237. }
  2238. }
  2239. ExtractFilesInArchive(downloadUrl, processFileName, processArguments);
  2240. // Delete downloaded file
  2241. File.Delete(archiveFileName);
  2242. vmManager.UpdateProgressInformation("downloaded file deleted.", false);
  2243. }
  2244. else if (extension.Equals("exe"))
  2245. {
  2246. WebClient client = new WebClient();
  2247. client.DownloadFile(downloadUrl, downloadFolder + @"downloadedfile.exe");
  2248. vmManager.UpdateProgressInformation(downloadUrl + " download completed.", false);
  2249. // Launch self extracting MSI
  2250. Process process = new Process();
  2251. process.StartInfo.UseShellExecute = false;
  2252. process.StartInfo.FileName = downloadFolder + @"\downloadedfile.exe";
  2253. process.StartInfo.Arguments = "/Q /T:" + extractFolder;
  2254. process.Start();
  2255. // Wait until process is over
  2256. process.WaitForExit();
  2257. vmManager.UpdateProgressInformation(downloadUrl + " exe file extracted.", false);
  2258. // Delete downloaded file
  2259. File.Delete(downloadFolder + @"\downloadedfile.exe");
  2260. }
  2261. else
  2262. {
  2263. Trace.TraceError("Unknown file extension: {0}", downloadUrl);
  2264. throw new InstallException(string.Format("Unknown file extension: {0}", downloadUrl));
  2265. }
  2266. }
  2267. catch (InstallException ex)
  2268. {
  2269. // Rethrow InstallException
  2270. throw ex;
  2271. }
  2272. catch (Exception ex)
  2273. {
  2274. Trace.TraceError("Error in downloading file {0}, Error: {1}", downloadUrl, ex.Message);
  2275. throw new InstallException(string.Format("Error in downloading file {0}, Error: {1}", downloadUrl, ex.Message));
  2276. }
  2277. }
  2278. // Extract archive
  2279. private static void ExtractFilesInArchive(string downloadUrl,
  2280. string processFileName,
  2281. string processArguments)
  2282. {
  2283. Process extractProcess = new Process();
  2284. extractProcess.StartInfo.UseShellExecute = false;
  2285. extractProcess.StartInfo.RedirectStandardInput = true;
  2286. extractProcess.StartInfo.RedirectStandardOutput = true;
  2287. // Output data received handler for cscript process
  2288. extractProcess.OutputDataReceived += new DataReceivedEventHandler(OutputDataReceivedHandler);
  2289. // setting the file name and arguments
  2290. extractProcess.StartInfo.FileName = processFileName;
  2291. if (!File.Exists(extractProcess.StartInfo.FileName))
  2292. {
  2293. // Process to be invoked not found
  2294. Trace.TraceError("File {0} not found.", extractProcess.StartInfo.FileName);
  2295. throw new InstallException(string.Format("File {0} not found.", extractProcess.StartInfo.FileName));
  2296. }
  2297. extractProcess.StartInfo.Arguments = processArguments;
  2298. try
  2299. {
  2300. // Get underlying VM Manager
  2301. IVMManager vmManager = WindowsAzureVMManager.GetVMManager();
  2302. vmManager.UpdateProgressInformation("Starting process: "
  2303. + extractProcess.StartInfo.FileName + " "
  2304. + extractProcess.StartInfo.Arguments, false);
  2305. extractProcess.Start();
  2306. // Start the asynchronous read of the output stream.
  2307. extractProcess.BeginOutputReadLine();
  2308. // Start extract process, wait for 30 minutes max
  2309. vmManager.UpdateProgressInformation("Waiting (30 minutes max) to exit process "
  2310. + extractProcess.StartInfo.FileName, false);
  2311. extractProcess.WaitForExit(30 * 60 * 1000);
  2312. if (extractProcess.HasExited)
  2313. {
  2314. if (extractProcess.ExitCode != 0)
  2315. {
  2316. // Unzip failed
  2317. Trace.TraceError("Failed to extract downloadUrl {0}", downloadUrl);
  2318. throw new InstallException(string.Format("Failed to extract downloadUrl {0}", downloadUrl));
  2319. }
  2320. else
  2321. {
  2322. vmManager.UpdateProgressInformation(downloadUrl + " archive extracted.", false);
  2323. }
  2324. }
  2325. else
  2326. {
  2327. // Extract process not completed in time
  2328. extractProcess.Kill();
  2329. Trace.TraceError("Killed extract process as it has not completed in time. Failed to extract downloadUrl {0}", downloadUrl);
  2330. throw new InstallException(string.Format("Killed extract process as it has not completed in time. Failed to extract downloadUrl {0}", downloadUrl));
  2331. }
  2332. extractProcess.Close();
  2333. }
  2334. catch (InstallException ex)
  2335. {
  2336. // Rethrow InstallException
  2337. throw ex;
  2338. }
  2339. catch (Exception ex)
  2340. {
  2341. // file extract failed
  2342. Trace.TraceError("Failed to extract downloadUrl {0}, Error: {1}", downloadUrl, ex.Message);
  2343. throw new InstallException(string.Format("Failed to extract downloadUrl {0}, Error: {1}", downloadUrl, ex.Message));
  2344. }
  2345. }
  2346. // Get VMManager WCF service binding
  2347. public static BasicHttpBinding GetVMManagerServiceBinding(string sslCertificateSHA1Thumbprint)
  2348. {
  2349. BasicHttpBinding binding = null;
  2350. if (string.IsNullOrEmpty(sslCertificateSHA1Thumbprint))
  2351. {
  2352. binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly);
  2353. }
  2354. else
  2355. {
  2356. binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
  2357. }
  2358. binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
  2359. binding.SendTimeout = TimeSpan.FromMinutes(10);
  2360. binding.ReceiveTimeout = TimeSpan.FromMinutes(10);
  2361. binding.OpenTimeout = TimeSpan.FromMinutes(10);
  2362. binding.CloseTimeout = TimeSpan.FromMinutes(10);
  2363. binding.MaxReceivedMessageSize = 256 * 1024;
  2364. binding.ReaderQuotas.MaxStringContentLength = 64 * 1024;
  2365. return binding;
  2366. }
  2367. // Get VMManager WCF service endpoint address
  2368. public static string GetVMManagerServiceEndpointAddress(string sslCertificateSHA1Thumbprint)
  2369. {
  2370. if (string.IsNullOrEmpty(sslCertificateSHA1Thumbprint))
  2371. {
  2372. return String.Format("http://{0}/WindowsAzureVMManager",
  2373. RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["VMManagerServiceExternalHttpPort"].IPEndpoint);
  2374. }
  2375. else
  2376. {
  2377. return String.Format("https://{0}/WindowsAzureVMManager",
  2378. RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["VMManagerServiceExternalHttpsPort"].IPEndpoint);
  2379. }
  2380. }
  2381. // Get VMManager WCF service endpoint
  2382. public static IPEndPoint GetVMManagerServiceEndpoint(string sslCertificateSHA1Thumbprint)
  2383. {
  2384. if (string.IsNullOrEmpty(sslCertificateSHA1Thumbprint))
  2385. {
  2386. return RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["VMManagerServiceExternalHttpPort"].IPEndpoint;
  2387. }
  2388. else
  2389. {
  2390. return RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["VMManagerServiceExternalHttpsPort"].IPEndpoint;
  2391. }
  2392. }
  2393. // Get singleton instance of the service
  2394. public static IVMManager GetVMManager()
  2395. {
  2396. // If available, get SSL certificate from service configuration that is needed for WCF service
  2397. string sslCertificateSHA1Thumbprint = null;
  2398. try
  2399. {
  2400. sslCertificateSHA1Thumbprint = RoleEnvironment.GetConfigurationSettingValue("SSLCertificateSHA1Thumbprint");
  2401. }
  2402. catch (Exception)
  2403. {
  2404. // Ignore, it means SSLCertificateSHA1Thumbprint is not defined
  2405. }
  2406. // Get WCF service binding
  2407. BasicHttpBinding binding = WindowsAzureVMManager.GetVMManagerServiceBinding(sslCertificateSHA1Thumbprint);
  2408. ChannelFactory<IVMManager> cfactory = new ChannelFactory<IVMManager>(binding,
  2409. WindowsAzureVMManager.GetVMManagerServiceEndpointAddress(sslCertificateSHA1Thumbprint));
  2410. ClientCredentials loginCredentials = new ClientCredentials();
  2411. loginCredentials.UserName.UserName = RoleEnvironment.GetConfigurationSettingValue("AdminUserName");
  2412. loginCredentials.UserName.Password = RoleEnvironment.GetConfigurationSettingValue("AdminPassword");
  2413. var defaultCredentials = cfactory.Endpoint.Behaviors.Find<ClientCredentials>();
  2414. cfactory.Endpoint.Behaviors.Remove(defaultCredentials); //remove default ones
  2415. cfactory.Endpoint.Behaviors.Add(loginCredentials); //add required ones
  2416. IVMManager vmManager = cfactory.CreateChannel();
  2417. if (!string.IsNullOrEmpty(sslCertificateSHA1Thumbprint))
  2418. {
  2419. // Trust any certificate
  2420. ServicePointManager.ServerCertificateValidationCallback
  2421. += RemoteCertificateValidate;
  2422. System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Ssl3;
  2423. }
  2424. return vmManager;
  2425. }
  2426. // Remotes the certificate validate.
  2427. private static bool RemoteCertificateValidate(
  2428. object sender, X509Certificate cert,
  2429. X509Chain chain, SslPolicyErrors error)
  2430. {
  2431. // trust any certificate!!!
  2432. return true;
  2433. }
  2434. // Create Storage Account using account information in service configuration file
  2435. public static CloudStorageAccount GetStorageAccount(bool useHttps)
  2436. {
  2437. CloudStorageAccount storageAccount = null;
  2438. if (RoleEnvironment.DeploymentId.StartsWith("deployment"))
  2439. {
  2440. storageAccount = CloudStorageAccount.DevelopmentStorageAccount;
  2441. }
  2442. else
  2443. {
  2444. // Create Storage Credetials and account
  2445. StorageCredentialsAccountAndKey oStorageCredentialsAccountAndKey
  2446. = new StorageCredentialsAccountAndKey(
  2447. RoleEnvironment.GetConfigurationSettingValue("WindowsAzureStorageAccountName"),
  2448. RoleEnvironment.GetConfigurationSettingValue("WindowsAzureStorageAccountKey")
  2449. );
  2450. // Create HTTPS storage endpoint
  2451. storageAccount = CloudStorageAccount.Parse(RoleEnvironment.GetConfigurationSettingValue("AcceleratorConnectionString").Replace("https", "http"));
  2452. }
  2453. return storageAccount;
  2454. }
  2455. // Check if blob exists
  2456. private static bool BlobExists(CloudBlob blob)
  2457. {
  2458. try
  2459. {
  2460. blob.FetchAttributes();
  2461. return true;
  2462. }
  2463. catch (StorageClientException e)
  2464. {
  2465. if (e.ErrorCode == StorageErrorCode.ResourceNotFound)
  2466. {
  2467. return false;
  2468. }
  2469. else
  2470. {
  2471. throw;
  2472. }
  2473. }
  2474. }
  2475. // Serialize List To Blob
  2476. private static void SerializeListToBlob(List<string> collection, CloudBlob blob)
  2477. {
  2478. try
  2479. {
  2480. SoapFormatter ser = new SoapFormatter();
  2481. using (MemoryStream memoryStream = new MemoryStream())
  2482. {
  2483. ser.Serialize(memoryStream, collection);
  2484. byte[] b = memoryStream.GetBuffer();
  2485. string serilizedNameValueCollection = Encoding.Default.GetString(b);
  2486. blob.UploadText(serilizedNameValueCollection);
  2487. }
  2488. }
  2489. catch (Exception ex)
  2490. {
  2491. Trace.TraceError("Failed to SerializeNameValueCollectionToBlob. Error: {0}", ex.Message);
  2492. }
  2493. }
  2494. // Serialize NameValueCollection To Blob
  2495. private static void SerializeNameValueCollectionToBlob(NameValueCollection collection, CloudBlob blob)
  2496. {
  2497. try
  2498. {
  2499. SoapFormatter ser = new SoapFormatter();
  2500. using (MemoryStream memoryStream = new MemoryStream())
  2501. {
  2502. ser.Serialize(memoryStream, collection);
  2503. byte[] b = memoryStream.GetBuffer();
  2504. string serilizedNameValueCollection = Encoding.Default.GetString(b);
  2505. blob.UploadText(serilizedNameValueCollection);
  2506. }
  2507. }
  2508. catch (Exception ex)
  2509. {
  2510. Trace.TraceError("Failed to SerializeNameValueCollectionToBlob. Error: {0}", ex.Message);
  2511. }
  2512. }
  2513. // Serialize NameValueCollection To string
  2514. private static string SerializeNameValueCollectionToString(NameValueCollection collection)
  2515. {
  2516. string serilizedNameValueCollection = null;
  2517. try
  2518. {
  2519. SoapFormatter ser = new SoapFormatter();
  2520. using (MemoryStream memoryStream = new MemoryStream())
  2521. {
  2522. ser.Serialize(memoryStream, collection);
  2523. byte[] b = memoryStream.GetBuffer();
  2524. serilizedNameValueCollection = Encoding.Default.GetString(b);
  2525. }
  2526. }
  2527. catch (Exception ex)
  2528. {
  2529. Trace.TraceError("Failed to SerializeNameValueCollectionToString. Error: {0}", ex.Message);
  2530. }
  2531. return serilizedNameValueCollection;
  2532. }
  2533. // Deserialize NameValueCollection from Blob
  2534. private static NameValueCollection DeserializeNameValueCollectionFromBlob(CloudBlob blob)
  2535. {
  2536. NameValueCollection collection = null;
  2537. try
  2538. {
  2539. using (MemoryStream memoryStream = new MemoryStream())
  2540. {
  2541. SoapFormatter ser = new SoapFormatter();
  2542. blob.DownloadToStream(memoryStream);
  2543. memoryStream.Seek(0, SeekOrigin.Begin);
  2544. collection = ser.Deserialize(memoryStream) as NameValueCollection;
  2545. }
  2546. }
  2547. catch (Exception ex)
  2548. {
  2549. Trace.TraceError("Failed to DeserializeNameValueCollectionFromBlob. Error: {0}", ex.Message);
  2550. }
  2551. return collection;
  2552. }
  2553. // Deserialize NameValueCollection from String
  2554. private static NameValueCollection DeserializeNameValueCollectionFromString(string serilizedNameValueCollection)
  2555. {
  2556. NameValueCollection collection = null;
  2557. using (MemoryStream memoryStream = new MemoryStream())
  2558. {
  2559. StreamWriter writer = new StreamWriter(memoryStream);
  2560. writer.Write(serilizedNameValueCollection);
  2561. writer.Flush();
  2562. memoryStream.Seek(0, SeekOrigin.Begin);
  2563. SoapFormatter ser = new SoapFormatter();
  2564. collection = ser.Deserialize(memoryStream) as NameValueCollection;
  2565. }
  2566. return collection;
  2567. }
  2568. // Get Installed Products Info
  2569. public IDictionary<string, string> GetInstalledProductsInfo()
  2570. {
  2571. string blobName = RoleEnvironment.GetConfigurationSettingValue("InstallationStatusConfigFileBlob");
  2572. CloudBlob blob = GetCloudBlobReference(blobName);
  2573. NameValueCollection installedProductsInfo = DeserializeNameValueCollectionFromBlob(blob);
  2574. // Transform NameValueCollection to Dictionary.
  2575. // TODO: Need to get rid of all this dirty work
  2576. Dictionary<string, string> dicInstalledProductsInfo = new Dictionary<string, string>();
  2577. foreach (string key in installedProductsInfo.AllKeys)
  2578. {
  2579. dicInstalledProductsInfo.Add(key, installedProductsInfo[key]);
  2580. }
  2581. return dicInstalledProductsInfo;
  2582. }
  2583. // Get cloud blob reference
  2584. private static CloudBlob GetCloudBlobReference(string blobName)
  2585. {
  2586. // Create HTTPS storage endpoint
  2587. CloudStorageAccount storageAccount = WindowsAzureVMManager.GetStorageAccount(true);
  2588. // Create blob for installation status information
  2589. string containerName = RoleEnvironment.GetConfigurationSettingValue("PHPApplicationsBackupContainerName");
  2590. CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
  2591. CloudBlobContainer container = blobClient.GetContainerReference(containerName);
  2592. if (container.CreateIfNotExist())
  2593. {
  2594. BlobContainerPermissions containerPermissions = new BlobContainerPermissions();
  2595. containerPermissions.PublicAccess = BlobContainerPublicAccessType.Container;
  2596. container.SetPermissions(containerPermissions);
  2597. }
  2598. return container.GetBlobReference(blobName);
  2599. }
  2600. // Kill processes starting with specified name. Needed to kill php-cgi.exe
  2601. private static void FindAndKillProcess(string name)
  2602. {
  2603. // Get a list of all running processes on the computer
  2604. foreach (Process process in Process.GetProcesses())
  2605. {
  2606. // Kill required processes
  2607. if (process.ProcessName.StartsWith(name))
  2608. {
  2609. Trace.TraceWarning("Killed process {0} with id={1}", name, process.Id);
  2610. try
  2611. {
  2612. if (process.HasExited == false)
  2613. {
  2614. process.Kill();
  2615. }
  2616. }
  2617. catch (Exception ex)
  2618. {
  2619. Trace.TraceError("Unable to kill process. Error: {0}", ex.Message);
  2620. }
  2621. }
  2622. }
  2623. }
  2624. }
  2625. }