using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Net; using System.Text.RegularExpressions; using System.Windows.Forms; using HeuristicLab.Clients.Hive.CloudManager.Azure; using HeuristicLab.Clients.Hive.CloudManager.Model; using HeuristicLab.Core; using Microsoft.WindowsAzure; using Microsoft.WindowsAzure.StorageClient; namespace HeuristicLab.Clients.Hive.CloudManager.Views { public partial class AddAzureServiceDialog : Form { private BindingList subscriptions; private BindingList hostedServices; private BindingList locations; private BindingList affinityGroups; private BindingList storageServices; private BackgroundWorker workerUpdate; private BackgroundWorker workerCreate; private bool isInitializing; private bool bwCompleted; private bool closePending; private string certificateFile; private string certificatePassword; public AddAzureServiceDialog(IItemList subs) { InitializeComponent(); isInitializing = true; bwCompleted = true; this.subscriptions = new BindingList(subs); this.hostedServices = new BindingList(); this.locations = new BindingList(); this.affinityGroups = new BindingList(); this.storageServices = new BindingList(); SetLookupBinding(cmbLocation, locations, "", ""); SetLookupBinding(cmbAffinityGroup, affinityGroups, "Label", "Name"); SetLookupBinding(cmbChooseHostedService, hostedServices, "ServiceName", "ServiceName"); SetLookupBinding(cmbChooseSubscription, subscriptions, "SubscriptionName", "SubscriptionID"); SetLookupBinding(cmbStorageServices, storageServices, "ServiceName", "Url"); if (!cbNewHostedService.Checked) { SetGroupBoxControlsEnabled(gbNewHostedService, cbNewHostedService.Checked); } certificateFile = string.Empty; certificatePassword = string.Empty; workerUpdate = new BackgroundWorker(); workerUpdate.DoWork += new DoWorkEventHandler(UpdateTask); workerUpdate.RunWorkerCompleted += new RunWorkerCompletedEventHandler(WorkerCompleted); workerCreate = new BackgroundWorker(); workerCreate.DoWork += new DoWorkEventHandler(CreateDeploymentTask); workerCreate.RunWorkerCompleted += new RunWorkerCompletedEventHandler(CreateDeploymentCompleted); isInitializing = false; cmbChooseSubscription_SelectedValueChanged(this, EventArgs.Empty); } protected override void OnFormClosing(FormClosingEventArgs e) { if (!bwCompleted) { SetAllControlsEnabled(this, false); e.Cancel = true; closePending = true; } } private void SetLookupBinding(ListControl toBind, object dataSource, string displayMember, string valueMember) { toBind.DisplayMember = displayMember; toBind.ValueMember = valueMember; toBind.DataSource = dataSource; } private void SetGroupBoxControlsEnabled(GroupBox gb, bool isEnabled) { foreach (Control ctrl in gb.Controls) { ctrl.Enabled = isEnabled; } } private void SetAllControlsEnabled(Form frm, bool isEnabled) { foreach (Control ctrl in frm.Controls) { if (!(ctrl is ProgressBar)) { ctrl.Enabled = isEnabled; } } } private void SetBindingList(ref BindingList bindinglist, List list) { if (list != null) { BindingList newList = new BindingList(list); bindinglist = newList; } } private void UpdateBindingList(BindingList bindinglist, List list) { if (list != null) { bindinglist.Clear(); foreach (T item in list) bindinglist.Add(item); } } private void UpdateSubscriptionItem(BindingList bindinglist, Subscription item) { if (item != null) { foreach (Subscription blItem in bindinglist) if (blItem.Equals(item)) { blItem.Merge(item); } } } private void cbNewHostedService_CheckedChanged(object sender, EventArgs e) { if (cbNewHostedService.Checked) { Subscription subscription = (Subscription)cmbChooseSubscription.SelectedItem; if (subscription.CurrentHostedServices == subscription.MaxHostedServices) { PluginInfrastructure.ErrorHandling.ShowErrorDialog(new Exception("You have already created the maximum number of hosted services")); cbNewHostedService.Checked = false; } else { SetGroupBoxControlsEnabled(gbNewHostedService, cbNewHostedService.Checked); cmbChooseHostedService.Enabled = false; } } else { // checked == false SetGroupBoxControlsEnabled(gbNewHostedService, false); if (hostedServices.Count == 0) { cbNewHostedService.Checked = true; } } } private void cmbChooseSubscription_SelectedValueChanged(object sender, EventArgs e) { if (!isInitializing) { Subscription item = (Subscription)cmbChooseSubscription.SelectedItem; if (item != null) { bwCompleted = false; progressBar.Visible = true; SetAllControlsEnabled(this, false); workerUpdate.RunWorkerAsync(item); } } } private void btnAddCertificate_Click(object sender, EventArgs e) { using (var form = new AddCertificate()) { form.ShowDialog(); if (!form.ErrorOccured) { certificateFile = form.CertificateFile; certificatePassword = form.CertificatePassword; lblCertificateFile.Text = certificateFile; } } } private void btnCancel_Click(object sender, EventArgs e) { this.Close(); } private void btnOk_Click(object sender, EventArgs e) { if (cmbChooseSubscription.SelectedItem != null) { Subscription sub = (Subscription)cmbChooseSubscription.SelectedItem; // the controls collection can be the whole form or just a panel or group if (Validation.hasValidationErrors(this.Controls)) { return; } else { bool newHostedServiceChecked = cbNewHostedService.Checked; bool regionChecked = rbRegion.Checked; HostedService hostedService = null; if (cbNewHostedService.Checked) { hostedService = new HostedService(); hostedService.ServiceName = tbServiceName.Text; HostedServiceProperties properties = new HostedServiceProperties(); if (regionChecked) { properties.AffinityGroup = string.Empty; properties.Location = (string)cmbLocation.SelectedItem; } else { properties.AffinityGroup = ((AffinityGroup)cmbAffinityGroup.SelectedItem).Name; properties.Location = string.Empty; } if (cmbAffinityGroup.SelectedItem != null) { properties.AffinityGroup = ((AffinityGroup)cmbAffinityGroup.SelectedItem).Name; } properties.Label = tbLabel.Text; properties.Location = (string)cmbLocation.SelectedItem; hostedService.HostedServiceProperties = properties; } else { hostedService = (HostedService)cmbChooseHostedService.SelectedItem; } string certFile = certificateFile; string certPw = certificatePassword; //int instanceCount = Convert.ToInt32(tbInstanceCount.Text); int instanceCount = Convert.ToInt32(instancesNumericUpDown.Value); string packagefilepath = string.Empty; if (rbVMSizeSmall.Checked) { packagefilepath = Constants.DeploymentPackagePathSmall; } else if (rbVMSizeMedium.Checked) { packagefilepath = Constants.DeploymentPackagePathMedium; } else if (rbVMSizeLarge.Checked) { packagefilepath = Constants.DeploymentPackagePathLarge; } else if (rbVMSizeExtraLarge.Checked) { packagefilepath = Constants.DeploymentPackagePathExtraLarge; } string deploymentSlot = string.Empty; if (rbDeployToProduction.Checked) { deploymentSlot = Constants.DeploymentSlotProduction; } else if (rbDeployToStaging.Checked) { deploymentSlot = Constants.DeploymentSlotStaging; } //var parameters1 = Tuple.Create // (sub, newHostedServiceChecked, regionChecked, certificateFile, certificatePassword, hostedService, instanceCount); CreateDeploymentData parameters = new CreateDeploymentData(); parameters.Subscription = sub; parameters.CreateNewHosteService = newHostedServiceChecked; parameters.UseRegion = regionChecked; parameters.CertificateFilePath = certificateFile; parameters.CertificatePassword = certificatePassword; parameters.HostedService = hostedService; parameters.InstanceCount = instanceCount; parameters.StorageService = (StorageService)cmbStorageServices.SelectedItem; parameters.CreateContainerIfNotExists = cbCreateBlobIfNotExists.Checked; parameters.BlobContainerName = tbBlobContainer.Text; parameters.DeploymentPackageFilePath = packagefilepath; parameters.DeploymentSlot = deploymentSlot; bwCompleted = false; progressBar.Visible = true; SetAllControlsEnabled(this, false); workerCreate.RunWorkerAsync(parameters); } } } private void UpdateTask(object sender, DoWorkEventArgs e) { try { Subscription item = (Subscription)e.Argument; item = CloudManagerClient.Instance.AzureProvider.GetSubscriptionInfo(item.SubscriptionID, item.CertificateThumbprint); this.Invoke((MethodInvoker)delegate { lblCores.Text = item.CurrentCoreCount + " / " + item.MaxCoreCount; instancesNumericUpDown.Maximum = item.MaxCoreCount; }); List services = CloudManagerClient.Instance.AzureProvider.ListHostedServices(item); List locs = CloudManagerClient.Instance.AzureProvider.ListLocations(item); List groups = CloudManagerClient.Instance.AzureProvider.ListAffinityGroups(item); List storage = CloudManagerClient.Instance.AzureProvider.ListStorageServices(item); this.Invoke((MethodInvoker)delegate { UpdateSubscriptionItem(subscriptions, item); }); this.Invoke((MethodInvoker)delegate { UpdateBindingList(hostedServices, services); }); this.Invoke((MethodInvoker)delegate { UpdateBindingList(locations, locs); }); this.Invoke((MethodInvoker)delegate { UpdateBindingList(affinityGroups, groups); }); this.Invoke((MethodInvoker)delegate { UpdateBindingList(storageServices, storage); }); } catch (Exception ex) { PluginInfrastructure.ErrorHandling.ShowErrorDialog(ex); } } private void WorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { SetAllControlsEnabled(this, true); progressBar.Visible = false; bwCompleted = true; if (closePending) { this.Close(); } } private void CreateDeploymentTask(object sender, DoWorkEventArgs e) { bool errorOccured = false; CreateDeploymentData parameters = (CreateDeploymentData)e.Argument; Subscription sub = parameters.Subscription; bool newHostedServiceChecked = parameters.CreateNewHosteService; bool regionChecked = parameters.UseRegion; string certFile = parameters.CertificateFilePath; string certPw = parameters.CertificatePassword; HostedService hostedService = parameters.HostedService; int instanceCount = parameters.InstanceCount; StorageService storageService = parameters.StorageService; string blobContainerName = parameters.BlobContainerName; bool createContainerIfNotExists = parameters.CreateContainerIfNotExists; string packageFilePath = parameters.DeploymentPackageFilePath; FileInfo packageFile = new FileInfo(packageFilePath); string deploymentSlot = parameters.DeploymentSlot; int defaultCollectionLimit = ServicePointManager.DefaultConnectionLimit; CloudStorageAccount storageAccount = null; ; CloudBlobClient blobClient = null; CloudBlobContainer blobContainer = null; // STEP 1 - Initialize storage account try { StorageServiceKeys keys = CloudManagerClient.Instance.AzureProvider.GetStorageKeys(sub, storageService.ServiceName); storageAccount = CloudStorageAccount.Parse(string.Format(Constants.StorageServiceConnectionFormat, storageService.ServiceName, keys.Primary)); blobClient = storageAccount.CreateCloudBlobClient(); blobContainer = blobClient.GetContainerReference(blobContainerName); if (createContainerIfNotExists) { blobContainer.CreateIfNotExist(); } else { if (!blobContainer.Exists()) { throw new ApplicationException("Specified blob container doesn't exist."); } } } catch (Exception ex) { errorOccured = true; PluginInfrastructure.ErrorHandling.ShowErrorDialog(ex); } // STEP 2 - Copy service package file to storage account CloudBlockBlob blobPackage = null; try { blobPackage = blobContainer.GetBlockBlobReference(packageFile.Name); if (!blobPackage.Exists()) { ServicePointManager.DefaultConnectionLimit = 64; blobPackage.UploadParallel(packageFile.FullName); } } catch (Exception ex) { errorOccured = true; PluginInfrastructure.ErrorHandling.ShowErrorDialog(ex); } // STEP 1 - Create Hosted Service try { if ((!errorOccured) && (newHostedServiceChecked)) { if (regionChecked) { CloudManagerClient.Instance.AzureProvider.CreateHostedService(sub, hostedService.ServiceName, hostedService.HostedServiceProperties.Label, hostedService.HostedServiceProperties.Description, hostedService.HostedServiceProperties.Location); } else { CloudManagerClient.Instance.AzureProvider.CreateHostedService(sub, hostedService.ServiceName, hostedService.HostedServiceProperties.Label, hostedService.HostedServiceProperties.Description, new AffinityGroup() { Name = hostedService.HostedServiceProperties.AffinityGroup }); } } } catch (Exception ex) { errorOccured = true; PluginInfrastructure.ErrorHandling.ShowErrorDialog(ex); } // STEP 2 - Add Certificate try { if ((!errorOccured) && (certFile != string.Empty)) { CloudManagerClient.Instance.AzureProvider.AddCertificate(sub, hostedService, certFile, certPw); } } catch (Exception ex) { errorOccured = true; PluginInfrastructure.ErrorHandling.ShowErrorDialog(ex); if (newHostedServiceChecked) { CloudManagerClient.Instance.AzureProvider.DeleteHostedService(sub, hostedService.ServiceName); } } // STEP 3 - Create Deployment try { if (!errorOccured) { //CloudManagerClient.Instance.AzureProvider.CreateDeployment(sub, hostedService.ServiceName, Guid.NewGuid().ToString(), Constants.DeploymentSlotStaging, Constants.DeploymentPackageUrl, Constants.DeploymentConfigurationUrl, Constants.DeploymentLabel, instanceCount); CloudManagerClient.Instance.AzureProvider.CreateDeployment(sub, hostedService.ServiceName, Guid.NewGuid().ToString(), deploymentSlot, blobPackage.Uri.ToString(), new FileInfo(Constants.DeploymentConfigurationPath), Constants.DeploymentLabel, instanceCount); } } catch (Exception ex) { errorOccured = true; PluginInfrastructure.ErrorHandling.ShowErrorDialog(ex); if (newHostedServiceChecked) { CloudManagerClient.Instance.AzureProvider.DeleteHostedService(sub, hostedService.ServiceName); } } ServicePointManager.DefaultConnectionLimit = defaultCollectionLimit; e.Result = errorOccured; } private void CreateDeploymentCompleted(object sender, RunWorkerCompletedEventArgs e) { SetAllControlsEnabled(this, true); progressBar.Visible = false; bwCompleted = true; bool errorOccured = (bool)e.Result; if (closePending) { this.Close(); } else if (!errorOccured) { this.Close(); } else { this.Show(); } } private void tbServiceName_Validating(object sender, CancelEventArgs e) { if (cbNewHostedService.Checked) { string url = tbServiceName.Text.Trim(); if (url == String.Empty) { errorProvider.SetError(tbServiceName, "A URL is required for the hosted service."); e.Cancel = true; } else if (!isValidHostedServiceUrl(url)) { errorProvider.SetError(tbServiceName, "A hosted service name must be between 1 and 63 " + "characters long, and be composed of letters, numbers " + "and hyphens. Hyphens shouldn't be first or last letter."); e.Cancel = true; } else { errorProvider.SetError(tbServiceName, ""); } } } private void tbLabel_Validating(object sender, CancelEventArgs e) { if (cbNewHostedService.Checked) { if (tbLabel.Text.Trim() == String.Empty) { errorProvider.SetError(tbLabel, "A name is required for the hosted service."); e.Cancel = true; } else { errorProvider.SetError(tbLabel, ""); } } } private bool isValidHostedServiceUrl(string url) { string regexString = @"[a-zA-Z0-9]+[a-zA-Z0-9-]{0,61}[a-zA-Z0-9]+"; //TODO Regex regex = new Regex(regexString); if (regex.IsMatch(url)) { return true; } else { return false; } } private bool isValidStorageContainerName(string name) { string regexString = @"[a-z0-9]+[a-z0-9-]{0,61}[a-z0-9]+"; //TODO Regex regex = new Regex(regexString); if (regex.IsMatch(name)) { return true; } else { return false; } } private void tbBlobContainer_Validating(object sender, CancelEventArgs e) { string name = tbBlobContainer.Text.Trim(); if (name == string.Empty) { errorProvider.SetError(tbBlobContainer, "A name is required for the blob container."); e.Cancel = true; } else if (!isValidStorageContainerName(name)) { errorProvider.SetError(tbBlobContainer, "A blob container name must be between 1 and 63 " + "characters long, and be composed of lowercase letters, numbers " + "and hyphens. Hyphens shouldn't be first or last letter."); e.Cancel = true; } else { errorProvider.SetError(tbBlobContainer, ""); } } private void cmbChooseHostedService_Validating(object sender, CancelEventArgs e) { if ((!cbNewHostedService.Checked) && (cmbChooseHostedService.SelectedItem == null)) { errorProvider.SetError(cmbChooseHostedService, "A hoste service is required."); e.Cancel = true; } else { errorProvider.SetError(cmbChooseHostedService, ""); } } private void cmbStorageServices_Validating(object sender, CancelEventArgs e) { if (cmbStorageServices.SelectedItem == null) { errorProvider.SetError(cmbStorageServices, "A storage service is required."); e.Cancel = true; } else { errorProvider.SetError(cmbStorageServices, ""); } } private class CreateDeploymentData { public Subscription Subscription { get; set; } public bool CreateNewHosteService { get; set; } public bool UseRegion { get; set; } public string CertificateFilePath { get; set; } public string CertificatePassword { get; set; } public HostedService HostedService { get; set; } public int InstanceCount { get; set; } public StorageService StorageService { get; set; } public string BlobContainerName { get; set; } public bool CreateContainerIfNotExists { get; set; } public string DeploymentPackageFilePath { get; set; } public string DeploymentSlot { get; set; } } } }