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

Image value mappings fixes #114

Merged
merged 7 commits into from
Oct 19, 2023
Merged
Show file tree
Hide file tree
Changes from 6 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
125 changes: 100 additions & 25 deletions src/aosm/azext_aosm/_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@
from pathlib import Path
from typing import Any, Dict, List, Optional, Union

from azure.cli.core.azclierror import InvalidArgumentValueError, ValidationError
from azure.cli.core.azclierror import (
InvalidArgumentValueError,
ValidationError,
)
from azext_aosm.util.constants import (
CNF,
NF_DEFINITION_OUTPUT_BICEP_PREFIX,
Expand Down Expand Up @@ -59,9 +62,51 @@ def validate(self):
if not self.version:
raise ValidationError("version must be set.")
if self.blob_sas_url and self.file_path:
raise ValidationError("Only one of file_path or blob_sas_url may be set.")
raise ValidationError(
"Only one of file_path or blob_sas_url may be set."
)
if not (self.blob_sas_url or self.file_path):
raise ValidationError("One of file_path or sas_blob_url must be set.")
raise ValidationError(
"One of file_path or sas_blob_url must be set."
)


@dataclass
class VhdArtifactConfig(ArtifactConfig):
image_disk_size_GB: Optional[Union[str, int]] = None
image_hyper_v_generation: Optional[str] = None
image_api_version: Optional[str] = None

def __post_init__(self):
"""
Convert parameters to the correct types.
"""
if (
isinstance(self.image_disk_size_GB, str)
and self.image_disk_size_GB.isdigit()
):
self.image_disk_size_GB = int(self.image_disk_size_GB)

@classmethod
def helptext(cls) -> "VhdArtifactConfig":
"""
Build an object where each value is helptext for that field.
"""
return VhdArtifactConfig(
image_disk_size_GB=(
"Optional. Specifies the size of empty data disks in gigabytes. "
"This value cannot be larger than 1023 GB."
),
image_hyper_v_generation=(
"Optional. Specifies the HyperVGenerationType of the VirtualMachine "
"created from the image. Valid values are V1 and V2. V1 is the default if "
"not specified."
),
image_api_version=(
"Optional. The ARM API version used to create the Microsoft.Compute/images resource."
),
**asdict(ArtifactConfig.helptext()),
)


@dataclass
Expand All @@ -78,7 +123,9 @@ def __post_init__(self):
"""
if self.publisher_name:
if not self.publisher_resource_group_name:
self.publisher_resource_group_name = f"{self.publisher_name}-rg"
self.publisher_resource_group_name = (
f"{self.publisher_name}-rg"
)
if not self.acr_artifact_store_name:
self.acr_artifact_store_name = f"{self.publisher_name}-acr"

Expand Down Expand Up @@ -197,15 +244,17 @@ def acr_manifest_names(self) -> List[str]:
can be multiple ACR manifests.
"""
sanitized_nf_name = self.nf_name.lower().replace("_", "-")
return [f"{sanitized_nf_name}-acr-manifest-{self.version.replace('.', '-')}"]
return [
f"{sanitized_nf_name}-acr-manifest-{self.version.replace('.', '-')}"
]


@dataclass
class VNFConfiguration(NFConfiguration):
blob_artifact_store_name: str = ""
image_name_parameter: str = ""
arm_template: Union[Dict[str, str], ArtifactConfig] = ArtifactConfig()
vhd: Union[Dict[str, str], ArtifactConfig] = ArtifactConfig()
vhd: Union[Dict[str, str], VhdArtifactConfig] = VhdArtifactConfig()

@classmethod
def helptext(cls) -> "VNFConfiguration":
Expand All @@ -222,7 +271,7 @@ def helptext(cls) -> "VNFConfiguration":
"image to use for the VM."
),
arm_template=ArtifactConfig.helptext(),
vhd=ArtifactConfig.helptext(),
vhd=VhdArtifactConfig.helptext(),
**asdict(NFConfiguration.helptext()),
)

Expand All @@ -244,8 +293,10 @@ def __post_init__(self):

if isinstance(self.vhd, dict):
if self.vhd.get("file_path"):
self.vhd["file_path"] = self.path_from_cli_dir(self.vhd["file_path"])
self.vhd = ArtifactConfig(**self.vhd)
self.vhd["file_path"] = self.path_from_cli_dir(
self.vhd["file_path"]
)
self.vhd = VhdArtifactConfig(**self.vhd)

def validate(self) -> None:
"""
Expand All @@ -255,7 +306,7 @@ def validate(self) -> None:
"""
super().validate()

assert isinstance(self.vhd, ArtifactConfig)
assert isinstance(self.vhd, VhdArtifactConfig)
assert isinstance(self.arm_template, ArtifactConfig)
self.vhd.validate()
self.arm_template.validate()
Expand All @@ -268,7 +319,10 @@ def validate(self) -> None:
"Config validation error. VHD artifact version should be in format"
" A-B-C"
)
if "." not in self.arm_template.version or "-" in self.arm_template.version:
if (
"." not in self.arm_template.version
or "-" in self.arm_template.version
):
raise ValidationError(
"Config validation error. ARM template artifact version should be in"
" format A.B.C"
Expand All @@ -278,7 +332,9 @@ def validate(self) -> None:
def sa_manifest_name(self) -> str:
"""Return the Storage account manifest name from the NFD name."""
sanitized_nf_name = self.nf_name.lower().replace("_", "-")
return f"{sanitized_nf_name}-sa-manifest-{self.version.replace('.', '-')}"
return (
f"{sanitized_nf_name}-sa-manifest-{self.version.replace('.', '-')}"
)

@property
def output_directory_for_build(self) -> Path:
Expand Down Expand Up @@ -420,7 +476,9 @@ def __post_init__(self):
package["path_to_mappings"] = self.path_from_cli_dir(
package["path_to_mappings"]
)
self.helm_packages[package_index] = HelmPackageConfig(**dict(package))
self.helm_packages[package_index] = HelmPackageConfig(
**dict(package)
)
if isinstance(self.images, dict):
self.images = CNFImageConfig(**self.images)

Expand Down Expand Up @@ -513,10 +571,14 @@ def validate(self) -> None:
:raises ValidationError for any invalid config
"""
if not self.name:
raise ValidationError("Network function definition name must be set")
raise ValidationError(
"Network function definition name must be set"
)

if not self.publisher:
raise ValidationError(f"Publisher name must be set for {self.name}")
raise ValidationError(
f"Publisher name must be set for {self.name}"
)

if not self.publisher_resource_group:
raise ValidationError(
Expand All @@ -540,7 +602,9 @@ def validate(self) -> None:

# Temporary validation while only private publishers exist
if self.publisher_scope not in ["private", "Private"]:
raise ValidationError("Only private publishers are currently supported")
raise ValidationError(
"Only private publishers are currently supported"
)

if self.type not in [CNF, VNF]:
raise ValueError(
Expand Down Expand Up @@ -600,19 +664,22 @@ def acr_manifest_name(self, nsd_version: str) -> str:

@dataclass
class NSConfiguration(Configuration):
network_functions: List[Union[NFDRETConfiguration, Dict[str, Any]]] = field(
default_factory=lambda: []
)
network_functions: List[
Union[NFDRETConfiguration, Dict[str, Any]]
] = field(default_factory=lambda: [])
nsd_name: str = ""
nsd_version: str = ""
nsdv_description: str = ""

def __post_init__(self):
"""Covert things to the correct format."""
super().__post_init__()
if self.network_functions and isinstance(self.network_functions[0], dict):
if self.network_functions and isinstance(
self.network_functions[0], dict
):
nf_ret_list = [
NFDRETConfiguration(**config) for config in self.network_functions
NFDRETConfiguration(**config)
for config in self.network_functions
]
self.network_functions = nf_ret_list

Expand Down Expand Up @@ -644,7 +711,9 @@ def validate(self):
"""
super().validate()
if not self.network_functions:
raise ValueError(("At least one network function must be included."))
raise ValueError(
("At least one network function must be included.")
)

for configuration in self.network_functions:
configuration.validate()
Expand Down Expand Up @@ -681,7 +750,9 @@ def acr_manifest_names(self) -> List[str]:
return acr_manifest_names


def get_configuration(configuration_type: str, config_file: str) -> Configuration:
def get_configuration(
configuration_type: str, config_file: str
) -> Configuration:
"""
Return the correct configuration object based on the type.

Expand All @@ -700,9 +771,13 @@ def get_configuration(configuration_type: str, config_file: str) -> Configuratio
config: Configuration
try:
if configuration_type == VNF:
config = VNFConfiguration(config_file=config_file, **config_as_dict)
config = VNFConfiguration(
config_file=config_file, **config_as_dict
)
elif configuration_type == CNF:
config = CNFConfiguration(config_file=config_file, **config_as_dict)
config = CNFConfiguration(
config_file=config_file, **config_as_dict
)
elif configuration_type == NSD:
config = NSConfiguration(config_file=config_file, **config_as_dict)
else:
Expand Down
45 changes: 27 additions & 18 deletions src/aosm/azext_aosm/generate_nfd/vnf_nfd_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from azext_aosm.util.constants import (
CONFIG_MAPPINGS_DIR_NAME,
DEPLOYMENT_PARAMETERS_FILENAME,
EXTRA_VHD_PARAMETERS,
OPTIONAL_DEPLOYMENT_PARAMETERS_FILENAME,
OPTIONAL_DEPLOYMENT_PARAMETERS_HEADING,
SCHEMA_PREFIX,
Expand All @@ -27,7 +28,7 @@
VNF_DEFINITION_BICEP_TEMPLATE_FILENAME,
VNF_MANIFEST_BICEP_TEMPLATE_FILENAME,
)
from azext_aosm.util.utils import input_ack
from azext_aosm.util.utils import input_ack, snake_case_to_camel_case

logger = get_logger(__name__)

Expand Down Expand Up @@ -61,7 +62,9 @@ class VnfNfdGenerator(NFDGenerator):
exposed.
"""

def __init__(self, config: VNFConfiguration, order_params: bool, interactive: bool):
def __init__(
self, config: VNFConfiguration, order_params: bool, interactive: bool
):
self.config = config

assert isinstance(self.config.arm_template, ArtifactConfig)
Expand Down Expand Up @@ -93,7 +96,9 @@ def generate_nfd(self) -> None:

self._create_parameter_files()
self._copy_to_output_directory()
print(f"Generated NFD bicep templates created in {self.output_directory}")
print(
f"Generated NFD bicep templates created in {self.output_directory}"
)
print(
"Please review these templates. When you are happy with them run "
"`az aosm nfd publish` with the same arguments."
Expand Down Expand Up @@ -141,7 +146,8 @@ def vm_parameters_ordered(self) -> Dict[str, Any]:
# Order parameters into those with and without defaults
has_default_field = "defaultValue" in self.vm_parameters[key]
has_default = (
has_default_field and not self.vm_parameters[key]["defaultValue"] == ""
has_default_field
and not self.vm_parameters[key]["defaultValue"] == ""
)

if has_default:
Expand Down Expand Up @@ -176,7 +182,9 @@ def write_deployment_parameters(self, directory: Path) -> None:
vm_parameters_to_exclude = []

vm_parameters = (
self.vm_parameters_ordered if self.order_params else self.vm_parameters
self.vm_parameters_ordered
if self.order_params
else self.vm_parameters
)

for key in vm_parameters:
Expand All @@ -188,7 +196,8 @@ def write_deployment_parameters(self, directory: Path) -> None:
# Order parameters into those without and then with defaults
has_default_field = "defaultValue" in self.vm_parameters[key]
has_default = (
has_default_field and not self.vm_parameters[key]["defaultValue"] == ""
has_default_field
and not self.vm_parameters[key]["defaultValue"] == ""
)

if self.interactive and has_default:
Expand Down Expand Up @@ -240,7 +249,9 @@ def write_deployment_parameters(self, directory: Path) -> None:
optional_deployment_parameters_path, "w", encoding="utf-8"
) as _file:
_file.write(OPTIONAL_DEPLOYMENT_PARAMETERS_HEADING)
_file.write(json.dumps(nfd_parameters_with_default, indent=4))
_file.write(
json.dumps(nfd_parameters_with_default, indent=4)
)
print(
"Optional ARM parameters detected. Created "
f"{OPTIONAL_DEPLOYMENT_PARAMETERS_FILENAME} to help you choose which "
Expand All @@ -255,7 +266,9 @@ def write_template_parameters(self, directory: Path) -> None:
"""
logger.debug("Create %s", TEMPLATE_PARAMETERS_FILENAME)
vm_parameters = (
self.vm_parameters_ordered if self.order_params else self.vm_parameters
self.vm_parameters_ordered
if self.order_params
else self.vm_parameters
)

template_parameters = {}
Expand All @@ -280,18 +293,14 @@ def write_vhd_parameters(self, directory: Path) -> None:

:param directory: The directory to put this file in.
"""
azureDeployLocation: str
if self.vm_parameters.get("location"):
# Location can be passed in as deploy parameter
azureDeployLocation = "{deployParameters.location}"
else:
# Couldn't find a location parameter in the source template, so hard code to
# the location we are deploying the publisher to.
azureDeployLocation = self.config.location

vhd_config = self.config.vhd
vhd_parameters = {
"imageName": self.image_name,
"azureDeployLocation": azureDeployLocation,
**{
snake_case_to_camel_case(key): value
for key, value in vhd_config.__dict__.items()
if key in EXTRA_VHD_PARAMETERS and value is not None
},
}

vhd_parameters_path = directory / VHD_PARAMETERS_FILENAME
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
},
"vhd": {
"blob_sas_url": "https://a/dummy/sas-url",
"version": "1-0-0"
"version": "1-0-0",
"image_disk_size_GB": 30,
"image_hyper_v_generation": "V1",
"image_api_version": "2023-03-01"
}
}
}
Loading