2
2
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
3
3
4
4
from __future__ import (absolute_import , division , print_function )
5
+ import os
5
6
__metaclass__ = type
6
7
7
8
from ansible_collections .solace .pubsub_plus .plugins .module_utils import solace_sys
8
9
from ansible_collections .solace .pubsub_plus .plugins .module_utils .solace_utils import SolaceUtils
9
10
from ansible_collections .solace .pubsub_plus .plugins .module_utils .solace_consts import SolaceTaskOps
10
- from ansible_collections .solace .pubsub_plus .plugins .module_utils .solace_error import SolaceCloudApiResponseDataError , SolaceInternalErrorAbstractMethod , SolaceApiError , SolaceParamsValidationError
11
+ from ansible_collections .solace .pubsub_plus .plugins .module_utils .solace_error import SolaceCloudApiResponseDataError , SolaceEnvVarError , SolaceInternalErrorAbstractMethod , SolaceApiError , SolaceParamsValidationError
11
12
from ansible_collections .solace .pubsub_plus .plugins .module_utils .solace_error import SolaceInternalError
12
13
from ansible_collections .solace .pubsub_plus .plugins .module_utils .solace_task_config import SolaceTaskConfig , SolaceTaskBrokerConfig , SolaceTaskSolaceCloudConfig
13
14
from ansible .module_utils .basic import AnsibleModule
@@ -489,7 +490,12 @@ def get_objects(self, config: SolaceTaskBrokerConfig, xml_cmd: str, reponse_list
489
490
490
491
class SolaceCloudApi (SolaceApi ):
491
492
492
- API_BASE_PATH = "https://api.solace.cloud/api/v0"
493
+ ENV_VAR_ANSIBLE_SOLACE_SOLACE_CLOUD_HOME = "ANSIBLE_SOLACE_SOLACE_CLOUD_HOME"
494
+ ANSIBLE_SOLACE_SOLACE_CLOUD_HOME_US = "us"
495
+ ANSIBLE_SOLACE_SOLACE_CLOUD_HOME_AU = "au"
496
+ API_BASE_PATH_US = "https://api.solace.cloud/api/v0"
497
+ API_BASE_PATH_AU = "https://api.solacecloud.com.au/api/v0"
498
+
493
499
API_DATA_CENTERS = "datacenters"
494
500
API_SERVICES = "services"
495
501
API_REQUESTS = "requests"
@@ -498,6 +504,28 @@ def __init__(self, module: AnsibleModule):
498
504
super ().__init__ (module )
499
505
return
500
506
507
+ def get_api_base_path (self , config : SolaceTaskSolaceCloudConfig ) -> str :
508
+ solace_cloud_home_value = self .ANSIBLE_SOLACE_SOLACE_CLOUD_HOME_US
509
+
510
+ if config .solace_cloud_home is not None and config .solace_cloud_home != '' :
511
+ solace_cloud_home_value = config .solace_cloud_home .lower ()
512
+ else :
513
+ solaceCloudHomeEnvVal = os .getenv (
514
+ self .ENV_VAR_ANSIBLE_SOLACE_SOLACE_CLOUD_HOME )
515
+ if solaceCloudHomeEnvVal is not None and solaceCloudHomeEnvVal != '' :
516
+ if solaceCloudHomeEnvVal .lower () == self .ANSIBLE_SOLACE_SOLACE_CLOUD_HOME_US :
517
+ solace_cloud_home_value = self .ANSIBLE_SOLACE_SOLACE_CLOUD_HOME_US
518
+ elif solaceCloudHomeEnvVal .lower () == self .ANSIBLE_SOLACE_SOLACE_CLOUD_HOME_AU :
519
+ solace_cloud_home_value = self .ANSIBLE_SOLACE_SOLACE_CLOUD_HOME_AU
520
+ else :
521
+ raise SolaceEnvVarError (self .ENV_VAR_ANSIBLE_SOLACE_SOLACE_CLOUD_HOME ,
522
+ solaceCloudHomeEnvVal ,
523
+ f"allowed values: { self .ANSIBLE_SOLACE_SOLACE_CLOUD_HOME_US } , { self .ANSIBLE_SOLACE_SOLACE_CLOUD_HOME_AU } " )
524
+ if solace_cloud_home_value == self .ANSIBLE_SOLACE_SOLACE_CLOUD_HOME_US :
525
+ return self .API_BASE_PATH_US
526
+ else :
527
+ return self .API_BASE_PATH_AU
528
+
501
529
def get_auth (self , config : SolaceTaskBrokerConfig ) -> str :
502
530
return config .get_solace_cloud_auth ()
503
531
@@ -524,7 +552,7 @@ def handle_bad_response(self, resp, module_op):
524
552
def get_data_centers (self , config : SolaceTaskSolaceCloudConfig ) -> list :
525
553
# GET /api/v0/datacenters
526
554
resp = self .make_get_request (
527
- config , [self .API_BASE_PATH , self .API_DATA_CENTERS ], query_params = None )
555
+ config , [self .get_api_base_path ( config ) , self .API_DATA_CENTERS ], query_params = None )
528
556
return resp
529
557
530
558
def _transform_service (self , service : dict ) -> dict :
@@ -546,7 +574,7 @@ def get_services(self, config: SolaceTaskSolaceCloudConfig) -> list:
546
574
module_op = SolaceTaskOps .OP_READ_OBJECT_LIST
547
575
try :
548
576
_resp = self .make_get_request (
549
- config , [self .API_BASE_PATH , self .API_SERVICES ], module_op )
577
+ config , [self .get_api_base_path ( config ) , self .API_SERVICES ], module_op )
550
578
except SolaceApiError as e :
551
579
resp = e .get_resp ()
552
580
# TODO: what is the code if solace cloud account has 0 services?
@@ -582,7 +610,7 @@ def get_service(self, config: SolaceTaskSolaceCloudConfig, service_id: str) -> d
582
610
module_op = SolaceTaskOps .OP_READ_OBJECT
583
611
try :
584
612
_resp = self .make_get_request (
585
- config , [self .API_BASE_PATH , self .API_SERVICES , service_id ], module_op )
613
+ config , [self .get_api_base_path ( config ) , self .API_SERVICES , service_id ], module_op )
586
614
except SolaceApiError as e :
587
615
resp = e .get_resp ()
588
616
if resp ['status_code' ] == 404 :
@@ -603,7 +631,7 @@ def create_service(self, config: SolaceTaskSolaceCloudConfig, wait_timeout_minut
603
631
# POST https://api.solace.cloud/api/v0/services
604
632
module_op = SolaceTaskOps .OP_CREATE_OBJECT
605
633
resp = self .make_post_request (
606
- config , [self .API_BASE_PATH , self .API_SERVICES ], data , module_op )
634
+ config , [self .get_api_base_path ( config ) , self .API_SERVICES ], data , module_op )
607
635
_service_id = resp ['serviceId' ]
608
636
if wait_timeout_minutes > 0 :
609
637
res = self .wait_for_service_create_completion (
@@ -671,7 +699,7 @@ def wait_for_service_create_completion(self, config: SolaceTaskSolaceCloudConfig
671
699
672
700
def delete_service (self , config : SolaceTaskSolaceCloudConfig , service_id : str ) -> dict :
673
701
# DELETE https://api.solace.cloud/api/v0/services/{{serviceId}}
674
- path_array = [SolaceCloudApi . API_BASE_PATH ,
702
+ path_array = [self . get_api_base_path ( config ) ,
675
703
SolaceCloudApi .API_SERVICES , service_id ]
676
704
return self .make_delete_request (config , path_array )
677
705
@@ -702,7 +730,7 @@ def get_object_settings(self, config: SolaceTaskBrokerConfig, path_array: list)
702
730
def get_service_request_status (self , config : SolaceTaskBrokerConfig , service_id : str , request_id : str ):
703
731
module_op = SolaceTaskOps .OP_READ_OBJECT
704
732
# GET https://api.solace.cloud/api/v0/services/{paste-your-serviceId-here}/requests/{{requestId}}
705
- path_array = [self .API_BASE_PATH , self .API_SERVICES ,
733
+ path_array = [self .get_api_base_path ( config ) , self .API_SERVICES ,
706
734
service_id , self .API_REQUESTS , request_id ]
707
735
resp = self .make_get_request (config , path_array , module_op )
708
736
# resp may not yet contain 'adminProgress' depending on whether this creation has started yet
@@ -711,16 +739,62 @@ def get_service_request_status(self, config: SolaceTaskBrokerConfig, service_id:
711
739
resp ['adminProgress' ] = 'inProgress'
712
740
return resp
713
741
742
+ def wait_for_service_requests_to_finish (self , config : SolaceTaskBrokerConfig , timeout_minutes : int , service_id : str ):
743
+ module_op = SolaceTaskOps .OP_READ_OBJECT
744
+ # GET https://api.solace.cloud/api/v0/services/{paste-your-serviceId-here}/requests
745
+ # returns list of dicts,
746
+ # - check all elements,
747
+ # - if "adminProgress" == "inProgress", wait and try again
748
+ # - raise SolaceApiError if timeout
749
+ are_all_completed = False
750
+ try_count = - 1
751
+ delay = 30 # seconds
752
+ max_retries = (timeout_minutes * 60 ) // delay
753
+ while not are_all_completed and try_count < max_retries :
754
+ path_array = [self .get_api_base_path (config ), self .API_SERVICES ,
755
+ service_id , self .API_REQUESTS ]
756
+ resp = self .make_get_request (config , path_array , module_op )
757
+ # logging.debug(f"wait_for_service_requests_to_finish(): resp=\n{json.dumps(resp, indent=2)}")
758
+ # iterate through list to check if any adminProgress == inProgress
759
+ # use generator:
760
+ matches = (
761
+ respElem for respElem in resp if respElem ['adminProgress' ] == 'inProgress' )
762
+ notCompletedElement = next (matches , None )
763
+ if notCompletedElement is None :
764
+ are_all_completed = True
765
+ try_count += 1
766
+ if not are_all_completed and timeout_minutes > 0 :
767
+ time .sleep (delay )
768
+
769
+ if not are_all_completed :
770
+ msg = [
771
+ "timeout waiting for all outstanding service requests to be completed" ,
772
+ f"timeout(mins)={ timeout_minutes } " ,
773
+ "request in progress:" ,
774
+ str (notCompletedElement )]
775
+ raise SolaceApiError (
776
+ resp , msg , self .get_module ()._name , module_op )
777
+ return
778
+
714
779
def make_service_post_request (self , config : SolaceTaskBrokerConfig , path_array : list , service_id : str , json_body , module_op ):
780
+
781
+ timeout_minutes = config .get_timeout () // 60
782
+ # set min timeout to 5 mins
783
+ timeout_minutes = max (timeout_minutes , 5 )
784
+
785
+ # check if there are any jobs still running against this service and wait until completed
786
+ self .wait_for_service_requests_to_finish (
787
+ config , timeout_minutes , service_id )
788
+
789
+ # now make the request
715
790
resp = self .make_request (config , requests .post , path_array , json_body )
716
791
# import logging, json
717
792
# logging.debug(f"resp (make_request) = \n{json.dumps(resp, indent=2)}")
718
793
request_id = resp ['id' ]
719
- timeout_minutes = 2
720
794
is_completed = False
721
795
is_failed = False
722
796
try_count = - 1
723
- delay = 5 # seconds
797
+ delay = 15 # seconds
724
798
max_retries = (timeout_minutes * 60 ) // delay
725
799
# wait 1 cycle before start polling
726
800
time .sleep (delay )
@@ -741,7 +815,7 @@ def make_service_post_request(self, config: SolaceTaskBrokerConfig, path_array:
741
815
resp , resp , self .get_module ()._name , module_op )
742
816
if not is_completed :
743
817
msg = [
744
- f"timeout service post request - not completed, state={ resp ['adminProgress' ]} " , str (resp )]
818
+ f"timeout service post request - not completed, timeout(mins)= { timeout_minutes } , state={ resp ['adminProgress' ]} " , str (resp )]
745
819
raise SolaceInternalError (msg )
746
820
return resp
747
821
@@ -794,7 +868,7 @@ def filter(self, settings: dict, query_params: dict) -> dict:
794
868
795
869
def get_cert_authority (self , config , service_id , cert_authority_name , query_params ):
796
870
# GET services/{serviceId}/serviceCertificateAuthorities/{certAuthorityName}
797
- path_array = [SolaceCloudApi . API_BASE_PATH , SolaceCloudApi .API_SERVICES ,
871
+ path_array = [self . get_api_base_path ( config ) , SolaceCloudApi .API_SERVICES ,
798
872
service_id , 'serviceCertificateAuthorities' , cert_authority_name ]
799
873
resp = self .get_object_settings (config , path_array )
800
874
cert_authority = resp ['certificate' ]
0 commit comments