1515# See the License for the specific language governing permissions and 
1616# limitations under the License. 
1717
18+ import  jmespath 
1819from  ansible .module_utils .basic  import  AnsibleModule 
1920from  ansible_collections .cloudera .cloud .plugins .module_utils .cdp_common  import  CdpModule 
2021
7576    type: str 
7677    required: False 
7778  subnet: 
78-     description: The subnet ID in AWS, or the Subnet Name on Azure or GCP 
79+     description: 
80+       - The subnet ID in AWS, or the Subnet Name on Azure or GCP 
81+       - Mutually exclusive with the subnet and subnets options  
7982    type: str 
8083    required: False 
8184    samples: 
8285      - Azure: fe-az-f0-sbnt-2 
8386      - AWS: subnet-0bb1c79de3EXAMPLE 
8487      - GCP: fe-gc-j8-sbnt-0 
88+   subnets: 
89+     description: 
90+       - List of subnet IDs in case of multi availability zone setup. 
91+       - Mutually exclusive with the subnet and subnets options  
92+     type: list 
93+     required: False 
94+   subnets_filter: 
95+     description: 
96+       - L(JMESPath,https://jmespath.org/) expression to filter the subnets to be used for the load balancer 
97+       - The expression will be applied to the full list of subnets for the specified environment 
98+       - Each subnet in the list is an object with the following attributes: subnetId, subnetName, availabilityZone, cidr 
99+       - The filter expression must only filter the list, but not apply any attribute projection 
100+       - Mutually exclusive with the subnet and subnets options  
101+     type: list 
102+     required: False 
85103  image: 
86104    description: ID of the image used for cluster instances 
87105    type: str 
152170    required: False 
153171    aliases: 
154172      - datahub_tags 
173+   extension: 
174+     description: 
175+       - Cluster extensions for Data Hub cluster. 
176+     type: str 
177+     required: False 
155178  force: 
156179    description: 
157180      - Flag indicating if the datahub should be force deleted. 
@@ -394,11 +417,15 @@ def __init__(self, module):
394417        self .environment  =  self ._get_param ('environment' )
395418        self .definition  =  self ._get_param ('definition' )
396419        self .subnet  =  self ._get_param ('subnet' )
420+         self .subnets  =  self ._get_param ('subnets' )
421+         self .subnets_filter  =  self ._get_param ('subnets_filter' )
397422        self .image_id  =  self ._get_param ('image' )
398423        self .image_catalog  =  self ._get_param ('catalog' )
399424        self .template  =  self ._get_param ('template' )
400425        self .groups  =  self ._get_param ('groups' )
401426        self .tags  =  self ._get_param ('tags' )
427+         self .extension  =  self ._get_param ('extension' )
428+         self .multi_az  =  self ._get_param ('multi_az' )
402429
403430        self .wait  =  self ._get_param ('wait' )
404431        self .delay  =  self ._get_param ('delay' )
@@ -542,16 +569,40 @@ def _configure_payload(self):
542569        )
543570
544571        if  self .definition  is  not None :
545-           payload ["clusterDefinitionName" ]= self .definition 
572+              payload ["clusterDefinitionName" ]  =   self .definition 
546573        else :
547-           payload ["image" ]= {"id" : self .image_id , "catalogName" : self .image_catalog }
548-           payload ["clusterTemplateName" ]= self .template 
549-           payload ["instanceGroups" ]= self .groups 
574+             payload ["image" ] =  {"id" : self .image_id , "catalogName" : self .image_catalog }
575+             payload ["clusterTemplateName" ] =  self .template 
576+             payload ["instanceGroups" ] =  self .groups 
577+ 
578+         if  self .subnets_filter :
579+             try :
580+                 env_info  =  self .cdpy .environments .describe_environment (self .environment )
581+                 subnet_metadata  =  list (env_info ['network' ]['subnetMetadata' ].values ())
582+             except  Exception :
583+                 subnet_metadata  =  []
584+             if  not  subnet_metadata :
585+                 self .module .fail_json (
586+                     msg = "Could not retrieve subnet metadata for CDP Environment %s"  %  self .env_crn )
587+ 
588+             subnets  =  self ._filter_subnets (self .subnets_filter , subnet_metadata )
589+             if  len (subnets ) ==  1 :
590+                 self .subnet  =  subnets [0 ]
591+             else :
592+                 self .subnets  =  subnets 
550593
551-         if  self .host_env ['cloudPlatform' ] ==  'GCP' :
552-             payload ['subnetName' ] =  self .subnet 
553-         else :
554-             payload ['subnetId' ] =  self .subnet 
594+         if  self .subnet :
595+             if  self .host_env ['cloudPlatform' ] ==  'GCP' :
596+                 payload ['subnetName' ] =  self .subnet 
597+             else :
598+                 payload ['subnetId' ] =  self .subnet 
599+         elif  self .subnets :
600+             payload ['subnetIds' ] =  self .subnets 
601+ 
602+         if  self .extension  is  not None :
603+             payload ['clusterExtension' ] =  self .extension 
604+ 
605+         payload ['multiAz' ] =  self .multi_az 
555606
556607        if  self .tags  is  not None :
557608            payload ['tags' ] =  list ()
@@ -560,6 +611,27 @@ def _configure_payload(self):
560611
561612        return  payload 
562613
614+     def  _filter_subnets (self , query , subnets ):
615+         """Apply a JMESPath to an array of subnets and return the id of the selected subnets. 
616+         The query must only filter the array, without applying any projection. The query result must also be an 
617+         array of subnet objects. 
618+ 
619+         :param query: JMESpath query to filter the subnet array. 
620+         :param subnets: An array of subnet objects. Each subnet in the array is an object with the following attributes: 
621+         subnetId, subnetName, availabilityZone, cidr. 
622+         :return: An array of subnet ids. 
623+         """ 
624+         filtered_subnets  =  []
625+         try :
626+             filtered_subnets  =  jmespath .search (query , subnets )
627+         except  Exception :
628+             self .module .fail_json (msg = "The specified subnet filter is an invalid JMESPath expression: "  %  query )
629+         try :
630+             return  [s ['subnetId' ] for  s  in  filtered_subnets ]
631+         except  Exception :
632+             self .module .fail_json (msg = 'The subnet filter "%s" should return an array of subnet objects ' 
633+                                       'but instead returned this: %s'  %  (query , json .dumps (filtered_subnets )))
634+ 
563635    def  _reconcile_existing_state (self , existing ):
564636        mismatched  =  list ()
565637
@@ -594,19 +666,26 @@ def main():
594666            state = dict (required = False , type = 'str' , choices = ['present' , 'started' , 'stopped' , 'absent' ], default = 'present' ),
595667            definition = dict (required = False , type = 'str' ),
596668            subnet = dict (required = False , type = 'str' , default = None ),
669+             subnets = dict (required = False , type = 'list' , elements = 'str' , default = None ),
670+             subnets_filter = dict (required = False , type = 'str' , default = None ),
597671            image = dict (required = False , type = 'str' , default = None ),
598672            catalog = dict (required = False , type = 'str' , default = None ),
599673            template = dict (required = False , type = 'str' , default = None ),
600674            groups = dict (required = False , type = 'list' , default = None ),
601675            environment = dict (required = False , type = 'str' , aliases = ['env' ], default = None ),
602676            tags = dict (required = False , type = 'dict' , aliases = ['datahub_tags' ]),
677+             extension = dict (required = False , type = 'dict' ),
678+             multi_az = dict (required = False , type = 'bool' , default = True ),
603679
604680            force = dict (required = False , type = 'bool' , default = False ),
605681            wait = dict (required = False , type = 'bool' , default = True ),
606682            delay = dict (required = False , type = 'int' , aliases = ['polling_delay' ], default = 15 ),
607683            timeout = dict (required = False , type = 'int' , aliases = ['polling_timeout' ], default = 3600 )
608684        ),
609-         supports_check_mode = True 
685+         supports_check_mode = True ,
686+         mutually_exclusive = [
687+             ('subnet' , 'subnets' , 'subnets_filter' ),
688+         ],
610689        #Punting on additional checks here. There are a variety of supporting datahub invocations that can make this more complex 
611690        #required_together=[ 
612691        #    ['subnet', 'image', 'catalog', 'template', 'groups', 'environment'], 
0 commit comments