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