Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose NFD version and managed identities #34

Merged
merged 5 commits into from
Jun 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/aosm/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ 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.
* `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
++++++
Expand Down
27 changes: 19 additions & 8 deletions src/aosm/azext_aosm/_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,12 @@
"nsdg_name": "Network Service Design Group Name. This is the collection of Network Service Design Versions. "
"Will be created if it does not exist.",
"nsd_version": "Version of the NSD to be created. This should be in the format A.B.C",
"network_function_definition_group_name": "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. "
"This can be created using the 'az aosm nfd' commands.",
"network_function_definition_group_name":
"Existing Network Function Definition Group Name. "
"This can be created using the 'az aosm nfd' commands.",
"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",
"network_function_type": "Type of nf in the definition. Valid values are 'cnf' or 'vnf'",
"helm_package_name": "Name of the Helm package",
Expand All @@ -66,7 +68,12 @@
"helm_depends_on":
"Names of the Helm packages this package depends on. "
"Leave as an empty array if no dependencies",
"source_registry_id": "Resource ID of the source acr registry from which to pull the image",
"image_name_parameter":
"The parameter name in the VM ARM template which specifies the name of the "
"image to use for the VM.",
"source_registry_id":
"Resource ID of the source acr registry from which to pull "
"the image",
}


Expand Down Expand Up @@ -98,7 +105,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
Expand Down Expand Up @@ -188,7 +196,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:
Expand Down Expand Up @@ -219,6 +228,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()

Expand Down Expand Up @@ -277,7 +287,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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
10 changes: 5 additions & 5 deletions src/aosm/azext_aosm/generate_nfd/templates/vnfdefinition.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 17 additions & 5 deletions src/aosm/azext_aosm/generate_nfd/vnf_nfd_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
"""
Expand Down Expand Up @@ -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 = (
Expand Down Expand Up @@ -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.
Expand All @@ -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)

Expand All @@ -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,
}

Expand Down
68 changes: 53 additions & 15 deletions src/aosm/azext_aosm/generate_nsd/nsd_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@
# --------------------------------------------------------------------------------------
"""Contains a class for generating NSDs and associated resources."""
import json
import copy
import os
import shutil
import tempfile
from typing import Dict
from functools import cached_property
from typing import Any, Dict, Optional

from jinja2 import Template
from knack.log import get_logger
from azext_aosm.vendored_sdks.models import NFVIType

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,
Expand Down Expand Up @@ -61,6 +62,8 @@ def __init__(self, api_clients: ApiClients, 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
nfdv = self._get_nfdv(config, api_clients)
Expand All @@ -69,7 +72,7 @@ def __init__(self, api_clients: ApiClients, config: NSConfiguration):
raise NotImplementedError(
"NFDV has no deploy parameters, cannot generate NSD."
)
self.deploy_parameters: str = nfdv.deploy_parameters
self.deploy_parameters: Optional[Dict[str, Any]] = json.loads(nfdv.deploy_parameters)

def _get_nfdv(
self, config: NSConfiguration, api_clients
Expand All @@ -90,13 +93,13 @@ def _get_nfdv(

def generate_nsd(self) -> 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")

# 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()
Expand All @@ -108,7 +111,45 @@ def generate_nsd(self) -> 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}

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}. "
"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}

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)
Expand All @@ -129,7 +170,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")

Expand All @@ -139,9 +180,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 = {
Expand All @@ -162,14 +201,13 @@ def write_nf_bicep(self) -> None:

bicep_deploymentValues = ""

deploy_parameters_dict = json.loads(self.deploy_parameters)
if "properties" not in deploy_parameters_dict:

if "properties" not in self.deploy_parameters:
raise ValueError(
f"NFDV in {self.config.network_function_definition_group_name} has "
"no properties within deployParameters"
)

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.
Expand All @@ -190,7 +228,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,
# Ideally we would use the network_function_type from reading the actual
Expand Down
13 changes: 11 additions & 2 deletions src/aosm/azext_aosm/generate_nsd/templates/nf_template.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ 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}}'

@description('The managed identity that should be used to create the NF.')
param managedIdentity string

param location string = '{{location}}'

param nfviType string = '{{nfvi_type}}'
Expand All @@ -30,11 +33,17 @@ 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'
networkFunctionDefinitionGroupName: networkFunctionDefinitionGroupName
networkFunctionDefinitionVersion: networkFunctionDefinitionVersion
networkFunctionDefinitionVersion: {{network_function_definition_version_parameter}}
networkFunctionDefinitionOfferingLocation: networkFunctionDefinitionOfferingLocation
nfviType: nfviType
nfviId: resourceGroupId
Expand Down