PageRenderTime 62ms CodeModel.GetById 23ms RepoModel.GetById 1ms 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

Large files files are truncated, but you can click here to view the full file

  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…

Large files files are truncated, but you can click here to view the full file