Webapp/SDAF/wwwroot/js/site.js (712 lines of code) (raw):

// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. // ================ // VALUE RETAINMENT // ================ var model; var azureResourceIds = [ "location", "subscription", "workload_zone", "resourcegroup_arm_id", "network_arm_id", "admin_subnet_arm_id", "db_subnet_arm_id", "app_subnet_arm_id", "web_subnet_arm_id", "iscsi_subnet_arm_id", "anf_subnet_arm_id", "admin_subnet_nsg_arm_id", "db_subnet_nsg_arm_id", "app_subnet_nsg_arm_id", "web_subnet_nsg_arm_id", "iscsi_subnet_nsg_arm_id", "anf_subnet_nsg_arm_id", "diagnostics_storage_account_arm_id", "witness_storage_account_arm_id", "transport_storage_account_id", "transport_private_endpoint_id", "install_storage_account_id", "azure_files_sapmnt_id", "install_private_endpoint_id", "user_keyvault_id", "automation_keyvault_id", "spn_keyvault_id", "proximityplacementgroup_arm_ids", "database_vm_avset_arm_ids", "application_server_vm_avset_arm_ids", "sapmnt_private_endpoint_id", "user_keyvault_id", "spn_keyvault_id" ]; var hanadb_sizes = [ { "text": "Default", "value": "Default" }, { "text": "Custom", "value": "Custom" }, { "text": "S4Demo", "value": "S4Demo" }, { "text": "Standard E20ds v4", "value": "E20ds_v4" }, { "text": "Standard E20ds v5", "value": "E20ds_v5" }, { "text": "Standard E32ds v4", "value": "E32ds_v4" }, { "text": "Standard E32ds v5", "value": "E32ds_v5" }, { "text": "Standard E48ds v4", "value": "E48ds_v4" }, { "text": "Standard E48ds v5", "value": "E48ds_v5" }, { "text": "Standard E64s v3", "value": "E64s_v3" }, { "text": "Standard E64s v4", "value": "E64s_v4" }, { "text": "Standard E64s v5", "value": "E64s_v5" }, { "text": "Standard E96s v5", "value": "E96s_v5" }, { "text": "Standard M32ts", "value": "M32ts" }, { "text": "Standard M32ls", "value": "M32ls" }, { "text": "Standard M64ls", "value": "M64ls" }, { "text": "Standard M64s", "value": "M64s" }, { "text": "Standard M64ms", "value": "M64ms" }, { "text": "Standard M128s", "value": "M128s" }, { "text": "Standard M128ms", "value": "M128ms" }, { "text": "Standard M208s_v2", "value": "M208s_v2" }, { "text": "Standard M208ms_v2", "value": "M208ms_v2" }, { "text": "Standard M416s_v2", "value": "M416s_v2" }, { "text": "Standard M416ms_v2", "value": "M416ms_v2" } ]; var anydb_sizes = [ { "text": "Default", "value": "Default" }, { "text": "256 GB", "value": "256" }, { "text": "512 GB", "value": "512" }, { "text": "1 TB", "value": "1024" }, { "text": "2 TB", "value": "2048" }, { "text": "5 TB", "value": "5120" }, { "text": "10 TB", "value": "10240" }, { "text": "15 TB", "value": "15360" }, { "text": "20 TB", "value": "20480" }, { "text": "30 TB", "value": "30720" }, { "text": "40 TB", "value": "40960" }, { "text": "50 TB", "value": "51200" } ]; // initializes the model variable representing a system or landscape function createModel(object) { model = object; } // updates the existing model and form with values from new object function updateModel(object) { for (var prop in object) { if (object[prop]) { model[prop] = object[prop]; setCurrentValue(prop); } } } // populate azure resource dropdowns and set to current value // retain all other form values selected for editing or on submit error // remove the loading background and access the form function retainFormValues() { // default to only show basic parameters $("#advanced-filter").prop("checked", false); $("#advanced-filter").trigger("change"); $("#expert-filter").prop("checked", false); $("#expert-filter").trigger("change"); var dropdownsAffected = [ { ids: ["location"], controller: "/Armclient/GetLocationOptions", errorMessage: "Error retrieving locations", input: {} }, { ids: ["subscription"], controller: "/Armclient/GetSubscriptionOptions", errorMessage: "Error retrieving subscriptions", input: {} }, { ids: ["workload_zone"], controller: "/Landscape/GetWorkloadZones", errorMessage: "Error retrieving existing workload zones", input: {} } ]; Promise.all(dropdownsAffected.map(updateAndSetDropdowns)).then(function () { for (var prop in model) { if (model[prop] != null) { setCurrentValue(prop); } } document.getElementById('loading-background').style.visibility = "hidden"; }).catch(function () { for (var prop in model) { if (model[prop] != null) { setCurrentValue(prop); } } document.getElementById('loading-background').style.visibility = "hidden"; }); } // gets azure resource data from a controller, populates dropdowns, and sets the current value function updateAndSetDropdowns(dropdown) { return new Promise(function (resolve, reject) { resetDropdowns(dropdown.ids); $.ajax({ type: "GET", url: dropdown.controller, data: dropdown.input, success: function (data) { dropdown.ids.forEach(function (id) { populateAzureDropdownData(id, data); setCurrentValue(id); }); resolve(); }, error: function () { errorFunc(dropdown.ids, dropdown.errorMessage); reject(); } }); }); } // populate environment dropdown with values from ADO if pipeline deployment function getEnvironmentsFromAdo(isPipelineDeployment) { var id = "environment"; if (isPipelineDeployment) { $.ajax({ type: "GET", url: "/Environment/GetEnvironments", data: {}, success: function (data) { resetDropdowns([id]); populateAzureDropdownData(id, data); setCurrentValue(id); }, error: function () { alert("Error retrieving existing environments from ADO"); } }); } } // populate a dropdown with data function populateAzureDropdownData(id, data) { for (i = 0; i < data.length; i++) { $("#" + id).append($("<option />").val(data[i].value).text(data[i].text)); } } // set the current value for a parameter function setCurrentValue(id) { var currentValue = model[id]; if (typeof currentValue == "boolean") { $("#" + id).prop('checked', currentValue); $("#" + id).trigger('change'); } else if (currentValue) { if (Array.isArray(currentValue)) { populateListData(id, currentValue); } else if (azureResourceIds.indexOf(id) > 0) { populateListData(id, [currentValue]); } $("#" + id).val(currentValue); if (id != "workload_zone") { $("#" + id).trigger('change'); } } } // populate an input parameter that accepts lists as data function populateListData(id, data) { for (j = 0; j < data.length; j++) { if ($("#" + id + " option[value='" + data[j] + "']").length <= 0) { $("#" + id).append($("<option />").val(data[j]).text(data[j])); } } } // to be run on unsuccessful ajax call function errorFunc(idsAffected, errorMessage) { resetDropdowns(idsAffected); alert(errorMessage); } // clear all values from dropdown function resetDropdowns(ids) { ids.forEach(function (id) { if ($("#" + id).val()) { $("#" + id).val(null).trigger('change'); } $("#" + id).empty(); }); } // set the subscription and virtual network to correct value based on one of the existing arm ids // required for the case that a user inputs valid arm ids without choosing a subscription or vnet and later wishes to edit function setDataFromResource() { if (model["subscription"] == null || model["network_arm_id"] == null) { var alreadySetArmId = null; for (i = 3; i < azureResourceIds.length; i++) { var currArmId = azureResourceIds[i]; if (model[currArmId] != null) { alreadySetArmId = model[currArmId]; break; } } if (alreadySetArmId != null) { if (model["subscription"] == null) { $.ajax({ type: "GET", url: "/Armclient/GetSubscriptionFromResource", data: { resourceId: alreadySetArmId }, success: function (data) { model["subscription"] = data; }, error: function () { console.log("Couldn't get subscription from any existing resources"); } }); } if (model["network_arm_id"] == null) { $.ajax({ type: "GET", url: "/Armclient/GetVnetFromResource", data: { resourceId: alreadySetArmId }, success: function (data) { model["network_arm_id"] = data; }, error: function () { console.log("Couldn't get network_arm_id from any existing resources"); } }); } } } } function updateImage(source, target) { var sourceVal = $(source).val(); if (sourceVal) { var confirmMessage = "Are you sure? Selecting this value will populate values for the parameter " + target; if (confirm(confirmMessage)) { $.ajax({ type: "GET", url: "/System/GetImage", data: { name: sourceVal }, success: function (data) { $("#" + target + "_os_type").val(data["os_type"]); $("#" + target + "_source_image_id").val(data["source_image_id"]); $("#" + target + "_publisher").val(data["publisher"]); $("#" + target + "_offer").val(data["offer"]); $("#" + target + "_sku").val(data["sku"]); $("#" + target + "_version").val(data["version"]); $("#" + target + "_type").val(data["type"]); }, error: function () { alert("Error getting image details"); } }); } else { $(source).val(null).trigger('change'); } } } function addTag(param) { var parentDiv = $("#" + param + "-tags-container"); var numTags = $("#" + param + "-tags-container input").length / 2; parentDiv.append('<div class="tag"></div>'); var lastTagContainer = parentDiv.children().last(); lastTagContainer.append('<div class="tag-key"></div>'); lastTagContainer.children().last().append('<label class="ms-Label tags-label" for="' + param + '_' + numTags + '__Key">Key</label>'); lastTagContainer.children().last().append('<input class="ms-TextField-field" id="' + param + '_' + numTags + '__Key" name="' + param + '[' + numTags + '].Key" type="text" value="">'); lastTagContainer.append('<div class="tag-value"></div>'); lastTagContainer.children().last().append('<label class="ms-Label tags-label" for="' + param + '_' + numTags + '__Value">Value</label>'); lastTagContainer.children().last().append('<input class="ms-TextField-field" id="' + param + '_' + numTags + '__Value" name="' + param + '[' + numTags + '].Value" type="text" value="">'); } function populateLocations(id, value) { $.ajax({ type: "GET", url: "/Armclient/GetLocationOptions", data: { useRegionMapping: true }, success: function (data) { populateAzureDropdownData(id, data); $("#" + id).val(value); $("#" + id).trigger('change'); }, error: function () { errorFunc([id], "Error retrieving locations"); } }); } // =============== // EVENT LISTENERS // =============== $("#subscription_id").on("change", function () { var subscriptionid = $(this).val(); var dropdownsAffected = [ { ids: ["resourcegroup_arm_id"], controller: "/Armclient/GetResourceGroupOptions", errorMessage: "Error retrieving resource groups for specified subscription", input: { subscriptionId: subscriptionid } }, { ids: ["network_arm_id"], controller: "/Armclient/GetVNetOptions", errorMessage: "Error retrieving vnets for specified subscription", input: { subscriptionId: subscriptionid } }, { ids: ["diagnostics_storage_account_arm_id", "witness_storage_account_arm_id", "transport_storage_account_id", "install_storage_account_id", "azure_files_sapmnt_id", "hanashared_id" ], controller: "/Armclient/GetStorageAccountOptions", errorMessage: "Error retrieving storage accounts for specified subscription", input: { subscriptionId: subscriptionid } }, { ids: ["transport_private_endpoint_id", "install_private_endpoint_id", "sapmnt_private_endpoint_id", "hanashared_private_endpoint_id" ], controller: "/Armclient/GetPrivateEndpointOptions", errorMessage: "Error retrieving private endpoints for specified subscription", input: { subscriptionId: subscriptionid } }, { ids: ["user_keyvault_id", "spn_keyvault_id", "automation_keyvault_id" ], controller: "/Armclient/GetKeyvaultOptions", errorMessage: "Error retrieving keyvaults for specified subscription", input: { subscriptionId: subscriptionid } }, { ids: ["proximityplacementgroup_arm_ids", "app_proximityplacementgroup_arm_ids" ], controller: "/Armclient/GetPPGroupOptions", errorMessage: "Error retrieving proximity placement groups for specified subscription", input: { subscriptionId: subscriptionid } }, { ids: ["database_vm_avset_arm_ids", "application_server_vm_avset_arm_ids" ], controller: "/Armclient/GetAvSetOptions", errorMessage: "Error retrieving availability sets for specified subscription", input: { subscriptionId: subscriptionid } }, { ids: ["user_assigned_identity_id"], controller: "/Armclient/GetUserAssignedIdentityOptions", errorMessage: "Error retrieving user assigned identities for specified subscription", input: { subscriptionId: subscriptionid } }, { ids: ["scaleset_id"], controller: "/Armclient/GetVMSSOptions", errorMessage: "Error retrieving Virtual machine scalesets for specified subscription", input: { subscriptionId: subscriptionid } } ]; if (subscriptionid) { Promise.all(dropdownsAffected.map(updateAndSetDropdowns)); } else { dropdownsAffected.map(({ ids }) => { resetDropdowns(ids) }); } }); $("#network_arm_id").on("change", function () { var vnetid = $(this).val(); var dropdownsAffected = [ { controller: "/Armclient/GetSubnetOptions", ids: [ "admin_subnet_arm_id", "anf_subnet_arm_id", "app_subnet_arm_id", "db_subnet_arm_id", "iscsi_subnet_arm_id", "storage_subnet_arm_id", "web_subnet_arm_id" ], errorMessage: "Error retrieving subnets for specified vnet", input: { vnetId: vnetid } }, { controller: "/Armclient/GetNsgOptions", ids: [ "admin_subnet_nsg_arm_id", "anf_subnet_nsg_arm_id", "app_subnet_nsg_arm_id", "db_subnet_nsg_arm_id", "iscsi_subnet_nsg_arm_id", "storage_subnet_nsg_arm_id", "web_subnet_nsg_arm_id" ], errorMessage: "Error retrieving network security groups for specified vnet's resource group", input: { vnetId: vnetid } } ]; if (vnetid) { Promise.all(dropdownsAffected.map(updateAndSetDropdowns)); } else { dropdownsAffected.map(({ ids }) => { resetDropdowns(ids) }); } }); $("#workload_zone").on("change", function () { var workloadzoneid = $(this).val(); if (workloadzoneid) { var confirmMessage = "Are you sure? Selecting this value will populate certain inputs with values from the workload zone: " + workloadzoneid; if (confirm(confirmMessage)) { $.ajax({ type: "GET", url: "/Landscape/GetByIdJson", data: { id: workloadzoneid, }, success: function (data) { entireLandscape = JSON.parse(data); partialLandscape = {}; partialLandscape["location"] = entireLandscape["location"]; partialLandscape["environment"] = entireLandscape["environment"]; partialLandscape["network_logical_name"] = entireLandscape["network_logical_name"]; updateModel(partialLandscape); }, error: function () { alert("Error populating data for given workload zone"); } }); } else { $("#workload_zone").val(null); } } }); $("#database_platform").on("change", function () { var platform = $(this).val(); if (platform == "" || platform == "NONE") { $("#database_size").val(null); $("#database_size").empty(); } else if (platform == "HANA") { $("#database_size").empty(); populateAzureDropdownData("database_size", hanadb_sizes); } else { $("#database_size").empty(); populateAzureDropdownData("database_size", anydb_sizes); } }); // ======================== // TOGGLE FUNCTIONS // ======================== // Hide and show basic, advanced, expert views function toggleParams(checkbox, displayNum) { var displayClass = (displayNum == 1) ? ".basic-parameter" : ((displayNum == 2) ? ".advanced-parameter" : ".expert-parameter"); if (checkbox.checked) { $(displayClass).show(); $(".grouping").has("div" + displayClass).show(); } else { $(displayClass).hide(); var matchingHeaders = $(".grouping").has("div.parameters > div:visible"); $(".grouping").hide(); matchingHeaders.show(); } } function toggleDefault(checkbox, id, modelType) { var modelDisplayText = (modelType == "Landscape") ? "workload zone." : "system." if (checkbox.checked) { var confirmMessage = "Are you sure? Selecting this value will populate certain inputs with values from the default " + modelDisplayText; if (confirm(confirmMessage)) { $.ajax({ type: "GET", url: "/" + modelType + "/GetDefaultJson", data: {}, success: function (data) { data = JSON.parse(data); data['IsDefault'] = false; data['sid'] = null; updateModel(data); }, error: function () { alert("Error populating data using the default " + modelDisplayText); } }); } else { $("#" + id).prop('checked', false); } } } function toggleDisableViaCheckbox(checkbox, id) { if (checkbox.checked) { $("#" + id).prop('disabled', false); } else { $("#" + id).prop('disabled', true); } } function toggleDisableViaNInputs(inputList, id) { for (i = 0; i < inputList.length; i++) { var input = inputList[i]; if ($("#" + input).val()) continue; else break; } if (i == inputList.length) { $("#" + id).prop('disabled', false); } else { $("#" + id).prop('checked', false); $("#" + id).prop('disabled', true); } } function toggleNullParameters() { $(".is-null-value").toggle(); } // Make header sticky on create and edit views function stickifyHeader(formHeader, sticky) { var currPosition = $(window).scrollTop(); if (currPosition > sticky) { formHeader.addClass("sticky"); $(".filter-checkboxes").css("text-align", "left"); $("#checkbox-and-searchbar").css("padding-left", "10px"); } else { formHeader.removeClass("sticky"); $(".filter-checkboxes").css("text-align", "center"); $("#checkbox-and-searchbar").css("padding-left", "0px"); } } // Disables an input when an overriding parameter has a value. Erases the pre-entered value if any function overrulesHandler(source, target) { if (target == null || target.Length == 0 || source == null || source.length == 0) { return ""; } var sourceVal = ""; if (source.options[source.selectedIndex] != null) { sourceVal = (source.nodeName == "SELECT") ? source.options[source.selectedIndex].value : source.value; } if (sourceVal.length > 0) { target.disabled = true; $("#" + target.id).val(null).trigger('change'); } else { target.disabled = false; } } // ==================== // FILTER PARAMETERS // ==================== function filterResults(searchText) { if (searchText == "") { $(".grouping").show(); $(".ms-TextField").show(); $("#basic-filter").trigger("change"); $("#advanced-filter").trigger("change"); $("#expert-filter").trigger("change"); } else { $(".grouping").hide(); $(".ms-TextField").hide(); var matchingHeaders = $(".grouping").has("h2:Contains(" + searchText + ")"); matchingHeaders.show(); matchingHeaders.find($(".ms-TextField")).show(); $(".grouping").has("label:Contains(" + searchText + ")").show(); $(".ms-TextField").has("label:Contains(" + searchText + ")").show(); } }; // Case insensitive contains jQuery.expr[':'].Contains = function (a, i, m) { return jQuery(a).text().toUpperCase().indexOf(m[3].toUpperCase()) >= 0; };