PageRenderTime 45ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/Resources/Companion/AdminRole/Controllers/BaseProductsController.cs

https://bitbucket.org/zgramana/azure-accelerators-project
C# | 588 lines | 465 code | 85 blank | 38 comment | 49 complexity | a31f96be9e68d0fe34b64d48b5e8fceb MD5 | raw file
Possible License(s): LGPL-2.0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Web;
  5. using System.Web.Mvc;
  6. using WindowsAzureCompanion.VMManagerService;
  7. using Microsoft.WindowsAzure.ServiceRuntime;
  8. using System.Xml;
  9. using System.ServiceModel.Syndication;
  10. using System.Diagnostics;
  11. using System.Collections.Specialized;
  12. using System.Xml.Linq;
  13. using System.Runtime.Serialization;
  14. using System.Runtime.Serialization.Json;
  15. using System.IO;
  16. using System.Text;
  17. using System.Text.RegularExpressions;
  18. namespace Microsoft.WindowsAzure.Companion.Controllers
  19. {
  20. public class ProductDownloadItem
  21. {
  22. public ProductDownload Download { get; set; }
  23. public List<string> Versions { get; set; }
  24. }
  25. public class ProductDownload
  26. {
  27. public string Name { get; set; }
  28. public string Version { get; set; }
  29. public string DownloadURL { get; set; }
  30. public string DownloadCondition { get; set; }
  31. public ProductConditions Conditions { get; set; }
  32. }
  33. [DataContract(Name = "ProductConditions")]
  34. public sealed class ProductConditions
  35. {
  36. [DataMember(Name = "Conditions", Order = 1)]
  37. public List<ProductCondition> Conditions { get; set; }
  38. }
  39. [DataContract(Name = "ProductCondition")]
  40. public sealed class ProductCondition
  41. {
  42. [DataMember(Name = "ProductID", Order = 1)]
  43. public string ProductID { get; set; }
  44. [DataMember(Name = "Versions", Order = 2)]
  45. public List<ProductVersion> Versions { get; set; }
  46. }
  47. [DataContract(Name = "ProductVersion")]
  48. public class ProductVersion : object
  49. {
  50. [DataMember(Name = "Version")]
  51. public string Version { get; set; }
  52. }
  53. public class Product
  54. {
  55. public string ProductID { get; set; }
  56. public bool IsDependency { get; set; }
  57. public SyndicationItem ProductItem { get; set; }
  58. public string Title { get; set; }
  59. public string EulaURL { get; set; }
  60. public string InstallPath { get; set; }
  61. public List<ProductDownload> DownloadURLs { get; set; }
  62. public List<string> Dependencies { get; set; }
  63. public bool IsInstalled { get; set; }
  64. public bool IsInstallValid { get; set; }
  65. }
  66. public class ProductCategoryGroup
  67. {
  68. public string Category { get; set; }
  69. public IEnumerable<SyndicationItem> Products { get; set; }
  70. }
  71. public abstract class BaseProductsController : BaseController
  72. {
  73. protected IDictionary<string, string> ProductsInstalledFeed { get; private set; }
  74. protected Dictionary<string, string> ProductsInstalled { get; private set; }
  75. protected IEnumerable<ProductCategoryGroup> ProductCategoryGroups { get; private set; }
  76. protected int ProductCategoryGroupsCount { get; private set; }
  77. protected abstract ActionResult PrepareViewData();
  78. /// <summary>
  79. /// Prepares the installed products view data.
  80. /// </summary>
  81. /// <returns></returns>
  82. protected ActionResult PrepareInstalledProductsViewData()
  83. {
  84. try
  85. {
  86. // Pass list of installed applications
  87. IVMManager vmManager = WindowsAzureVMManager.GetVMManager();
  88. this.ProductsInstalledFeed = vmManager.GetInstalledProductsInfo();
  89. this.ProductsInstalled = new Dictionary<string, string>();
  90. foreach (string productId in this.ProductsInstalledFeed.Keys)
  91. {
  92. string valueInstalledProduct = this.ProductsInstalledFeed[productId];
  93. string[] productInstalledInfo = valueInstalledProduct.Split(',');
  94. string productInstalledVersion = productInstalledInfo[2].Trim();
  95. this.ProductsInstalled.Add(productId, productInstalledVersion);
  96. }
  97. ViewData["InstalledProducts"] = this.ProductsInstalledFeed;
  98. }
  99. catch (Exception ex)
  100. {
  101. Trace.TraceError("Unable to read installed products status: {0}", ex.Message);
  102. return RedirectToAction("Error", "Home", new { ErrorMessage = "Unable to read installed products status" });
  103. }
  104. return null;
  105. }
  106. /// <summary>
  107. /// Prepares the products view data.
  108. /// </summary>
  109. /// <returns></returns>
  110. protected ActionResult PrepareProductsViewData()
  111. {
  112. ActionResult result = null;
  113. // Set port number for applications website
  114. if (Server.HtmlEncode(Request.Url.Scheme).Equals("http"))
  115. {
  116. ViewData["ProductListXmlFeedPort"] = RoleEnvironment.CurrentRoleInstance.
  117. InstanceEndpoints["HttpIn"].IPEndpoint.Port.ToString();
  118. }
  119. else
  120. {
  121. ViewData["ProductListXmlFeedPort"] = RoleEnvironment.CurrentRoleInstance.
  122. InstanceEndpoints["HttpsIn"].IPEndpoint.Port.ToString();
  123. }
  124. try
  125. {
  126. if (0 < this.ProductListXmlFeedItems.Count())
  127. {
  128. // Categorize products
  129. this.ProductCategoryGroups = from item in (IEnumerable<SyndicationItem>)ViewData["ProductListXmlFeedItems"]
  130. group item by item.ElementExtensions.ReadElementExtensions<string>("productCategory", "http://www.w3.org/2005/Atom")[0] into g
  131. select new ProductCategoryGroup
  132. {
  133. Category = g.Key,
  134. Products = g
  135. };
  136. this.ProductCategoryGroupsCount = this.ProductCategoryGroups.Count();
  137. }
  138. else
  139. {
  140. this.ProductCategoryGroups = null;
  141. this.ProductCategoryGroupsCount = 0;
  142. }
  143. result = PrepareInstalledProductsViewData();
  144. if (null != result)
  145. {
  146. return result;
  147. }
  148. }
  149. catch (Exception ex)
  150. {
  151. Trace.TraceError("Unable to read list of Products from Atom Feed: {0}", ex.Message);
  152. return RedirectToAction("Error", "Home", new { ErrorMessage = "Unable to read list of Products from Atom Feed" });
  153. }
  154. return null;
  155. }
  156. /// <summary>
  157. /// Prepares the product category groups.
  158. /// </summary>
  159. /// <param name="productCategories">The product categories.</param>
  160. /// <param name="productCategoryGroups">The product category groups.</param>
  161. /// <returns></returns>
  162. protected ActionResult PrepareProductCategoryGroups(
  163. string productCategoriesConfig,
  164. string productCategoryGroups
  165. ) {
  166. string productCategoryListing = String.Empty;
  167. try
  168. {
  169. productCategoryListing = RoleEnvironment.GetConfigurationSettingValue(productCategoriesConfig);
  170. if (string.IsNullOrEmpty(productCategoryListing))
  171. {
  172. throw new System.NullReferenceException("Unable to read product category configuration setting");
  173. }
  174. }
  175. catch ( Exception ex )
  176. {
  177. Trace.TraceError("Unable to read product category configuration setting: {0}", ex.Message);
  178. return RedirectToAction("Error", "Home", new { ErrorMessage = "Unable to read product category configuration setting." });
  179. }
  180. ActionResult viewResult = this.PrepareProductsViewData();
  181. if (null != viewResult)
  182. {
  183. return viewResult;
  184. }
  185. try
  186. {
  187. if (0 < this.ProductCategoryGroupsCount)
  188. {
  189. char[] commaSplit = { ',' };
  190. string[] categoriesArray = productCategoryListing.TrimEnd(commaSplit).Split(commaSplit);
  191. if (null == categoriesArray || 0 == categoriesArray.Count())
  192. {
  193. throw new System.NullReferenceException();
  194. }
  195. List<string> categoriesList = new List<string>(categoriesArray.Length);
  196. categoriesList.AddRange(categoriesArray);
  197. List<string> categoriesListTrimmed
  198. = categoriesList.ConvertAll<string>
  199. (
  200. new Converter<string, string>(
  201. delegate(string str)
  202. {
  203. str = str.Trim();
  204. return str;
  205. }
  206. )
  207. );
  208. IEnumerable<ProductCategoryGroup> enumProductCategoryGroup
  209. = from productCategoryGroup in this.ProductCategoryGroups
  210. where categoriesListTrimmed.Contains(productCategoryGroup.Category, StringComparer.OrdinalIgnoreCase)
  211. select productCategoryGroup;
  212. List<ProductCategoryGroup> listProductCategoryGroup = enumProductCategoryGroup.ToList();
  213. ViewData[productCategoryGroups]
  214. = 0 < listProductCategoryGroup.Count
  215. ? listProductCategoryGroup
  216. : null;
  217. }
  218. }
  219. catch (Exception ex)
  220. {
  221. Trace.TraceError("Unable to read list of Platform products: {0}", ex.Message);
  222. return RedirectToAction("Error", "Home", new { ErrorMessage = "Unable to read list of Platform products." });
  223. }
  224. ActionResult result = PrepareInstalledProductsViewData();
  225. if (null != result)
  226. {
  227. return result;
  228. }
  229. return View();
  230. }
  231. /// <summary>
  232. /// Prepares the products to install view data.
  233. /// </summary>
  234. /// <returns></returns>
  235. protected ActionResult PrepareProductsToInstallViewData()
  236. {
  237. string productsSelectedToInstall = ViewData["ProductsToInstall"] as string;
  238. if (!string.IsNullOrEmpty(productsSelectedToInstall))
  239. {
  240. string[] productIDsSelectedToInstall = productsSelectedToInstall.Split(',');
  241. if (0 < productIDsSelectedToInstall.Count())
  242. {
  243. Dictionary<string, bool> productIDsToInstall = new Dictionary<string, bool>();
  244. foreach (string productID in productIDsSelectedToInstall)
  245. {
  246. productIDsToInstall.Add(productID, false);
  247. }
  248. ActionResult result = PrepareInstalledProductsViewData();
  249. if (null != result)
  250. {
  251. return result;
  252. }
  253. Dictionary<string, Product> productsToInstallAll = new Dictionary<string, Product>();
  254. BuildProductList(productIDsToInstall, ref productsToInstallAll);
  255. ValidateProductListDependencies(ref productsToInstallAll);
  256. ViewData["ProductsToInstallToAccept"] = productsToInstallAll;
  257. }
  258. }
  259. return View();
  260. }
  261. /// <summary>
  262. /// Validates the product list dependencies.
  263. /// </summary>
  264. /// <param name="productsToInstallAll">The products to install all.</param>
  265. private void ValidateProductListDependencies(ref Dictionary<string, Product> productsToInstallAll)
  266. {
  267. Dictionary<string, List<string>> dicProductDependenciesAll = new Dictionary<string, List<string>>();
  268. foreach (KeyValuePair<string, Product> kvpProductInstall in productsToInstallAll)
  269. {
  270. string productID = kvpProductInstall.Key;
  271. Product p = kvpProductInstall.Value;
  272. Dictionary<string, List<string>> dicProductDependencies = new Dictionary<string, List<string>>();
  273. foreach (ProductDownload pd in p.DownloadURLs)
  274. {
  275. if (null == pd.Conditions)
  276. {
  277. continue;
  278. }
  279. foreach (ProductCondition pc in pd.Conditions.Conditions)
  280. {
  281. string productIdCondition = pc.ProductID;
  282. List<string> listVersions = new List<string>();
  283. foreach (ProductVersion v in pc.Versions )
  284. {
  285. listVersions.Add(v.Version);
  286. }
  287. if (!dicProductDependencies.Keys.Contains(productIdCondition, StringComparer.OrdinalIgnoreCase))
  288. {
  289. dicProductDependencies.Add(productIdCondition, listVersions);
  290. }
  291. else
  292. {
  293. dicProductDependencies[productIdCondition]
  294. = dicProductDependencies[productIdCondition].Union(listVersions).ToList();
  295. }
  296. }
  297. }
  298. foreach (KeyValuePair<string, List<string>> kvpProductDependency in dicProductDependencies)
  299. {
  300. string productIdDependency = kvpProductDependency.Key;
  301. List<string> productIdVersions = kvpProductDependency.Value;
  302. if (!dicProductDependenciesAll.Keys.Contains(productIdDependency, StringComparer.OrdinalIgnoreCase))
  303. {
  304. dicProductDependenciesAll.Add(productIdDependency, productIdVersions);
  305. }
  306. else
  307. {
  308. dicProductDependenciesAll[productIdDependency]
  309. = dicProductDependenciesAll[productIdDependency].Intersect(productIdVersions).ToList();
  310. }
  311. }
  312. }
  313. foreach (KeyValuePair<string, Product> kvpProduct in productsToInstallAll)
  314. {
  315. Product p = kvpProduct.Value;
  316. List<ProductDownload> downloadURLsValid = new List<ProductDownload>();
  317. foreach (ProductDownload pd in p.DownloadURLs)
  318. {
  319. if (null == pd.Conditions)
  320. {
  321. downloadURLsValid.Add(pd);
  322. continue;
  323. }
  324. Dictionary<string, List<string>> dicProductDependencies = new Dictionary<string, List<string>>();
  325. foreach (ProductCondition pc in pd.Conditions.Conditions)
  326. {
  327. List<string> listVersions = new List<string>();
  328. foreach (ProductVersion v in pc.Versions)
  329. {
  330. listVersions.Add(v.Version);
  331. }
  332. if (dicProductDependencies.Keys.Contains(pc.ProductID, StringComparer.OrdinalIgnoreCase))
  333. {
  334. dicProductDependencies[pc.ProductID]
  335. = dicProductDependencies[pc.ProductID].Union(listVersions).ToList();
  336. }
  337. else
  338. {
  339. dicProductDependencies[pc.ProductID] = listVersions;
  340. }
  341. }
  342. bool areConditionsValid = true;
  343. foreach (KeyValuePair<string, List<string>> kvpProductCondition in dicProductDependencies)
  344. {
  345. string productConditionID = kvpProductCondition.Key;
  346. List<string> versionsCondition = kvpProductCondition.Value;
  347. if (!dicProductDependenciesAll.Keys.Contains(productConditionID, StringComparer.OrdinalIgnoreCase))
  348. {
  349. areConditionsValid = false;
  350. break;
  351. }
  352. if (0 == dicProductDependenciesAll[productConditionID].Count)
  353. {
  354. areConditionsValid = false;
  355. break;
  356. }
  357. List<string> productConditionVersionsIntersect
  358. = dicProductDependenciesAll[productConditionID].Intersect(versionsCondition).ToList();
  359. if (0 == productConditionVersionsIntersect.Count)
  360. {
  361. areConditionsValid = false;
  362. break;
  363. }
  364. if (this.ProductsInstalled.Keys.Contains(productConditionID, StringComparer.OrdinalIgnoreCase))
  365. {
  366. if (!productConditionVersionsIntersect.Contains(this.ProductsInstalled[productConditionID], StringComparer.OrdinalIgnoreCase))
  367. {
  368. areConditionsValid = false;
  369. break;
  370. }
  371. }
  372. }
  373. if ( areConditionsValid )
  374. {
  375. downloadURLsValid.Add(pd);
  376. }
  377. }
  378. if (0 == downloadURLsValid.Count)
  379. {
  380. p.IsInstallValid = false;
  381. }
  382. else
  383. {
  384. p.IsInstallValid = true;
  385. p.DownloadURLs = downloadURLsValid;
  386. }
  387. }
  388. }
  389. /// <summary>
  390. /// Builds the product list.
  391. /// </summary>
  392. /// <param name="feed">The feed.</param>
  393. /// <param name="productsToInstallNew">The products to install new.</param>
  394. /// <param name="productsInstallList">The products list.</param>
  395. private void BuildProductList (
  396. Dictionary<string, bool> productIDsToInstallNew,
  397. ref Dictionary<string, Product> productsToInstallAll
  398. )
  399. {
  400. if (0 == productIDsToInstallNew.Count)
  401. {
  402. return;
  403. }
  404. Dictionary<string, bool> productIDsToInstall = new Dictionary<string, bool>(productIDsToInstallNew);
  405. productIDsToInstallNew.Clear();
  406. IEnumerable<Product> Products
  407. = from item in this.ProductListXmlFeedItems
  408. where productIDsToInstall.Keys.Contains(item.ElementExtensions.ReadElementExtensions<string>("productId", "http://www.w3.org/2005/Atom")[0])
  409. select new Product
  410. {
  411. ProductID = item.ElementExtensions.ReadElementExtensions<string>("productId", "http://www.w3.org/2005/Atom")[0],
  412. ProductItem = item,
  413. IsInstalled = this.ProductsInstalled.Keys.Contains(item.ElementExtensions.ReadElementExtensions<string>("productId", "http://www.w3.org/2005/Atom")[0]),
  414. IsDependency = productIDsToInstall[item.ElementExtensions.ReadElementExtensions<string>("productId", "http://www.w3.org/2005/Atom")[0]]
  415. };
  416. foreach (Product p in Products)
  417. {
  418. string productID = p.ProductID;
  419. if (p.IsInstalled)
  420. {
  421. continue;
  422. }
  423. if (productsToInstallAll.Keys.Contains(productID, StringComparer.OrdinalIgnoreCase))
  424. {
  425. continue;
  426. }
  427. p.DownloadURLs = null;
  428. if (0 < p.ProductItem.ElementExtensions.Where<SyndicationElementExtension>(x => x.OuterName == "installerFileChoices").Count())
  429. {
  430. XElement installerFileChoicesElement
  431. = p.ProductItem.ElementExtensions.Where<SyndicationElementExtension>(x => x.OuterName == "installerFileChoices").FirstOrDefault().GetObject<XElement>();
  432. if (null != installerFileChoicesElement)
  433. {
  434. p.DownloadURLs = new List<ProductDownload>();
  435. foreach (XElement extension in installerFileChoicesElement.Elements())
  436. {
  437. ProductDownload pd
  438. = new ProductDownload
  439. {
  440. DownloadURL = extension.Attribute("url").Value,
  441. Version = (null != extension.Attribute("version")) ? extension.Attribute("version").Value : String.Empty
  442. };
  443. if (!string.IsNullOrEmpty(pd.DownloadCondition))
  444. {
  445. string jsonRaw = Server.HtmlDecode(pd.DownloadCondition);
  446. ProductConditions pc = null;
  447. this.ConvertJSON(jsonRaw, ref pc);
  448. pd.Conditions = pc;
  449. }
  450. p.DownloadURLs.Add(pd);
  451. }
  452. }
  453. }
  454. // Set dependancies
  455. p.Dependencies = null;
  456. XElement dependenciesElement = p.ProductItem.ElementExtensions.
  457. ReadElementExtensions<XElement>("dependencies", "http://www.w3.org/2005/Atom").SingleOrDefault();
  458. if (dependenciesElement != null)
  459. {
  460. p.Dependencies = new List<string>();
  461. foreach (string dependency in dependenciesElement.Value.Split(','))
  462. {
  463. p.Dependencies.Add(dependency);
  464. if (!productIDsToInstallNew.Keys.Contains(dependency, StringComparer.OrdinalIgnoreCase))
  465. {
  466. productIDsToInstallNew.Add(dependency, true);
  467. }
  468. }
  469. }
  470. productsToInstallAll.Add(
  471. productID,
  472. p
  473. );
  474. }
  475. BuildProductList(productIDsToInstallNew, ref productsToInstallAll);
  476. }
  477. /// <summary>
  478. /// Converts the JSON.
  479. /// </summary>
  480. /// <param name="jsonRaw">The json raw.</param>
  481. /// <param name="pc">The pc.</param>
  482. /// <returns></returns>
  483. private bool ConvertJSON(string jsonRaw, ref ProductConditions pc)
  484. {
  485. bool success = false;
  486. try
  487. {
  488. string json = Regex.Replace(jsonRaw, "\'", "\"");
  489. DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(ProductConditions));
  490. using (MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(json)))
  491. {
  492. pc = ser.ReadObject(ms) as ProductConditions;
  493. }
  494. success = true;
  495. }
  496. catch (Exception ex)
  497. {
  498. throw ex;
  499. }
  500. return success;
  501. }
  502. }
  503. }