зеркало из https://github.com/microsoft/lisa.git
enable community gallery image
This commit is contained in:
Родитель
402711ddf4
Коммит
4890deb5ee
|
@ -124,6 +124,10 @@ func getOsDiskSharedGallery(shared_gallery object) object => {
|
|||
id: resourceId(shared_gallery.subscription_id, empty(shared_gallery.resource_group_name) ? 'None' : shared_gallery.resource_group_name, 'Microsoft.Compute/galleries/images/versions', shared_gallery.image_gallery, shared_gallery.image_definition, shared_gallery.image_version)
|
||||
}
|
||||
|
||||
func getOSDiskCommunityGalleryImage(community_gallery_image object) object => {
|
||||
communityGalleryImageId: '/CommunityGalleries/${community_gallery_image.image_gallery}/Images/${community_gallery_image.image_definition}/Versions/${community_gallery_image.image_version}'
|
||||
}
|
||||
|
||||
func getOsDiskMarketplace(marketplace object) object => {
|
||||
publisher: marketplace.publisher
|
||||
offer: marketplace.offer
|
||||
|
@ -131,10 +135,13 @@ func getOsDiskMarketplace(marketplace object) object => {
|
|||
version: marketplace.version
|
||||
}
|
||||
|
||||
func generateImageReference(node object) object => (isVhd(node) ? getOsDiskVhd(node.name)
|
||||
: ((!empty(node.shared_gallery))
|
||||
func generateImageReference(node object) object => isVhd(node)
|
||||
? getOsDiskVhd(node.name)
|
||||
: !empty(node.shared_gallery)
|
||||
? getOsDiskSharedGallery(node.shared_gallery)
|
||||
: getOsDiskMarketplace(node.marketplace)))
|
||||
: !empty(node.community_gallery_image)
|
||||
? getOSDiskCommunityGalleryImage(node.community_gallery_image)
|
||||
: getOsDiskMarketplace(node.marketplace)
|
||||
|
||||
func getSecurityProfileForOSDisk(node object) object => empty(node.security_profile.disk_encryption_set_id)
|
||||
? {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"_generator": {
|
||||
"name": "bicep",
|
||||
"version": "0.24.24.22086",
|
||||
"templateHash": "2250159769761272390"
|
||||
"templateHash": "10469517385668739272"
|
||||
}
|
||||
},
|
||||
"functions": [
|
||||
|
@ -211,6 +211,20 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"getOSDiskCommunityGalleryImage": {
|
||||
"parameters": [
|
||||
{
|
||||
"type": "object",
|
||||
"name": "community_gallery_image"
|
||||
}
|
||||
],
|
||||
"output": {
|
||||
"type": "object",
|
||||
"value": {
|
||||
"communityGalleryImageId": "[format('/CommunityGalleries/{0}/Images/{1}/Versions/{2}', parameters('community_gallery_image').image_gallery, parameters('community_gallery_image').image_definition, parameters('community_gallery_image').image_version)]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"getOsDiskMarketplace": {
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -237,7 +251,7 @@
|
|||
],
|
||||
"output": {
|
||||
"type": "object",
|
||||
"value": "[if(__bicep.isVhd(parameters('node')), __bicep.getOsDiskVhd(parameters('node').name), if(not(empty(parameters('node').shared_gallery)), __bicep.getOsDiskSharedGallery(parameters('node').shared_gallery), __bicep.getOsDiskMarketplace(parameters('node').marketplace)))]"
|
||||
"value": "[if(__bicep.isVhd(parameters('node')), __bicep.getOsDiskVhd(parameters('node').name), if(not(empty(parameters('node').shared_gallery)), __bicep.getOsDiskSharedGallery(parameters('node').shared_gallery), if(not(empty(parameters('node').community_gallery_image)), __bicep.getOSDiskCommunityGalleryImage(parameters('node').community_gallery_image), __bicep.getOsDiskMarketplace(parameters('node').marketplace))))]"
|
||||
}
|
||||
},
|
||||
"getSecurityProfileForOSDisk": {
|
||||
|
|
|
@ -11,7 +11,7 @@ from functools import lru_cache, partial
|
|||
from pathlib import Path, PurePath
|
||||
from threading import Lock
|
||||
from time import sleep, time
|
||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union
|
||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Type, Union
|
||||
|
||||
import requests
|
||||
from assertpy import assert_that
|
||||
|
@ -152,6 +152,13 @@ SIG_IMAGE_KEYS = [
|
|||
"image_definition",
|
||||
"image_version",
|
||||
]
|
||||
CG_IMAGE_KEYS = [
|
||||
"location",
|
||||
"image_gallery",
|
||||
"image_definition",
|
||||
"image_version",
|
||||
]
|
||||
PURCHASE_PLAN_KEYS = ["name", "product", "publisher"]
|
||||
|
||||
# IMDS is a REST API that's available at a well-known, non-routable IP address (169.254.169.254). # noqa: E501
|
||||
METADATA_ENDPOINT = "http://169.254.169.254/metadata/instance?api-version=2021-02-01"
|
||||
|
@ -244,6 +251,20 @@ class VhdSchema(AzureImageSchema):
|
|||
vmgs_path: Optional[str] = None
|
||||
|
||||
|
||||
@dataclass_json()
|
||||
@dataclass
|
||||
class CommunityGalleryImageSchema(AzureImageSchema):
|
||||
image_gallery: str = ""
|
||||
image_definition: str = ""
|
||||
image_version: str = ""
|
||||
location: str = ""
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(
|
||||
f"{self.image_gallery}/{self.image_definition}/{self.image_version}"
|
||||
)
|
||||
|
||||
|
||||
@dataclass_json()
|
||||
@dataclass
|
||||
class AzureNodeSchema:
|
||||
|
@ -275,7 +296,9 @@ class AzureNodeSchema:
|
|||
vhd_raw: Optional[Union[Dict[Any, Any], str]] = field(
|
||||
default=None, metadata=field_metadata(data_key="vhd")
|
||||
)
|
||||
|
||||
community_gallery_image_raw: Optional[Union[Dict[Any, Any], str]] = field(
|
||||
default=None, metadata=field_metadata(data_key="community_gallery_image")
|
||||
)
|
||||
hyperv_generation: int = field(
|
||||
default=1,
|
||||
metadata=field_metadata(validate=validate.OneOf([1, 2])),
|
||||
|
@ -296,6 +319,8 @@ class AzureNodeSchema:
|
|||
|
||||
_orignal_vhd_path: str = ""
|
||||
|
||||
_community_gallery_image: InitVar[Optional[CommunityGalleryImageSchema]] = None
|
||||
|
||||
def __post_init__(self, *args: Any, **kwargs: Any) -> None:
|
||||
# trim whitespace of values.
|
||||
strip_strs(
|
||||
|
@ -309,6 +334,7 @@ class AzureNodeSchema:
|
|||
"marketplace_raw",
|
||||
"shared_gallery_raw",
|
||||
"vhd_raw",
|
||||
"community_gallery_image_raw",
|
||||
"data_disk_caching_type",
|
||||
"os_disk_type",
|
||||
"data_disk_type",
|
||||
|
@ -322,226 +348,146 @@ class AzureNodeSchema:
|
|||
|
||||
@property
|
||||
def purchase_plan(self) -> Optional[AzureVmPurchasePlanSchema]:
|
||||
# this is a safe guard and prevent mypy error on typing
|
||||
if not hasattr(self, "_purchase_plan"):
|
||||
self._purchase_plan: Optional[AzureVmPurchasePlanSchema] = None
|
||||
purchase_plan: Optional[AzureVmPurchasePlanSchema] = self._purchase_plan
|
||||
if not purchase_plan:
|
||||
if isinstance(self.purchase_plan_raw, dict):
|
||||
purchase_plan = schema.load_by_type(
|
||||
AzureVmPurchasePlanSchema, self.purchase_plan_raw
|
||||
)
|
||||
if not all(
|
||||
[
|
||||
purchase_plan.name.strip(),
|
||||
purchase_plan.product.strip(),
|
||||
purchase_plan.publisher.strip(),
|
||||
]
|
||||
):
|
||||
purchase_plan = None
|
||||
purchase_plan = self._parse_image(
|
||||
"purchase_plan",
|
||||
AzureVmPurchasePlanSchema,
|
||||
PURCHASE_PLAN_KEYS,
|
||||
self.purchase_plan_raw,
|
||||
)
|
||||
if isinstance(self.purchase_plan_raw, str):
|
||||
self.purchase_plan_raw = self.purchase_plan_raw.strip()
|
||||
|
||||
if self.purchase_plan_raw:
|
||||
purchase_plan_strings = re.split(r"[:\s]+", self.purchase_plan_raw)
|
||||
|
||||
if len(purchase_plan_strings) == 3:
|
||||
purchase_plan = AzureVmPurchasePlanSchema(
|
||||
name=purchase_plan_strings[0],
|
||||
product=purchase_plan_strings[1],
|
||||
publisher=purchase_plan_strings[2],
|
||||
)
|
||||
# purchase_plan_raw is used
|
||||
self.purchase_plan_raw = purchase_plan.to_dict()
|
||||
else:
|
||||
# this step makes purchase_plan_raw is validated, and
|
||||
# filter out any unwanted content.
|
||||
self.purchase_plan_raw = purchase_plan.to_dict() # type: ignore
|
||||
elif self.purchase_plan_raw:
|
||||
assert isinstance(
|
||||
self.purchase_plan_raw, str
|
||||
), f"actual: {type(self.purchase_plan_raw)}"
|
||||
|
||||
self.purchase_plan_raw = self.purchase_plan_raw.strip()
|
||||
|
||||
if self.purchase_plan_raw:
|
||||
purchase_plan_strings = re.split(r"[:\s]+", self.purchase_plan_raw)
|
||||
|
||||
if len(purchase_plan_strings) == 3:
|
||||
purchase_plan = AzureVmPurchasePlanSchema(
|
||||
name=purchase_plan_strings[0],
|
||||
product=purchase_plan_strings[1],
|
||||
publisher=purchase_plan_strings[2],
|
||||
)
|
||||
# purchase_plan_raw is used
|
||||
self.purchase_plan_raw = purchase_plan.to_dict() # type: ignore
|
||||
else:
|
||||
raise LisaException(
|
||||
f"Invalid value for the provided purchase_plan "
|
||||
f"parameter: '{self.purchase_plan_raw}'."
|
||||
f"The purchase_plan parameter should be in the format: "
|
||||
f"'<name> <product> <publisher>' "
|
||||
)
|
||||
self._purchase_plan = purchase_plan
|
||||
return purchase_plan
|
||||
raise LisaException(
|
||||
f"Invalid value for the provided purchase_plan "
|
||||
f"parameter: '{self.purchase_plan_raw}'."
|
||||
f"The purchase_plan parameter should be in the format: "
|
||||
f"'<name> <product> <publisher>' "
|
||||
)
|
||||
self._purchase_plan = purchase_plan
|
||||
return (
|
||||
purchase_plan
|
||||
if isinstance(purchase_plan, AzureVmPurchasePlanSchema)
|
||||
else None
|
||||
)
|
||||
|
||||
@purchase_plan.setter
|
||||
def purchase_plan(self, value: Optional[AzureVmPurchasePlanSchema]) -> None:
|
||||
self._purchase_plan = value
|
||||
if value is None:
|
||||
self.purchase_plan_raw = None
|
||||
else:
|
||||
self.purchase_plan_raw = value.to_dict() # type: ignore
|
||||
self._parse_image_raw("purchase_plan", value)
|
||||
|
||||
@property
|
||||
def marketplace(self) -> Optional[AzureVmMarketplaceSchema]:
|
||||
# this is a safe guard and prevent mypy error on typing
|
||||
if not hasattr(self, "_marketplace"):
|
||||
self._marketplace: Optional[AzureVmMarketplaceSchema] = None
|
||||
marketplace: Optional[AzureVmMarketplaceSchema] = self._marketplace
|
||||
if not marketplace:
|
||||
if isinstance(self.marketplace_raw, dict):
|
||||
marketplace = self._parse_image(
|
||||
"marketplace",
|
||||
AzureVmMarketplaceSchema,
|
||||
MARKETPLACE_IMAGE_KEYS,
|
||||
self.marketplace_raw,
|
||||
)
|
||||
if isinstance(self.marketplace_raw, str):
|
||||
self.marketplace_raw = self.marketplace_raw.strip()
|
||||
|
||||
if self.marketplace_raw:
|
||||
# Users decide the cases of image names,
|
||||
# the inconsistent cases cause the mismatched error in notifiers.
|
||||
# The lower() normalizes the image names,
|
||||
# it has no impact on deployment.
|
||||
self.marketplace_raw = dict(
|
||||
(
|
||||
(k, v.lower())
|
||||
if isinstance(v, str) and k in MARKETPLACE_IMAGE_KEYS
|
||||
else (k, v)
|
||||
marketplace_strings = re.split(r"[:\s]+", self.marketplace_raw.lower())
|
||||
|
||||
if len(marketplace_strings) == 4:
|
||||
marketplace = AzureVmMarketplaceSchema(
|
||||
publisher=marketplace_strings[0],
|
||||
offer=marketplace_strings[1],
|
||||
sku=marketplace_strings[2],
|
||||
version=marketplace_strings[3],
|
||||
)
|
||||
for k, v in self.marketplace_raw.items()
|
||||
)
|
||||
marketplace = schema.load_by_type(
|
||||
AzureVmMarketplaceSchema, self.marketplace_raw
|
||||
)
|
||||
if not all(
|
||||
[
|
||||
marketplace.publisher,
|
||||
marketplace.offer,
|
||||
marketplace.sku,
|
||||
marketplace.version,
|
||||
]
|
||||
):
|
||||
marketplace = None
|
||||
# marketplace_raw is used
|
||||
self.marketplace_raw = marketplace.to_dict()
|
||||
else:
|
||||
# this step makes marketplace_raw is validated, and
|
||||
# filter out any unwanted content.
|
||||
self.marketplace_raw = marketplace.to_dict() # type: ignore
|
||||
elif self.marketplace_raw:
|
||||
assert isinstance(
|
||||
self.marketplace_raw, str
|
||||
), f"actual: {type(self.marketplace_raw)}"
|
||||
|
||||
self.marketplace_raw = self.marketplace_raw.strip()
|
||||
|
||||
if self.marketplace_raw:
|
||||
# Users decide the cases of image names,
|
||||
# the inconsistent cases cause the mismatched error in notifiers.
|
||||
# The lower() normalizes the image names,
|
||||
# it has no impact on deployment.
|
||||
marketplace_strings = re.split(
|
||||
r"[:\s]+", self.marketplace_raw.lower()
|
||||
raise LisaException(
|
||||
f"Invalid value for the provided marketplace "
|
||||
f"parameter: '{self.marketplace_raw}'."
|
||||
f"The marketplace parameter should be in the format: "
|
||||
f"'<Publisher> <Offer> <Sku> <Version>' "
|
||||
f"or '<Publisher>:<Offer>:<Sku>:<Version>'"
|
||||
)
|
||||
|
||||
if len(marketplace_strings) == 4:
|
||||
marketplace = AzureVmMarketplaceSchema(
|
||||
publisher=marketplace_strings[0],
|
||||
offer=marketplace_strings[1],
|
||||
sku=marketplace_strings[2],
|
||||
version=marketplace_strings[3],
|
||||
)
|
||||
# marketplace_raw is used
|
||||
self.marketplace_raw = marketplace.to_dict() # type: ignore
|
||||
else:
|
||||
raise LisaException(
|
||||
f"Invalid value for the provided marketplace "
|
||||
f"parameter: '{self.marketplace_raw}'."
|
||||
f"The marketplace parameter should be in the format: "
|
||||
f"'<Publisher> <Offer> <Sku> <Version>' "
|
||||
f"or '<Publisher>:<Offer>:<Sku>:<Version>'"
|
||||
)
|
||||
self._marketplace = marketplace
|
||||
return marketplace
|
||||
self._marketplace = marketplace
|
||||
return (
|
||||
marketplace if isinstance(marketplace, AzureVmMarketplaceSchema) else None
|
||||
)
|
||||
|
||||
@marketplace.setter
|
||||
def marketplace(self, value: Optional[AzureVmMarketplaceSchema]) -> None:
|
||||
self._marketplace = value
|
||||
if value is None:
|
||||
self.marketplace_raw = None
|
||||
else:
|
||||
self.marketplace_raw = value.to_dict() # type: ignore
|
||||
self._parse_image_raw("marketplace", value)
|
||||
|
||||
@property
|
||||
def shared_gallery(self) -> Optional[SharedImageGallerySchema]:
|
||||
# this is a safe guard and prevent mypy error on typing
|
||||
if not hasattr(self, "_shared_gallery"):
|
||||
self._shared_gallery: Optional[SharedImageGallerySchema] = None
|
||||
shared_gallery: Optional[SharedImageGallerySchema] = self._shared_gallery
|
||||
if shared_gallery:
|
||||
return shared_gallery
|
||||
if isinstance(self.shared_gallery_raw, dict):
|
||||
# Users decide the cases of image names,
|
||||
# the inconsistent cases cause the mismatched error in notifiers.
|
||||
# The lower() normalizes the image names,
|
||||
# it has no impact on deployment.
|
||||
self.shared_gallery_raw = dict(
|
||||
(k, v.lower()) if isinstance(v, str) and k in SIG_IMAGE_KEYS else (k, v)
|
||||
for k, v in self.shared_gallery_raw.items()
|
||||
)
|
||||
shared_gallery = schema.load_by_type(
|
||||
SharedImageGallerySchema, self.shared_gallery_raw
|
||||
)
|
||||
if not all(
|
||||
[
|
||||
shared_gallery.image_definition,
|
||||
shared_gallery.image_version,
|
||||
shared_gallery.image_gallery,
|
||||
]
|
||||
):
|
||||
shared_gallery = None
|
||||
else:
|
||||
if not shared_gallery.subscription_id:
|
||||
shared_gallery.subscription_id = self.subscription_id
|
||||
# this step makes shared_gallery_raw is validated, and
|
||||
# filter out any unwanted content.
|
||||
self.shared_gallery_raw = shared_gallery.to_dict() # type: ignore
|
||||
elif self.shared_gallery_raw:
|
||||
assert isinstance(
|
||||
self.shared_gallery_raw, str
|
||||
), f"actual: {type(self.shared_gallery_raw)}"
|
||||
# Users decide the cases of image names,
|
||||
# the inconsistent cases cause the mismatched error in notifiers.
|
||||
# The lower() normalizes the image names,
|
||||
# it has no impact on deployment.
|
||||
shared_gallery_strings = re.split(
|
||||
r"[/]+", self.shared_gallery_raw.strip().lower()
|
||||
)
|
||||
if len(shared_gallery_strings) == 5:
|
||||
shared_gallery = SharedImageGallerySchema(
|
||||
subscription_id=shared_gallery_strings[0],
|
||||
resource_group_name=shared_gallery_strings[1],
|
||||
image_gallery=shared_gallery_strings[2],
|
||||
image_definition=shared_gallery_strings[3],
|
||||
image_version=shared_gallery_strings[4],
|
||||
)
|
||||
# shared_gallery_raw is used
|
||||
self.shared_gallery_raw = shared_gallery.to_dict() # type: ignore
|
||||
elif len(shared_gallery_strings) == 3:
|
||||
shared_gallery = SharedImageGallerySchema(
|
||||
subscription_id=self.subscription_id,
|
||||
image_gallery=shared_gallery_strings[0],
|
||||
image_definition=shared_gallery_strings[1],
|
||||
image_version=shared_gallery_strings[2],
|
||||
)
|
||||
# shared_gallery_raw is used
|
||||
self.shared_gallery_raw = shared_gallery.to_dict() # type: ignore
|
||||
else:
|
||||
raise LisaException(
|
||||
f"Invalid value for the provided shared gallery "
|
||||
f"parameter: '{self.shared_gallery_raw}'."
|
||||
f"The shared gallery parameter should be in the format: "
|
||||
f"'<subscription_id>/<resource_group_name>/<image_gallery>/"
|
||||
f"<image_definition>/<image_version>' or '<image_gallery>/"
|
||||
f"<image_definition>/<image_version>'"
|
||||
shared_gallery = self._parse_image(
|
||||
"shared_gallery",
|
||||
SharedImageGallerySchema,
|
||||
SIG_IMAGE_KEYS,
|
||||
self.shared_gallery_raw,
|
||||
)
|
||||
if (
|
||||
isinstance(shared_gallery, SharedImageGallerySchema)
|
||||
and not shared_gallery.subscription_id
|
||||
):
|
||||
shared_gallery.subscription_id = self.subscription_id
|
||||
if isinstance(self.shared_gallery_raw, str):
|
||||
self.shared_gallery_raw = self.shared_gallery_raw.strip()
|
||||
if self.shared_gallery_raw:
|
||||
# Users decide the cases of image names,
|
||||
# the inconsistent cases cause the mismatched error in notifiers.
|
||||
# The lower() normalizes the image names,
|
||||
# it has no impact on deployment.
|
||||
shared_gallery_strings = re.split(
|
||||
r"[/]+", self.shared_gallery_raw.strip().lower()
|
||||
)
|
||||
if len(shared_gallery_strings) == 5:
|
||||
shared_gallery = SharedImageGallerySchema(
|
||||
subscription_id=shared_gallery_strings[0],
|
||||
resource_group_name=shared_gallery_strings[1],
|
||||
image_gallery=shared_gallery_strings[2],
|
||||
image_definition=shared_gallery_strings[3],
|
||||
image_version=shared_gallery_strings[4],
|
||||
)
|
||||
elif len(shared_gallery_strings) == 3:
|
||||
shared_gallery = SharedImageGallerySchema(
|
||||
subscription_id=self.subscription_id,
|
||||
image_gallery=shared_gallery_strings[0],
|
||||
image_definition=shared_gallery_strings[1],
|
||||
image_version=shared_gallery_strings[2],
|
||||
)
|
||||
else:
|
||||
raise LisaException(
|
||||
f"Invalid value for the provided shared gallery "
|
||||
f"parameter: '{self.shared_gallery_raw}'."
|
||||
f"The shared gallery parameter should be in the format: "
|
||||
f"'<subscription_id>/<resource_group_name>/<image_gallery>/"
|
||||
f"<image_definition>/<image_version>' or '<image_gallery>/"
|
||||
f"<image_definition>/<image_version>'"
|
||||
)
|
||||
self.shared_gallery_raw = shared_gallery.to_dict()
|
||||
self._shared_gallery = shared_gallery
|
||||
return shared_gallery
|
||||
return (
|
||||
shared_gallery
|
||||
if isinstance(shared_gallery, SharedImageGallerySchema)
|
||||
else None
|
||||
)
|
||||
|
||||
@shared_gallery.setter
|
||||
def shared_gallery(self, value: Optional[SharedImageGallerySchema]) -> None:
|
||||
self._shared_gallery = value
|
||||
if value is None:
|
||||
self.shared_gallery_raw = None
|
||||
else:
|
||||
self.shared_gallery_raw = value.to_dict() # type: ignore
|
||||
self._parse_image_raw("shared_gallery", value)
|
||||
|
||||
@property
|
||||
def vhd(self) -> Optional[VhdSchema]:
|
||||
|
@ -577,11 +523,50 @@ class AzureNodeSchema:
|
|||
|
||||
@vhd.setter
|
||||
def vhd(self, value: Optional[VhdSchema]) -> None:
|
||||
self._vhd = value
|
||||
if value is None:
|
||||
self.vhd_raw = None
|
||||
else:
|
||||
self.vhd_raw = self._vhd.to_dict() # type: ignore
|
||||
self._parse_image_raw("vhd", value)
|
||||
|
||||
@property
|
||||
def community_gallery_image(self) -> Optional[CommunityGalleryImageSchema]:
|
||||
community_gallery_image = self._parse_image(
|
||||
"community_gallery_image",
|
||||
CommunityGalleryImageSchema,
|
||||
CG_IMAGE_KEYS,
|
||||
self.community_gallery_image_raw,
|
||||
)
|
||||
if isinstance(self.community_gallery_image_raw, str):
|
||||
self.community_gallery_image_raw = self.community_gallery_image_raw.strip()
|
||||
if self.community_gallery_image_raw:
|
||||
community_gallery_image_strings = re.split(
|
||||
r"[/]+", self.community_gallery_image_raw.lower()
|
||||
)
|
||||
if len(community_gallery_image_strings) == 4:
|
||||
community_gallery_image = CommunityGalleryImageSchema(
|
||||
location=community_gallery_image_strings[0],
|
||||
image_gallery=community_gallery_image_strings[1],
|
||||
image_definition=community_gallery_image_strings[2],
|
||||
image_version=community_gallery_image_strings[3],
|
||||
)
|
||||
self.community_gallery_image_raw = community_gallery_image.to_dict()
|
||||
else:
|
||||
raise LisaException(
|
||||
"Invalid value for the provided community gallery image"
|
||||
f"parameter: '{self.community_gallery_image_raw}'."
|
||||
"The community gallery image parameter should be in the"
|
||||
" format: '<location>/<image_gallery>/<image_definition>"
|
||||
"/<image_version>'"
|
||||
)
|
||||
self._community_gallery_image = community_gallery_image
|
||||
return (
|
||||
community_gallery_image
|
||||
if isinstance(community_gallery_image, CommunityGalleryImageSchema)
|
||||
else None
|
||||
)
|
||||
|
||||
@community_gallery_image.setter
|
||||
def community_gallery_image(
|
||||
self, value: Optional[CommunityGalleryImageSchema]
|
||||
) -> None:
|
||||
self._parse_image_raw("community_gallery_image", value)
|
||||
|
||||
def get_image_name(self) -> str:
|
||||
result = ""
|
||||
|
@ -601,6 +586,13 @@ class AzureNodeSchema:
|
|||
f"{self.shared_gallery.image_definition}/"
|
||||
f"{self.shared_gallery.image_version}"
|
||||
)
|
||||
elif self.community_gallery_image:
|
||||
assert isinstance(
|
||||
self.community_gallery_image_raw, dict
|
||||
), f"actual type: {type(self.community_gallery_image_raw)}"
|
||||
result = "/".join(
|
||||
[self.community_gallery_image_raw.get(k, "") for k in CG_IMAGE_KEYS]
|
||||
)
|
||||
elif self.marketplace:
|
||||
assert isinstance(
|
||||
self.marketplace_raw, dict
|
||||
|
@ -610,6 +602,66 @@ class AzureNodeSchema:
|
|||
)
|
||||
return result
|
||||
|
||||
def _parse_image(
|
||||
self,
|
||||
prop_name: str,
|
||||
schema_type: Type[
|
||||
Union[
|
||||
VhdSchema,
|
||||
AzureImageSchema,
|
||||
SharedImageGallerySchema,
|
||||
CommunityGalleryImageSchema,
|
||||
AzureVmPurchasePlanSchema,
|
||||
]
|
||||
],
|
||||
keys: List[str],
|
||||
raw_data: Optional[Union[Dict[Any, Any], str]],
|
||||
) -> Any:
|
||||
if not hasattr(self, f"_{prop_name}"):
|
||||
setattr(self, f"_{prop_name}", None)
|
||||
prop_value = getattr(self, f"_{prop_name}")
|
||||
|
||||
if prop_value:
|
||||
return prop_value
|
||||
|
||||
if isinstance(raw_data, dict):
|
||||
normalized_data = {
|
||||
k: (v.lower() if isinstance(v, str) and hasattr(schema_type, k) else v)
|
||||
for k, v in raw_data.items()
|
||||
}
|
||||
prop_value = schema.load_by_type(schema_type, normalized_data)
|
||||
|
||||
# Check if all required keys have values
|
||||
if all(getattr(prop_value, key) for key in keys):
|
||||
setattr(self, f"{prop_name}_raw", prop_value.to_dict())
|
||||
else:
|
||||
setattr(self, f"{prop_name}_raw", None)
|
||||
|
||||
return prop_value
|
||||
|
||||
def _parse_image_raw(
|
||||
self,
|
||||
prop_name: str,
|
||||
value: Optional[
|
||||
Union[
|
||||
VhdSchema,
|
||||
AzureImageSchema,
|
||||
SharedImageGallerySchema,
|
||||
CommunityGalleryImageSchema,
|
||||
AzureVmPurchasePlanSchema,
|
||||
]
|
||||
],
|
||||
) -> None:
|
||||
setattr(self, f"_{prop_name}", value)
|
||||
if value is not None:
|
||||
raw_value = (
|
||||
value.to_dict() if hasattr(value, "to_dict") else value # type: ignore
|
||||
)
|
||||
else:
|
||||
raw_value = None
|
||||
|
||||
setattr(self, f"{prop_name}_raw", raw_value)
|
||||
|
||||
|
||||
@dataclass_json()
|
||||
@dataclass
|
||||
|
@ -624,18 +676,17 @@ class AzureNodeArmParameter(AzureNodeSchema):
|
|||
@classmethod
|
||||
def from_node_runbook(cls, runbook: AzureNodeSchema) -> "AzureNodeArmParameter":
|
||||
parameters = runbook.to_dict() # type: ignore
|
||||
if "marketplace" in parameters:
|
||||
parameters["marketplace_raw"] = parameters["marketplace"]
|
||||
del parameters["marketplace"]
|
||||
if "purchase_plan" in parameters:
|
||||
parameters["purchase_plan_raw"] = parameters["purchase_plan"]
|
||||
del parameters["purchase_plan"]
|
||||
if "shared_gallery" in parameters:
|
||||
parameters["shared_gallery_raw"] = parameters["shared_gallery"]
|
||||
del parameters["shared_gallery"]
|
||||
if "vhd" in parameters:
|
||||
parameters["vhd_raw"] = parameters["vhd"]
|
||||
del parameters["vhd"]
|
||||
keys_to_rename = {
|
||||
"marketplace": "marketplace_raw",
|
||||
"purchase_plan": "purchase_plan_raw",
|
||||
"shared_gallery": "shared_gallery_raw",
|
||||
"community_gallery_image": "community_gallery_image_raw",
|
||||
"vhd": "vhd_raw",
|
||||
}
|
||||
|
||||
for old_key, new_key in keys_to_rename.items():
|
||||
if old_key in parameters:
|
||||
parameters[new_key] = parameters.pop(old_key)
|
||||
|
||||
arm_parameters = AzureNodeArmParameter(**parameters)
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ import requests
|
|||
from azure.core.exceptions import HttpResponseError, ResourceNotFoundError
|
||||
from azure.identity import DefaultAzureCredential
|
||||
from azure.mgmt.compute.models import (
|
||||
CommunityGalleryImage,
|
||||
CommunityGalleryImageVersion,
|
||||
GalleryImage,
|
||||
GalleryImageVersion,
|
||||
ResourceSku,
|
||||
|
@ -97,6 +99,7 @@ from .common import (
|
|||
AzureNodeSchema,
|
||||
AzureVmMarketplaceSchema,
|
||||
AzureVmPurchasePlanSchema,
|
||||
CommunityGalleryImageSchema,
|
||||
DataDiskCreateOption,
|
||||
DataDiskSchema,
|
||||
SharedImageGallerySchema,
|
||||
|
@ -1402,11 +1405,27 @@ class AzurePlatform(Platform):
|
|||
azure_node_runbook.vhd = vhd
|
||||
azure_node_runbook.marketplace = None
|
||||
azure_node_runbook.shared_gallery = None
|
||||
log.debug(
|
||||
f"current vhd generation is {azure_node_runbook.hyperv_generation}."
|
||||
)
|
||||
elif azure_node_runbook.shared_gallery:
|
||||
azure_node_runbook.marketplace = None
|
||||
azure_node_runbook.shared_gallery = self._parse_shared_gallery_image(
|
||||
azure_node_runbook.shared_gallery
|
||||
)
|
||||
azure_node_runbook.hyperv_generation = _get_gallery_image_generation(
|
||||
self._get_sig(azure_node_runbook.shared_gallery)
|
||||
)
|
||||
elif azure_node_runbook.community_gallery_image:
|
||||
azure_node_runbook.marketplace = None
|
||||
azure_node_runbook.community_gallery_image = (
|
||||
self._parse_community_gallery_image(
|
||||
azure_node_runbook.community_gallery_image
|
||||
)
|
||||
)
|
||||
azure_node_runbook.hyperv_generation = _get_gallery_image_generation(
|
||||
self._get_cgi(azure_node_runbook.community_gallery_image)
|
||||
)
|
||||
elif not azure_node_runbook.marketplace:
|
||||
# set to default marketplace, if nothing specified
|
||||
azure_node_runbook.marketplace = AzureVmMarketplaceSchema()
|
||||
|
@ -1431,15 +1450,6 @@ class AzurePlatform(Platform):
|
|||
and image_info.os_disk_image.operating_system == "Windows"
|
||||
):
|
||||
azure_node_runbook.is_linux = False
|
||||
elif azure_node_runbook.shared_gallery:
|
||||
azure_node_runbook.hyperv_generation = _get_gallery_image_generation(
|
||||
self._get_detailed_sig(azure_node_runbook.shared_gallery)
|
||||
)
|
||||
else:
|
||||
log.debug(
|
||||
"there is no way to detect vhd generation, unless user provides it"
|
||||
f" current vhd generation is {azure_node_runbook.hyperv_generation}"
|
||||
)
|
||||
|
||||
if azure_node_runbook.is_linux is None:
|
||||
# fill it default value
|
||||
|
@ -1477,6 +1487,11 @@ class AzurePlatform(Platform):
|
|||
arm_parameters.osdisk_size_in_gb,
|
||||
self._get_sig_os_disk_size(arm_parameters.shared_gallery),
|
||||
)
|
||||
elif arm_parameters.community_gallery_image:
|
||||
arm_parameters.osdisk_size_in_gb = max(
|
||||
arm_parameters.osdisk_size_in_gb,
|
||||
self._get_cgi_os_disk_size(arm_parameters.community_gallery_image),
|
||||
)
|
||||
else:
|
||||
assert (
|
||||
arm_parameters.marketplace
|
||||
|
@ -2062,6 +2077,41 @@ class AzurePlatform(Platform):
|
|||
|
||||
return new_marketplace
|
||||
|
||||
@lru_cache(maxsize=10) # noqa: B019
|
||||
def _parse_community_gallery_image(
|
||||
self, community_gallery_image: CommunityGalleryImageSchema
|
||||
) -> CommunityGalleryImageSchema:
|
||||
new_community_gallery_image = copy.copy(community_gallery_image)
|
||||
compute_client = get_compute_client(self)
|
||||
if community_gallery_image.image_version.lower() == "latest":
|
||||
community_gallery_images_list = (
|
||||
compute_client.community_gallery_image_versions.list(
|
||||
location=community_gallery_image.location,
|
||||
public_gallery_name=community_gallery_image.image_gallery,
|
||||
gallery_image_name=community_gallery_image.image_definition,
|
||||
)
|
||||
)
|
||||
image: Optional[CommunityGalleryImageVersion] = None
|
||||
time: Optional[datetime] = None
|
||||
for image in community_gallery_images_list:
|
||||
assert image, "'image' must not be 'None'"
|
||||
assert image.name, "'image.name' must not be 'None'"
|
||||
community_gallery_image_version = (
|
||||
compute_client.community_gallery_image_versions.get(
|
||||
location=community_gallery_image.location,
|
||||
public_gallery_name=community_gallery_image.image_gallery,
|
||||
gallery_image_name=community_gallery_image.image_definition,
|
||||
gallery_image_version_name=image.name,
|
||||
)
|
||||
)
|
||||
if not time:
|
||||
time = community_gallery_image_version.published_date
|
||||
new_community_gallery_image.image_version = image.name
|
||||
if community_gallery_image_version.published_date > time:
|
||||
time = community_gallery_image_version.published_date
|
||||
new_community_gallery_image.image_version = image.name
|
||||
return new_community_gallery_image
|
||||
|
||||
@lru_cache(maxsize=10) # noqa: B019
|
||||
def _parse_shared_gallery_image(
|
||||
self, shared_image: SharedImageGallerySchema
|
||||
|
@ -2430,33 +2480,64 @@ class AzurePlatform(Platform):
|
|||
assert isinstance(vhd_os_disk_size, int), f"actual: {type(vhd_os_disk_size)}"
|
||||
return vhd_os_disk_size
|
||||
|
||||
def _get_sig_info(
|
||||
def _get_sig_version(
|
||||
self, shared_image: SharedImageGallerySchema
|
||||
) -> GalleryImageVersion:
|
||||
compute_client = get_compute_client(self)
|
||||
sig_info = compute_client.gallery_image_versions.get(
|
||||
sig_version = compute_client.gallery_image_versions.get(
|
||||
resource_group_name=shared_image.resource_group_name,
|
||||
gallery_name=shared_image.image_gallery,
|
||||
gallery_image_name=shared_image.image_definition,
|
||||
gallery_image_version_name=shared_image.image_version,
|
||||
expand="ReplicationStatus",
|
||||
)
|
||||
assert isinstance(sig_info, GalleryImageVersion), f"actual: {type(sig_info)}"
|
||||
return sig_info
|
||||
assert isinstance(
|
||||
sig_version, GalleryImageVersion
|
||||
), f"actual: {type(sig_version)}"
|
||||
return sig_version
|
||||
|
||||
@lru_cache(maxsize=10) # noqa: B019
|
||||
def _get_detailed_sig(self, shared_image: SharedImageGallerySchema) -> GalleryImage:
|
||||
def _get_cgi_version(
|
||||
self, community_gallery_image: CommunityGalleryImageSchema
|
||||
) -> CommunityGalleryImageVersion:
|
||||
compute_client = get_compute_client(self)
|
||||
detailed_sig = compute_client.gallery_images.get(
|
||||
cgi_version = compute_client.community_gallery_image_versions.get(
|
||||
location=community_gallery_image.location,
|
||||
public_gallery_name=community_gallery_image.image_gallery,
|
||||
gallery_image_name=community_gallery_image.image_definition,
|
||||
gallery_image_version_name=community_gallery_image.image_version,
|
||||
)
|
||||
assert isinstance(
|
||||
cgi_version, CommunityGalleryImageVersion
|
||||
), f"actual: {type(cgi_version)}"
|
||||
return cgi_version
|
||||
|
||||
@lru_cache(maxsize=10) # noqa: B019
|
||||
def _get_cgi(
|
||||
self, community_gallery_image: CommunityGalleryImageSchema
|
||||
) -> CommunityGalleryImage:
|
||||
compute_client = get_compute_client(self)
|
||||
cgi = compute_client.community_gallery_images.get(
|
||||
location=community_gallery_image.location,
|
||||
public_gallery_name=community_gallery_image.image_gallery,
|
||||
gallery_image_name=community_gallery_image.image_definition,
|
||||
)
|
||||
assert isinstance(cgi, CommunityGalleryImage), f"actual: {type(cgi)}"
|
||||
return cgi
|
||||
|
||||
@lru_cache(maxsize=10) # noqa: B019
|
||||
def _get_sig(self, shared_image: SharedImageGallerySchema) -> GalleryImage:
|
||||
compute_client = get_compute_client(self)
|
||||
sig = compute_client.gallery_images.get(
|
||||
resource_group_name=shared_image.resource_group_name,
|
||||
gallery_name=shared_image.image_gallery,
|
||||
gallery_image_name=shared_image.image_definition,
|
||||
)
|
||||
assert isinstance(detailed_sig, GalleryImage), f"actual: {type(detailed_sig)}"
|
||||
return detailed_sig
|
||||
assert isinstance(sig, GalleryImage), f"actual: {type(sig)}"
|
||||
return sig
|
||||
|
||||
def _get_sig_os_disk_size(self, shared_image: SharedImageGallerySchema) -> int:
|
||||
found_image = self._get_sig_info(shared_image)
|
||||
found_image = self._get_sig_version(shared_image)
|
||||
assert found_image.storage_profile, "'storage_profile' must not be 'None'"
|
||||
assert (
|
||||
found_image.storage_profile.os_disk_image
|
||||
|
@ -2466,6 +2547,18 @@ class AzurePlatform(Platform):
|
|||
), "'size_in_gb' must not be 'None'"
|
||||
return int(found_image.storage_profile.os_disk_image.size_in_gb)
|
||||
|
||||
def _get_cgi_os_disk_size(
|
||||
self, community_gallery_image: CommunityGalleryImageSchema
|
||||
) -> int:
|
||||
found_image = self._get_cgi_version(community_gallery_image)
|
||||
storage_profile = found_image.storage_profile # type: ignore
|
||||
assert storage_profile, "'storage_profile' must not be 'None'"
|
||||
assert storage_profile.os_disk_image, "'os_disk_image' must not be 'None'"
|
||||
assert (
|
||||
storage_profile.os_disk_image.disk_size_gb
|
||||
), "'disk_size_gb' must not be 'None'"
|
||||
return int(storage_profile.os_disk_image.disk_size_gb)
|
||||
|
||||
def _get_normalized_vm_sizes(
|
||||
self, name: str, location: str, log: Logger
|
||||
) -> List[str]:
|
||||
|
@ -2853,7 +2946,7 @@ class AzurePlatform(Platform):
|
|||
azure_runbook.shared_gallery = self._parse_shared_gallery_image(
|
||||
azure_runbook.shared_gallery
|
||||
)
|
||||
sig = self._get_detailed_sig(azure_runbook.shared_gallery)
|
||||
sig = self._get_sig(azure_runbook.shared_gallery)
|
||||
generation = _get_gallery_image_generation(sig)
|
||||
node_space.features.add(features.VhdGenerationSettings(gen=generation))
|
||||
node_space.features.add(
|
||||
|
@ -2863,7 +2956,16 @@ class AzurePlatform(Platform):
|
|||
node_space.features.add(
|
||||
features.VhdGenerationSettings(gen=azure_runbook.hyperv_generation)
|
||||
)
|
||||
|
||||
elif azure_runbook.community_gallery_image:
|
||||
azure_runbook.community_gallery_image = self._parse_community_gallery_image(
|
||||
azure_runbook.community_gallery_image
|
||||
)
|
||||
cgi = self._get_cgi(azure_runbook.community_gallery_image)
|
||||
generation = _get_gallery_image_generation(cgi)
|
||||
node_space.features.add(features.VhdGenerationSettings(gen=generation))
|
||||
node_space.features.add(
|
||||
features.ArchitectureSettings(arch=cgi.architecture) # type: ignore
|
||||
)
|
||||
if node_space.disk:
|
||||
assert node_space.disk.os_disk_type
|
||||
if (
|
||||
|
@ -2896,6 +2998,11 @@ class AzurePlatform(Platform):
|
|||
azure_runbook.shared_gallery
|
||||
)
|
||||
return self._get_sig_os_disk_size(azure_runbook.shared_gallery)
|
||||
elif azure_runbook.community_gallery_image:
|
||||
azure_runbook.community_gallery_image = self._parse_community_gallery_image(
|
||||
azure_runbook.community_gallery_image
|
||||
)
|
||||
return self._get_cgi_os_disk_size(azure_runbook.community_gallery_image)
|
||||
else:
|
||||
assert azure_runbook.vhd
|
||||
assert azure_runbook.vhd.vhd_path
|
||||
|
@ -2994,11 +3101,13 @@ def _get_vhd_generation(image_info: VirtualMachineImage) -> int:
|
|||
return vhd_gen
|
||||
|
||||
|
||||
def _get_gallery_image_generation(shared_image: GalleryImage) -> int:
|
||||
def _get_gallery_image_generation(
|
||||
image: Union[GalleryImage, CommunityGalleryImage]
|
||||
) -> int:
|
||||
assert (
|
||||
shared_image.hyper_v_generation
|
||||
), f"no hyper_v_generation property for image {shared_image.name}"
|
||||
return int(shared_image.hyper_v_generation.strip("V"))
|
||||
hasattr(image, "hyper_v_generation") and image.hyper_v_generation
|
||||
), f"no hyper_v_generation property for image {image.name}"
|
||||
return int(image.hyper_v_generation.strip("V"))
|
||||
|
||||
|
||||
def _get_disk_size_in_gb(additional_properties: Dict[str, int]) -> int:
|
||||
|
|
Загрузка…
Ссылка в новой задаче