From 9a6804a1d7f2ccf326935b8fe65b970550f9f3dc Mon Sep 17 00:00:00 2001 From: Jamie Parsons Date: Wed, 21 Jun 2023 16:35:27 +0100 Subject: [PATCH 1/4] NFDV version exposed as a CGV on an SNS --- src/aosm/HISTORY.rst | 1 + src/aosm/azext_aosm/_configuration.py | 2 +- .../azext_aosm/generate_nsd/nsd_generator.py | 53 ++++++++++++++----- .../generate_nsd/templates/nf_template.bicep | 4 +- 4 files changed, 43 insertions(+), 17 deletions(-) diff --git a/src/aosm/HISTORY.rst b/src/aosm/HISTORY.rst index 93107646473..b386d73d720 100644 --- a/src/aosm/HISTORY.rst +++ b/src/aosm/HISTORY.rst @@ -6,6 +6,7 @@ Release History unreleased ++++++++++ * `az aosm nfd build` options `--order-params` and `--interactive` to help users choose which NF parameters to expose as deployParameters. Feature added that allows CNF value mappings file to be generated if none is supplied. +* NFDV version exposed as a CGV on an SNS. 0.2.0 ++++++ diff --git a/src/aosm/azext_aosm/_configuration.py b/src/aosm/azext_aosm/_configuration.py index a26a3af3a77..70bdbbc0104 100644 --- a/src/aosm/azext_aosm/_configuration.py +++ b/src/aosm/azext_aosm/_configuration.py @@ -55,7 +55,7 @@ "Exising Network Function Definition Group Name. " "This can be created using the 'az aosm nfd' commands.", "network_function_definition_version_name": - "Exising Network Function Definition Version Name. " + "Existing Network Function Definition Version Name. " "This can be created using the 'az aosm nfd' commands.", "network_function_definition_offering_location": "Offering location of the Network Function Definition", "helm_package_name": "Name of the Helm package", diff --git a/src/aosm/azext_aosm/generate_nsd/nsd_generator.py b/src/aosm/azext_aosm/generate_nsd/nsd_generator.py index 5651072eb84..f7fce914926 100644 --- a/src/aosm/azext_aosm/generate_nsd/nsd_generator.py +++ b/src/aosm/azext_aosm/generate_nsd/nsd_generator.py @@ -4,19 +4,17 @@ # -------------------------------------------------------------------------------------- """Contains a class for generating VNF NFDs and associated resources.""" import json -import logging +import copy import os import shutil import tempfile from functools import cached_property -from pathlib import Path from typing import Any, Dict, Optional from jinja2 import Template from knack.log import get_logger from azext_aosm._configuration import NSConfiguration -from azext_aosm.generate_nfd.nfd_generator_base import NFDGenerator from azext_aosm.util.constants import ( CONFIG_MAPPINGS, NF_DEFINITION_BICEP_FILE, @@ -51,20 +49,23 @@ def __init__(self, config: NSConfiguration): self.nsd_bicep_template_name = NSD_DEFINITION_BICEP_SOURCE_TEMPLATE self.nf_bicep_template_name = NF_TEMPLATE_BICEP_FILE self.nsd_bicep_output_name = NSD_DEFINITION_BICEP_FILE + self.nfdv_parameter_name = \ + f"{self.config.network_function_definition_group_name.replace('-', '_')}_nfd_version" self.build_folder_name = self.config.build_output_folder_name + self.deploy_parameters: Optional[Dict[str, Any]] = None def generate_nsd(self, deploy_parameters) -> None: """Generate a NSD templates which includes an Artifact Manifest, NFDV and NF templates.""" - logger.info(f"Generate NSD bicep templates") + logger.info("Generate NSD bicep templates") - self.deploy_parameters = deploy_parameters + self.deploy_parameters = json.loads(deploy_parameters) # Create temporary folder. with tempfile.TemporaryDirectory() as tmpdirname: self.tmp_folder_name = tmpdirname - self.create_parameter_files() + self.create_config_group_schema_files() self.write_nsd_manifest() self.write_nf_bicep() self.write_nsd_bicep() @@ -76,7 +77,34 @@ def generate_nsd(self, deploy_parameters) -> None: "`az aosm nsd publish` with the same arguments." ) - def create_parameter_files(self) -> None: + @cached_property + def config_group_schema_dict(self) -> Dict[str, Any]: + """ + :return: The Config Group Schema as a dictionary. + """ + # This function cannot be called before deployment parameters have been + # supplied. + assert self.deploy_parameters + + # Take a copy of the deploy parameters. + cgs_dict = copy.deepcopy(self.deploy_parameters) + + # Re-title it. + cgs_dict["title"] = self.config.cg_schema_name + + # Add in the NFDV version as a parameter. + description_string = ( + f"The version of the {self.config.network_function_definition_group_name} " + f"NFD to use. This version must be compatable with (have the same " + f"parameters exposed as) " + f"{self.config.network_function_definition_version_name}." + ) + cgs_dict["properties"][self.nfdv_parameter_name] = \ + {"type": "string", "description": description_string} + + return cgs_dict + + def create_config_group_schema_files(self) -> None: """Create the Schema and configMappings json files.""" temp_schemas_folder_path = os.path.join(self.tmp_folder_name, SCHEMAS) os.mkdir(temp_schemas_folder_path) @@ -97,7 +125,7 @@ def write_schema(self, folder_path: str) -> None: schema_path = os.path.join(folder_path, f"{self.config.cg_schema_name}.json") with open(schema_path, "w") as _file: - _file.write(json.dumps(json.loads(self.deploy_parameters), indent=4)) + _file.write(json.dumps(self.config_group_schema_dict, indent=4)) logger.debug(f"{schema_path} created") @@ -107,9 +135,7 @@ def write_config_mappings(self, folder_path: str) -> None: :param folder_path: The folder to put this file in. """ - - deploy_parameters_dict = json.loads(self.deploy_parameters) - deploy_properties = deploy_parameters_dict["properties"] + deploy_properties = self.config_group_schema_dict["properties"] logger.debug("Create configMappings.json") config_mappings = { @@ -130,8 +156,7 @@ def write_nf_bicep(self) -> None: bicep_deploymentValues = "" - deploy_parameters_dict = json.loads(self.deploy_parameters) - deploy_properties = deploy_parameters_dict["properties"] + deploy_properties = self.deploy_parameters["properties"] for key, value in deploy_properties.items(): # location is sometimes part of deploy_properties. @@ -149,7 +174,7 @@ def write_nf_bicep(self) -> None: "network_function_name": self.config.network_function_name, "publisher_name": self.config.publisher_name, "network_function_definition_group_name": self.config.network_function_definition_group_name, - "network_function_definition_version_name": self.config.network_function_definition_version_name, + "network_function_definition_version_parameter": self.nfdv_parameter_name, "network_function_definition_offering_location": self.config.network_function_definition_offering_location, "location": self.config.location, }, diff --git a/src/aosm/azext_aosm/generate_nsd/templates/nf_template.bicep b/src/aosm/azext_aosm/generate_nsd/templates/nf_template.bicep index d558ef3f316..101cb6851d0 100644 --- a/src/aosm/azext_aosm/generate_nsd/templates/nf_template.bicep +++ b/src/aosm/azext_aosm/generate_nsd/templates/nf_template.bicep @@ -10,7 +10,7 @@ param publisherName string = '{{publisher_name}}' param networkFunctionDefinitionGroupName string = '{{network_function_definition_group_name}}' @description('NFD version') -param networkFunctionDefinitionVersion string = '{{network_function_definition_version_name}}' +param {{network_function_definition_version_parameter}} string @description('Offering location for the Network Function') param networkFunctionDefinitionOfferingLocation string = '{{network_function_definition_offering_location}}' @@ -32,7 +32,7 @@ resource nf_resource 'Microsoft.HybridNetwork/networkFunctions@2023-04-01-previe publisherName: publisherName publisherScope: 'Private' networkFunctionDefinitionGroupName: networkFunctionDefinitionGroupName - networkFunctionDefinitionVersion: networkFunctionDefinitionVersion + networkFunctionDefinitionVersion: {{network_function_definition_version_parameter}} networkFunctionDefinitionOfferingLocation: networkFunctionDefinitionOfferingLocation nfviType: 'AzureCore' nfviId: resourceGroupId From 2d675d374b65dc7c87b24f685188d9d329de11af Mon Sep 17 00:00:00 2001 From: Jamie Parsons Date: Tue, 27 Jun 2023 12:45:38 +0100 Subject: [PATCH 2/4] Managed identities support on NFs --- src/aosm/azext_aosm/_configuration.py | 12 +++++++--- .../generate_nfd/vnf_nfd_generator.py | 22 ++++++++++++++----- .../azext_aosm/generate_nsd/nsd_generator.py | 11 ++++++++++ .../generate_nsd/templates/nf_template.bicep | 4 ++++ 4 files changed, 41 insertions(+), 8 deletions(-) diff --git a/src/aosm/azext_aosm/_configuration.py b/src/aosm/azext_aosm/_configuration.py index 70bdbbc0104..3b6e0e572aa 100644 --- a/src/aosm/azext_aosm/_configuration.py +++ b/src/aosm/azext_aosm/_configuration.py @@ -71,6 +71,8 @@ "helm_depends_on": "Names of the Helm packages this package depends on. " "Leave as an empty array if no dependencies", + "image_name_parameter": + "The parameter name in the VM ARM template which specifies the name of the image to use for the VM.", } @@ -102,7 +104,8 @@ def nfdg_name(self) -> str: @property def acr_manifest_name(self) -> str: """Return the ACR manifest name from the NFD name.""" - return f"{self.nf_name}-acr-manifest-{self.version.replace('.', '-')}" + sanitized_nf_name = self.nf_name.lower().replace("_", "-") + return f"{sanitized_nf_name}-acr-manifest-{self.version.replace('.', '-')}" @dataclass @@ -189,7 +192,8 @@ def network_function_name(self) -> str: @property def acr_manifest_name(self) -> str: """Return the ACR manifest name from the NFD name.""" - return f"{self.network_function_name.lower().replace('_', '-')}-acr-manifest-{self.nsd_version.replace('.', '-')}" + sanitised_nf_name = self.network_function_name.lower().replace('_', '-') + return f"{sanitised_nf_name}-acr-manifest-{self.nsd_version.replace('.', '-')}" @property def nfvi_site_name(self) -> str: @@ -220,6 +224,7 @@ def arm_template_artifact_name(self) -> str: @dataclass class VNFConfiguration(NFConfiguration): blob_artifact_store_name: str = DESCRIPTION_MAP["blob_artifact_store_name"] + image_name_parameter: str = DESCRIPTION_MAP["image_name_parameter"] arm_template: Any = ArtifactConfig() vhd: Any = ArtifactConfig() @@ -278,7 +283,8 @@ def validate(self) -> None: @property def sa_manifest_name(self) -> str: """Return the Storage account manifest name from the NFD name.""" - return f"{self.nf_name}-sa-manifest-{self.version.replace('.', '-')}" + sanitized_nf_name = self.nf_name.lower().replace("_", "-") + return f"{sanitized_nf_name}-sa-manifest-{self.version.replace('.', '-')}" @property def build_output_folder_name(self) -> str: diff --git a/src/aosm/azext_aosm/generate_nfd/vnf_nfd_generator.py b/src/aosm/azext_aosm/generate_nfd/vnf_nfd_generator.py index 7d33fab1016..e9581a6b9a1 100644 --- a/src/aosm/azext_aosm/generate_nfd/vnf_nfd_generator.py +++ b/src/aosm/azext_aosm/generate_nfd/vnf_nfd_generator.py @@ -78,6 +78,7 @@ def __init__(self, config: VNFConfiguration, order_params: bool, interactive: bo self.order_params = order_params self.interactive = interactive self.tmp_folder_name = "" + self.image_name = f"{self.config.nf_name}Image" def generate_nfd(self) -> None: """ @@ -179,6 +180,11 @@ def write_deployment_parameters(self, folder_path: str) -> None: ) for key in vm_parameters: + if key == self.config.image_name_parameter: + # There is only one correct answer for the image name, so don't ask the + # user, instead it is hardcoded in config mappings. + continue + # Order parameters into those without and then with defaults has_default_field = "defaultValue" in self.vm_parameters[key] has_default = ( @@ -240,7 +246,7 @@ def write_deployment_parameters(self, folder_path: str) -> None: f"{OPTIONAL_DEPLOYMENT_PARAMETERS_FILE} to help you choose which " "to expose." ) - + def write_template_parameters(self, folder_path: str) -> None: """ Write out the NFD templateParameters.json file. @@ -251,9 +257,15 @@ def write_template_parameters(self, folder_path: str) -> None: vm_parameters = ( self.vm_parameters_ordered if self.order_params else self.vm_parameters ) - template_parameters = { - key: f"{{deployParameters.{key}}}" for key in vm_parameters - } + + template_parameters = {} + + for key in vm_parameters: + if key == self.config.image_name_parameter: + template_parameters[key] = self.image_name + continue + + template_parameters[key] = f"{{deployParameters.{key}}}" template_parameters_path = os.path.join(folder_path, TEMPLATE_PARAMETERS) @@ -278,7 +290,7 @@ def write_vhd_parameters(self, folder_path: str) -> None: azureDeployLocation = self.config.location vhd_parameters = { - "imageName": f"{self.config.nf_name}Image", + "imageName": self.image_name, "azureDeployLocation": azureDeployLocation, } diff --git a/src/aosm/azext_aosm/generate_nsd/nsd_generator.py b/src/aosm/azext_aosm/generate_nsd/nsd_generator.py index f7fce914926..682e7f8077a 100644 --- a/src/aosm/azext_aosm/generate_nsd/nsd_generator.py +++ b/src/aosm/azext_aosm/generate_nsd/nsd_generator.py @@ -102,6 +102,17 @@ def config_group_schema_dict(self) -> Dict[str, Any]: cgs_dict["properties"][self.nfdv_parameter_name] = \ {"type": "string", "description": description_string} + managed_identity_description_string = ( + "The managed identity to use to deploy NFs within this SNS. This should " + "of the form '/subscriptions/{subscriptionId}/resourceGroups/" + "{resourceGroupName}/providers/Microsoft.ManagedIdentity/" + "userAssignedIdentities/{identityName}. " + "To instead use the System Assigned Managed Identity leave this as an " + "empty string." + ) + cgs_dict["properties"]["managedIdentity"] = \ + {"type": "string", "description": managed_identity_description_string} + return cgs_dict def create_config_group_schema_files(self) -> None: diff --git a/src/aosm/azext_aosm/generate_nsd/templates/nf_template.bicep b/src/aosm/azext_aosm/generate_nsd/templates/nf_template.bicep index 101cb6851d0..34b2eb16721 100644 --- a/src/aosm/azext_aosm/generate_nsd/templates/nf_template.bicep +++ b/src/aosm/azext_aosm/generate_nsd/templates/nf_template.bicep @@ -15,6 +15,9 @@ param {{network_function_definition_version_parameter}} string @description('Offering location for the Network Function') param networkFunctionDefinitionOfferingLocation string = '{{network_function_definition_offering_location}}' +@description('The managed identity that should be used to create the NF.') +param managedIdentity string + param location string = '{{location}}' param resourceGroupId string = resourceGroup().id @@ -36,6 +39,7 @@ resource nf_resource 'Microsoft.HybridNetwork/networkFunctions@2023-04-01-previe networkFunctionDefinitionOfferingLocation: networkFunctionDefinitionOfferingLocation nfviType: 'AzureCore' nfviId: resourceGroupId + identity: {managedIdentity: {}} allowSoftwareUpdate: true deploymentValues: string(deploymentValues) } From 7d267d655aec0f3327268a9dc168fab6f3c6a66f Mon Sep 17 00:00:00 2001 From: Sunny Carter Date: Tue, 27 Jun 2023 19:17:38 +0100 Subject: [PATCH 3/4] Fix identiy, fix API versions --- .../generate_nfd/templates/vnfartifactmanifests.bicep | 10 +++++----- .../generate_nfd/templates/vnfdefinition.bicep | 10 +++++----- src/aosm/azext_aosm/generate_nsd/nsd_generator.py | 4 ++-- .../generate_nsd/templates/nf_template.bicep | 7 ++++++- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/aosm/azext_aosm/generate_nfd/templates/vnfartifactmanifests.bicep b/src/aosm/azext_aosm/generate_nfd/templates/vnfartifactmanifests.bicep index 554e3bfa28d..bc884941987 100644 --- a/src/aosm/azext_aosm/generate_nfd/templates/vnfartifactmanifests.bicep +++ b/src/aosm/azext_aosm/generate_nfd/templates/vnfartifactmanifests.bicep @@ -20,24 +20,24 @@ param vhdVersion string param armTemplateVersion string // Created by the az aosm definition publish command before the template is deployed -resource publisher 'Microsoft.HybridNetwork/publishers@2022-09-01-preview' existing = { +resource publisher 'Microsoft.HybridNetwork/publishers@2023-04-01-preview' existing = { name: publisherName scope: resourceGroup() } // Created by the az aosm definition publish command before the template is deployed -resource acrArtifactStore 'Microsoft.HybridNetwork/publishers/artifactStores@2022-09-01-preview' existing = { +resource acrArtifactStore 'Microsoft.HybridNetwork/publishers/artifactStores@2023-04-01-preview' existing = { parent: publisher name: acrArtifactStoreName } // Created by the az aosm definition publish command before the template is deployed -resource saArtifactStore 'Microsoft.HybridNetwork/publishers/artifactStores@2022-09-01-preview' existing = { +resource saArtifactStore 'Microsoft.HybridNetwork/publishers/artifactStores@2023-04-01-preview' existing = { parent: publisher name: saArtifactStoreName } -resource saArtifactManifest 'Microsoft.Hybridnetwork/publishers/artifactStores/artifactManifests@2022-09-01-preview' = { +resource saArtifactManifest 'Microsoft.Hybridnetwork/publishers/artifactStores/artifactManifests@2023-04-01-preview' = { parent: saArtifactStore name: saManifestName location: location @@ -52,7 +52,7 @@ resource saArtifactManifest 'Microsoft.Hybridnetwork/publishers/artifactStores/a } } -resource acrArtifactManifest 'Microsoft.Hybridnetwork/publishers/artifactStores/artifactManifests@2022-09-01-preview' = { +resource acrArtifactManifest 'Microsoft.Hybridnetwork/publishers/artifactStores/artifactManifests@2023-04-01-preview' = { parent: acrArtifactStore name: acrManifestName location: location diff --git a/src/aosm/azext_aosm/generate_nfd/templates/vnfdefinition.bicep b/src/aosm/azext_aosm/generate_nfd/templates/vnfdefinition.bicep index 7f98fcf55be..0439097e8d0 100644 --- a/src/aosm/azext_aosm/generate_nfd/templates/vnfdefinition.bicep +++ b/src/aosm/azext_aosm/generate_nfd/templates/vnfdefinition.bicep @@ -20,30 +20,30 @@ param vhdVersion string param armTemplateVersion string // Created by the az aosm definition publish command before the template is deployed -resource publisher 'Microsoft.HybridNetwork/publishers@2022-09-01-preview' existing = { +resource publisher 'Microsoft.HybridNetwork/publishers@2023-04-01-preview' existing = { name: publisherName scope: resourceGroup() } // Created by the az aosm definition publish command before the template is deployed -resource acrArtifactStore 'Microsoft.HybridNetwork/publishers/artifactStores@2022-09-01-preview' existing = { +resource acrArtifactStore 'Microsoft.HybridNetwork/publishers/artifactStores@2023-04-01-preview' existing = { parent: publisher name: acrArtifactStoreName } // Created by the az aosm definition publish command before the template is deployed -resource saArtifactStore 'Microsoft.HybridNetwork/publishers/artifactStores@2022-09-01-preview' existing = { +resource saArtifactStore 'Microsoft.HybridNetwork/publishers/artifactStores@2023-04-01-preview' existing = { parent: publisher name: saArtifactStoreName } // Created by the az aosm definition publish command before the template is deployed -resource nfdg 'Microsoft.Hybridnetwork/publishers/networkfunctiondefinitiongroups@2022-09-01-preview' existing = { +resource nfdg 'Microsoft.Hybridnetwork/publishers/networkfunctiondefinitiongroups@2023-04-01-preview' existing = { parent: publisher name: nfDefinitionGroup } -resource nfdv 'Microsoft.Hybridnetwork/publishers/networkfunctiondefinitiongroups/networkfunctiondefinitionversions@2022-09-01-preview' = { +resource nfdv 'Microsoft.Hybridnetwork/publishers/networkfunctiondefinitiongroups/networkfunctiondefinitionversions@2023-04-01-preview' = { parent: nfdg name: nfDefinitionVersion location: location diff --git a/src/aosm/azext_aosm/generate_nsd/nsd_generator.py b/src/aosm/azext_aosm/generate_nsd/nsd_generator.py index 5b989c1a5ac..3f365d4cbb6 100644 --- a/src/aosm/azext_aosm/generate_nsd/nsd_generator.py +++ b/src/aosm/azext_aosm/generate_nsd/nsd_generator.py @@ -141,8 +141,8 @@ def config_group_schema_dict(self) -> Dict[str, Any]: "of the form '/subscriptions/{subscriptionId}/resourceGroups/" "{resourceGroupName}/providers/Microsoft.ManagedIdentity/" "userAssignedIdentities/{identityName}. " - "To instead use the System Assigned Managed Identity leave this as an " - "empty string." + "The az aosm tool only supports user assigned identities at present, " + "you cannot use a System Assigned identity." ) cgs_dict["properties"]["managedIdentity"] = \ {"type": "string", "description": managed_identity_description_string} diff --git a/src/aosm/azext_aosm/generate_nsd/templates/nf_template.bicep b/src/aosm/azext_aosm/generate_nsd/templates/nf_template.bicep index f745dca5183..89a2362e8ec 100644 --- a/src/aosm/azext_aosm/generate_nsd/templates/nf_template.bicep +++ b/src/aosm/azext_aosm/generate_nsd/templates/nf_template.bicep @@ -33,6 +33,12 @@ var deploymentValues = { resource nf_resource 'Microsoft.HybridNetwork/networkFunctions@2023-04-01-preview' = { name: '{{network_function_name}}' location: location + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${managedIdentity}': {} + } + } properties: { publisherName: publisherName publisherScope: 'Private' @@ -41,7 +47,6 @@ resource nf_resource 'Microsoft.HybridNetwork/networkFunctions@2023-04-01-previe networkFunctionDefinitionOfferingLocation: networkFunctionDefinitionOfferingLocation nfviType: nfviType nfviId: resourceGroupId - identity: {managedIdentity: {}} allowSoftwareUpdate: true deploymentValues: string(deploymentValues) } From 81ef54c193825ef21bf19458a1395c06404bd442 Mon Sep 17 00:00:00 2001 From: Sunny Carter Date: Tue, 27 Jun 2023 19:25:39 +0100 Subject: [PATCH 4/4] history --- src/aosm/HISTORY.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/src/aosm/HISTORY.rst b/src/aosm/HISTORY.rst index 1c5da029015..836fcf03607 100644 --- a/src/aosm/HISTORY.rst +++ b/src/aosm/HISTORY.rst @@ -8,6 +8,7 @@ unreleased * `az aosm nfd build` options `--order-params` and `--interactive` to help users choose which NF parameters to expose as deployParameters. Feature added that allows CNF value mappings file to be generated if none is supplied. * NFDV version exposed as a CGV on an SNS. * `az aosm nfd publish` option added for `--definition-type cnf` to publish the CNF bicep templates, upload helm charts from disk to the ACR and copy the images from a source ACR to the target ACR. +* Managed Identity added to VNF NF templates - requires subscription to be registered for the feature flag. 0.2.0 ++++++