From 53c621d437896ebbf0ee45b0424b457d828618bc Mon Sep 17 00:00:00 2001 From: Divyansh Date: Thu, 15 Jul 2021 23:55:07 -0700 Subject: [PATCH] Add support for testing image in shared gallery --- lisa/sut_orchestrator/azure/arm_template.json | 18 +++- lisa/sut_orchestrator/azure/common.py | 90 +++++++++++++++++++ lisa/sut_orchestrator/azure/platform_.py | 4 + 3 files changed, 110 insertions(+), 2 deletions(-) diff --git a/lisa/sut_orchestrator/azure/arm_template.json b/lisa/sut_orchestrator/azure/arm_template.json index d2779b9706..2ad006f75f 100644 --- a/lisa/sut_orchestrator/azure/arm_template.json +++ b/lisa/sut_orchestrator/azure/arm_template.json @@ -278,7 +278,7 @@ "linuxConfiguration": "[if(empty(parameters('admin_key_data')), json('null'), lisa.getLinuxConfiguration(concat('/home/', parameters('admin_username'), '/.ssh/authorized_keys'), parameters('admin_key_data')))]" }, "storageProfile": { - "imageReference": "[if(not(empty(parameters('nodes')[copyIndex('vmCopy')]['vhd'])), lisa.getOsDiskVhd(parameters('nodes')[copyIndex('vmCopy')]['name']), lisa.getOsDiskMarketplace(parameters('nodes')[copyIndex('vmCopy')]))]", + "imageReference": "[if(not(empty(parameters('nodes')[copyIndex('vmCopy')]['vhd'])), lisa.getOsDiskVhd(parameters('nodes')[copyIndex('vmCopy')]['name']), if(not(empty(parameters('nodes')[copyIndex('vmCopy')]['shared_gallery'])), lisa.getOsDiskSharedGallery(parameters('nodes')[copyIndex('vmCopy')]['shared_gallery']), lisa.getOsDiskMarketplace(parameters('nodes')[copyIndex('vmCopy')])))]", "osDisk": { "name": "[concat(parameters('nodes')[copyIndex('vmCopy')]['name'], '-osDisk')]", "managedDisk": { @@ -327,6 +327,20 @@ "value": "[parameters('node')['marketplace']]" } }, + "getOsDiskSharedGallery": { + "parameters": [ + { + "name": "node", + "type": "object" + } + ], + "output": { + "type": "object", + "value": { + "id": "[resourceId(parameters('node')['subscription_id'], 'None', 'Microsoft.Compute/galleries/images/versions', parameters('node')['image_gallery'], parameters('node')['image_definition'], parameters('node')['image_version'])]" + } + } + }, "getOsDiskVhd": { "parameters": [ { @@ -371,4 +385,4 @@ } } ] -} +} \ No newline at end of file diff --git a/lisa/sut_orchestrator/azure/common.py b/lisa/sut_orchestrator/azure/common.py index 01812db638..a186cc2ec8 100644 --- a/lisa/sut_orchestrator/azure/common.py +++ b/lisa/sut_orchestrator/azure/common.py @@ -60,22 +60,38 @@ class AzureVmMarketplaceSchema: version: str = "Latest" +@dataclass_json() +@dataclass +class SharedImageGallerySchema: + subscription_id: str = "" + image_gallery: str = "" + image_definition: str = "" + image_version: str = "" + + @dataclass_json() @dataclass class AzureNodeSchema: name: str = "" vm_size: str = "" location: str = "" + subscription_id: str = "" marketplace_raw: Optional[Union[Dict[Any, Any], str]] = field( default=None, metadata=schema.metadata(data_key="marketplace") ) + shared_gallery_raw: Optional[Union[Dict[Any, Any], str]] = field( + default=None, metadata=schema.metadata(data_key="shared_gallery") + ) vhd: str = "" nic_count: int = 1 + # for marketplace image, which need to accept terms purchase_plan: Optional[AzureVmPurchasePlanSchema] = None _marketplace: InitVar[Optional[AzureVmMarketplaceSchema]] = None + _shared_gallery: InitVar[Optional[SharedImageGallerySchema]] = None + @property def marketplace(self) -> Optional[AzureVmMarketplaceSchema]: # this is a safe guard and prevent mypy error on typing @@ -132,10 +148,84 @@ def marketplace(self, value: Optional[AzureVmMarketplaceSchema]) -> None: else: self.marketplace_raw = value.to_dict() # type: ignore + @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 not 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()) for k, v in self.shared_gallery_raw.items() + ) + shared_gallery = SharedImageGallerySchema.schema().load( # type: ignore + self.shared_gallery_raw + ) + if not shared_gallery.subscription_id: # type: ignore + shared_gallery.subscription_id = ( # type: ignore + 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) == 4: + shared_gallery = SharedImageGallerySchema(*shared_gallery_strings) + # shared_gallery_raw is used + self.shared_gallery_raw = shared_gallery.to_dict() # type: ignore + elif len(shared_gallery_strings) == 3: + shared_gallery = SharedImageGallerySchema() + shared_gallery.subscription_id = self.subscription_id + shared_gallery.image_gallery = shared_gallery_strings[0] + shared_gallery.image_definition = shared_gallery_strings[1] + shared_gallery.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"'//" + f"/' or '/" + f"/'" + ) + self._shared_gallery = shared_gallery + return shared_gallery + + @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 + def get_image_name(self) -> str: result = "" if self.vhd: result = self.vhd + elif self.shared_gallery: + assert isinstance( + self.shared_gallery_raw, dict + ), f"actual type: {type(self.shared_gallery_raw)}" + result = " ".join([x for x in self.shared_gallery_raw.values()]) elif self.marketplace: assert isinstance( self.marketplace_raw, dict diff --git a/lisa/sut_orchestrator/azure/platform_.py b/lisa/sut_orchestrator/azure/platform_.py index 97d036146e..39c39addf0 100644 --- a/lisa/sut_orchestrator/azure/platform_.py +++ b/lisa/sut_orchestrator/azure/platform_.py @@ -839,6 +839,7 @@ def _create_deployment_parameters( azure_node_runbook = node_space.get_extended_runbook( AzureNodeSchema, type_name=AZURE ) + azure_node_runbook.subscription_id = self.subscription_id # init node node = environment.nodes.from_requirement( @@ -859,6 +860,9 @@ def _create_deployment_parameters( if azure_node_runbook.vhd: # vhd is higher priority azure_node_runbook.marketplace = None + azure_node_runbook.shared_gallery = None + elif azure_node_runbook.shared_gallery: + azure_node_runbook.marketplace = None elif not azure_node_runbook.marketplace: # set to default marketplace, if nothing specified azure_node_runbook.marketplace = AzureVmMarketplaceSchema()