using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using Microsoft.Azure.Management.AppService.Fluent;
using Microsoft.Azure.Management.Fluent;
using Microsoft.Azure.Management.Sql.Fluent;
using Microsoft.Azure.Management.Sql.Fluent.Models;
using Tango.BL;
using Tango.BL.Entities;
using Tango.Core;
using Tango.Core.DB;
using Tango.Core.Helpers;
namespace Tango.AzureUtils.Database
{
public class DatabaseManager : AzureUtilsComponentBase
{
#region Constructors
///
/// Initializes a new instance of the class.
///
/// The azure.
public DatabaseManager(IAzure azure) : base(azure)
{
}
#endregion
#region SQL Examiner
///
/// Opens the SQL examiner schema using the specified source app and target app configuration.
///
/// The source application.
/// The target application.
///
public async Task OpenSQLExaminerSchema(IWebAppBase sourceApp, IWebAppBase targetApp)
{
String projectFile = Path.Combine(AssemblyHelper.GetCurrentAssemblyFolder(), "Database", "GENERAL_ENV_UPGRADE.seproj");
using (Stream stream = GetProjectFileStream(projectFile))
{
XElement projectXml = XElement.Load(stream);
var sourceSettings = await sourceApp.GetMachineServiceSettingsAsync();
var targetSettings = await targetApp.GetMachineServiceSettingsAsync();
ApplyDatabaseSettingsToProjectXml(projectXml, sourceSettings, targetSettings);
var tempFile = TemporaryManager.CreateImaginaryFile(".seproj");
tempFile.Persist = true;
File.WriteAllText(tempFile, projectXml.ToString());
Process.Start(tempFile);
}
}
///
/// Opens the SQL examiner data using the specified source app and target app configuration.
///
/// The source application.
/// The target application.
/// if set to true [for creation].
///
public async Task OpenSQLExaminerData(IWebAppBase sourceApp, IWebAppBase targetApp, bool forCreation = false)
{
String projectFile = Path.Combine(AssemblyHelper.GetCurrentAssemblyFolder(), "Database", "GENERAL_ENV_UPGRADE.sdeproj");
if (forCreation)
{
projectFile = Path.Combine(AssemblyHelper.GetCurrentAssemblyFolder(), "Database", "GENERAL_ENV_CREATE.sdeproj");
}
using (Stream stream = GetProjectFileStream(projectFile))
{
XElement projectXml = XElement.Load(stream);
var sourceSettings = await sourceApp.GetMachineServiceSettingsAsync();
var targetSettings = await targetApp.GetMachineServiceSettingsAsync();
ApplyDatabaseSettingsToProjectXml(projectXml, sourceSettings, targetSettings);
var tempFile = TemporaryManager.CreateImaginaryFile(".sdeproj");
tempFile.Persist = true;
File.WriteAllText(tempFile, projectXml.ToString());
Process.Start(tempFile);
}
}
private Stream GetProjectFileStream(String projectFile)
{
byte[] projectBytes = File.ReadAllBytes(projectFile);
MemoryStream stream = new MemoryStream(projectBytes);
return stream;
}
private void ApplyDatabaseSettingsToProjectXml(XElement projectXml, MachineServiceSettings sourceSettings, MachineServiceSettings targetSettings)
{
var sourceElement = projectXml.Elements().SelectMany(x => x.Descendants()).SingleOrDefault(x => x.Name == "Source" && x.Attributes().SingleOrDefault(y => y.Name == "id").Value == "1");
var targetElement = projectXml.Elements().SelectMany(x => x.Descendants()).SingleOrDefault(x => x.Name == "Source" && x.Attributes().SingleOrDefault(y => y.Name == "id").Value == "2");
ApplyDatabaseSettingsToSourceElement(sourceElement, sourceSettings);
ApplyDatabaseSettingsToSourceElement(targetElement, targetSettings);
}
private void ApplyDatabaseSettingsToSourceElement(XElement sourceElement, MachineServiceSettings settings)
{
sourceElement.Element("ServerName").SetValue(settings.DB_ADDRESS);
sourceElement.Element("Database").SetValue(settings.DB_CATALOG);
sourceElement.Element("Login").SetValue(settings.DB_USER_NAME);
sourceElement.Element("Password").SetValue(settings.DB_PASSWORD);
}
#endregion
#region Azure SQL Database
public async Task IsDatabaseExists(String serverName, String databaseName)
{
OnProgress(AzureUtilsStage.Database, $"Checking {serverName} database server...");
var sqlServer = (await Azure.SqlServers.ListAsync()).SingleOrDefault(x => x.Name == serverName);
var database = (await sqlServer.Databases.ListAsync()).SingleOrDefault(x => x.Name == databaseName);
return database != null;
}
public async Task AddDatabase(String serverName, String databaseName)
{
OnProgress(AzureUtilsStage.Database, $"Checking {serverName} database server...");
var sqlServer = (await Azure.SqlServers.ListAsync()).SingleOrDefault(x => x.Name == serverName);
OnProgress(AzureUtilsStage.Database, $"Creating new database '{databaseName}'...");
var database = await sqlServer.Databases.Define(databaseName).WithEdition(DatabaseEdition.Standard).CreateAsync();
return database;
}
public Task AddDatabase(String databaseName)
{
return AddDatabase("twine", databaseName);
}
public async Task RemoveDatabase(String serverName, String databaseName)
{
OnProgress(AzureUtilsStage.Database, $"Checking {serverName} database server...");
var sqlServer = (await Azure.SqlServers.ListAsync()).SingleOrDefault(x => x.Name == serverName);
OnProgress(AzureUtilsStage.Database, $"Removing database '{databaseName}'...");
await sqlServer.Databases.DeleteAsync(databaseName);
}
#endregion
#region Users & Permissions
public async Task AddEnvironmentGroupPermissions(String address, String databaseName, String groupName, String email, String password)
{
OnProgress(AzureUtilsStage.Database, $"Adding environment group permissions on '{databaseName}'...");
using (DbManager db = DbManager.FromDataSource(new DataSource()
{
Type = DataSourceType.Azure,
Address = address,
Catalog = databaseName,
UserName = email,
Password = password,
IntegratedSecurity = false
}))
{
await db.ExecuteCommandAsync($"CREATE USER [{groupName}] FROM EXTERNAL PROVIDER");
await db.ExecuteCommandAsync($"ALTER ROLE db_datareader ADD MEMBER [{groupName}];");
await db.ExecuteCommandAsync($"ALTER ROLE db_datawriter ADD MEMBER [{groupName}];");
}
}
public async Task AddBackupUser(String address, String databaseName, String userName, String password)
{
OnProgress(AzureUtilsStage.Database, $"Adding BackupUser permissions on '{databaseName}'...");
using (DbManager db = DbManager.FromCredentials(address, databaseName, userName, password))
{
await db.ExecuteCommandAsync("CREATE USER [BackupUser] FOR LOGIN [BackupUser] WITH DEFAULT_SCHEMA=[dbo]");
await db.ExecuteCommandAsync("EXEC sp_addrolemember N'db_owner', N'BackupUser'");
await db.ExecuteCommandAsync("EXEC sp_addrolemember N'db_accessadmin', N'BackupUser'");
await db.ExecuteCommandAsync("EXEC sp_addrolemember N'db_securityadmin', N'BackupUser'");
await db.ExecuteCommandAsync("EXEC sp_addrolemember N'db_backupoperator', N'BackupUser'");
await db.ExecuteCommandAsync("EXEC sp_addrolemember N'db_datareader', N'BackupUser'");
await db.ExecuteCommandAsync("EXEC sp_addrolemember N'db_datawriter', N'BackupUser'");
}
}
public async Task AddTangoWebAppUser(String address, String databaseName, String userName, String password)
{
OnProgress(AzureUtilsStage.Database, $"Adding Tango WebApp permissions on '{databaseName}'...");
using (DbManager db = DbManager.FromCredentials(address, databaseName, userName, password))
{
await db.ExecuteCommandAsync("CREATE USER [Tango] FOR LOGIN [BackupUser] WITH DEFAULT_SCHEMA=[dbo]");
await db.ExecuteCommandAsync("EXEC sp_addrolemember N'db_datareader', N'Tango'");
await db.ExecuteCommandAsync("EXEC sp_addrolemember N'db_datawriter', N'Tango'");
}
}
#endregion
#region Versions
public async Task UpgradeMachineStudioVersion(IWebAppBase sourceApp, IWebAppBase targetApp)
{
var latestMachineStudioVersion = await GetLatestMachineStudioVersion(sourceApp);
var targetDataSource = (await targetApp.GetMachineServiceSettingsAsync()).ToDataSource();
OnProgress(AzureUtilsStage.Database, $"Adding machine studio database entry for version '{latestMachineStudioVersion.Version}'...");
using (var db = ObservablesContext.CreateDefault(targetDataSource))
{
db.MachineStudioVersions.Add(latestMachineStudioVersion);
await db.SaveChangesAsync();
}
}
public async Task UpgradePPCVersion(IWebAppBase sourceApp, IWebAppBase targetApp)
{
var latestPPCVersion = await GetLatestPPCVersion(sourceApp);
var targetDataSource = (await targetApp.GetMachineServiceSettingsAsync()).ToDataSource();
OnProgress(AzureUtilsStage.Database, $"Adding PPC database entry for version '{latestPPCVersion.Version}'...");
using (var db = ObservablesContext.CreateDefault(targetDataSource))
{
db.TangoVersions.Add(latestPPCVersion);
await db.SaveChangesAsync();
}
}
public async Task UpgradeFSEVersion(IWebAppBase sourceApp, IWebAppBase targetApp)
{
var latestFSEVersion = await GetLatestFSEVersion(sourceApp);
var targetDataSource = (await targetApp.GetMachineServiceSettingsAsync()).ToDataSource();
OnProgress(AzureUtilsStage.Database, $"Adding FSE database entry for version '{latestFSEVersion.Version}'...");
using (var db = ObservablesContext.CreateDefault(targetDataSource))
{
db.FseVersions.Add(latestFSEVersion);
await db.SaveChangesAsync();
}
}
public async Task DowngradeMachineStudioVersion(IWebAppBase app)
{
var latestMachineStudioVersion = await GetLatestMachineStudioVersion(app);
var dataSource = (await app.GetMachineServiceSettingsAsync()).ToDataSource();
OnProgress(AzureUtilsStage.Database, $"Removing machine studio database entry for version '{latestMachineStudioVersion.Version}'...");
using (var db = ObservablesContext.CreateDefault(dataSource))
{
var latest = await db.MachineStudioVersions.SingleOrDefaultAsync(x => x.Guid == latestMachineStudioVersion.Guid);
db.MachineStudioVersions.Remove(latest);
await db.SaveChangesAsync();
}
}
public async Task DowngradePPCVersion(IWebAppBase app)
{
var latestPPCVersion = await GetLatestPPCVersion(app);
var dataSource = (await app.GetMachineServiceSettingsAsync()).ToDataSource();
OnProgress(AzureUtilsStage.Database, $"Removing PPC database entry for version '{latestPPCVersion.Version}'...");
using (var db = ObservablesContext.CreateDefault(dataSource))
{
var latest = await db.TangoVersions.SingleOrDefaultAsync(x => x.Guid == latestPPCVersion.Guid);
db.TangoVersions.Remove(latest);
await db.SaveChangesAsync();
}
}
public async Task GetLatestMachineStudioVersion(IWebAppBase app)
{
OnProgress(AzureUtilsStage.Database, $"Getting latest machine studio version on '{app.Name}'...");
MachineServiceSettings settings = null;
try
{
settings = await app.GetMachineServiceSettingsAsync();
}
catch (Exception ex)
{
throw new ArgumentException("Could not fetch machine service settings. Please check that all settings are available.", ex);
}
try
{
DataSource dataSource = settings.ToDataSource();
using (var db = ObservablesContext.CreateDefault(dataSource))
{
var versions = await db.MachineStudioVersions.ToListAsync();
var latest_machine_version = versions.OrderByDescending(x => Version.Parse(x.Version)).FirstOrDefault();
return latest_machine_version;
}
}
catch (Exception ex)
{
throw new InvalidDataException($"Could not retrieve '{app.Name}' latest Machine Studio version from database.", ex);
}
}
public async Task GetLatestPPCVersion(IWebAppBase app)
{
OnProgress(AzureUtilsStage.Database, $"Getting latest PPC version on '{app.Name}'...");
MachineServiceSettings settings = null;
try
{
settings = await app.GetMachineServiceSettingsAsync();
}
catch (Exception ex)
{
throw new ArgumentException("Could not fetch machine service settings. Please check that all settings are available.", ex);
}
try
{
DataSource dataSource = settings.ToDataSource();
using (var db = ObservablesContext.CreateDefault(dataSource))
{
var versions = await db.TangoVersions.ToListAsync();
var latest_version = versions.OrderByDescending(x => Version.Parse(x.Version)).FirstOrDefault();
return latest_version;
}
}
catch (Exception ex)
{
throw new InvalidDataException($"Could not retrieve '{app.Name}' latest PPC version from database.", ex);
}
}
public async Task GetLatestFSEVersion(IWebAppBase app)
{
OnProgress(AzureUtilsStage.Database, $"Getting latest FSE version on '{app.Name}'...");
MachineServiceSettings settings = null;
try
{
settings = await app.GetMachineServiceSettingsAsync();
}
catch (Exception ex)
{
throw new ArgumentException("Could not fetch machine service settings. Please check that all settings are available.", ex);
}
try
{
DataSource dataSource = settings.ToDataSource();
using (var db = ObservablesContext.CreateDefault(dataSource))
{
var versions = await db.FseVersions.ToListAsync();
var latest_version = versions.OrderByDescending(x => Version.Parse(x.Version)).FirstOrDefault();
return latest_version;
}
}
catch (Exception ex)
{
throw new InvalidDataException($"Could not retrieve '{app.Name}' latest FSE version from database.", ex);
}
}
public async Task ValidateMachineStudioDatabaseUpgrade(IWebAppBase sourceApp, IWebAppBase targetApp)
{
OnProgress(AzureUtilsStage.Validating, "Validating machine studio database upgrade...");
var sourceSettings = await sourceApp.GetMachineServiceSettingsAsync();
var targetSettings = await targetApp.GetMachineServiceSettingsAsync();
var latestSourceMachineStudioVersion = await GetLatestMachineStudioVersion(sourceApp);
//Check if there is any source machine studio version.
if (latestSourceMachineStudioVersion == null)
{
throw new ValidationException("Could not locate a Machine Studio version entry on the source database.");
}
var latestTargetMachineStudioVersion = await GetLatestMachineStudioVersion(targetApp);
//Check target latest machine studio version is older if there is any.
if (latestTargetMachineStudioVersion != null && Version.Parse(latestSourceMachineStudioVersion.Version) <= Version.Parse(latestTargetMachineStudioVersion.Version))
{
throw new ValidationException($"Machine Studio source version is '{latestSourceMachineStudioVersion.Version}' while target version is '{latestTargetMachineStudioVersion.Version}'.");
}
}
public async Task ValidatePPCDatabaseUpgrade(IWebAppBase sourceApp, IWebAppBase targetApp)
{
OnProgress(AzureUtilsStage.Validating, "Validating PPC database upgrade...");
var sourceSettings = await sourceApp.GetMachineServiceSettingsAsync();
var targetSettings = await targetApp.GetMachineServiceSettingsAsync();
var latestSourcePPCVersion = await GetLatestPPCVersion(sourceApp);
//Check if there is any source PPC version.
if (latestSourcePPCVersion == null)
{
throw new ValidationException("Could not locate a PPC version entry on the source database.");
}
var latestTargetPPCVersion = await GetLatestPPCVersion(targetApp);
//Check target latest PPC version is older if there is any.
if (latestTargetPPCVersion != null && Version.Parse(latestSourcePPCVersion.Version) <= Version.Parse(latestTargetPPCVersion.Version))
{
throw new ValidationException($"PPC source version is '{latestSourcePPCVersion.Version}' while target version is '{latestTargetPPCVersion.Version}'.");
}
}
public async Task ValidateFSEDatabaseUpgrade(IWebAppBase sourceApp, IWebAppBase targetApp)
{
OnProgress(AzureUtilsStage.Validating, "Validating FSE database upgrade...");
var sourceSettings = await sourceApp.GetMachineServiceSettingsAsync();
var targetSettings = await targetApp.GetMachineServiceSettingsAsync();
var latestSourceFSEVersion = await GetLatestFSEVersion(sourceApp);
//Check if there is any source FSE version.
if (latestSourceFSEVersion == null)
{
throw new ValidationException("Could not locate a FSE version entry on the source database.");
}
var latestTargetFSEVersion = await GetLatestFSEVersion(targetApp);
//Check target latest FSE version is older if there is any.
if (latestTargetFSEVersion != null && Version.Parse(latestSourceFSEVersion.Version) <= Version.Parse(latestTargetFSEVersion.Version))
{
throw new ValidationException($"FSE source version is '{latestSourceFSEVersion.Version}' while target version is '{latestTargetFSEVersion.Version}'.");
}
}
#endregion
}
}