def get_locked_adom_list(self): """ Gets the list of locked adoms """ try: locked_list = list() locked_by_user_list = dict() for adom in self._adom_list: adom_lock_info = self.get_lock_info(adom=adom) self.log('lockinfo for adom:%s' % (adom)) self.log(json.dumps(adom_lock_info, indent=4)) if adom_lock_info[1]['status']['code'] != 0: continue # if 'data' is not in the response, the adom is locked by no one if 'data' not in adom_lock_info[1]: continue locked_list.append(to_text(adom)) locked_by_user_list[to_text(adom)] = to_text(adom_lock_info[1]['data'][0]['lock_user']) self._locked_adom_list = locked_list self._locked_adoms_by_user = locked_by_user_list self.log('locked adom list: %s' % (self._locked_adom_list)) self.log('locked adom and user list: %s' % (self._locked_adoms_by_user)) except Exception as err: raise FMGBaseException(msg=("An error occurred while trying to get the locked adom list. Error: " + to_text(err)))
def send_request(self, request_method, path, payload=None, headers=None, **kwargs): headers = headers if headers else BASE_HEADERS try: self._display_request(request_method) response, response_data = self.connection.send( path, payload, method=request_method, headers=headers, **kwargs) value = self._get_response_value(response_data) return response.getcode(), self._response_to_json(value) except AnsibleConnectionFailure as e: if to_text('401') in to_text(e): return 401, 'Authentication failure' else: return 404, 'Object not found' except HTTPError as e: error = json.loads(e.read()) return e.code, error
def send_request(self, request_method, path, payload=None): # payload = json.dumps(payload) if payload else '{}' try: self._display_request(request_method, path) response, response_data = self.connection.send( path, payload, method=request_method, headers=BASE_HEADERS, force_basic_auth=True, ) value = self._get_response_value(response_data) return response.getcode(), self._response_to_json(value) except AnsibleConnectionFailure as e: self.connection.queue_message("vvv", "AnsibleConnectionFailure: %s" % e) if to_text("Could not connect to") in to_text(e): raise if to_text("401") in to_text(e): return 401, "Authentication failure" else: return 404, "Object not found" except HTTPError as e: error = json.loads(e.read()) return e.code, error
def send_request(self, request_method="POST", path="/api_jsonrpc.php", payload=None): if not payload: payload = {} try: self._display_request(request_method, path) response, response_data = self.connection.send( path, payload, method=request_method, headers=BASE_HEADERS) value = to_text(response_data.getvalue()) try: json_data = json.loads(value) if value else {} if "result" in json_data: json_data = json_data["result"] # JSONDecodeError only available on Python 3.5+ except ValueError: raise ConnectionError("Invalid JSON response: %s" % value) return response.getcode(), json_data except AnsibleConnectionFailure as e: self.connection.queue_message("vvv", "AnsibleConnectionFailure: %s" % e) if to_text("Could not connect to") in to_text(e): raise if to_text("401") in to_text(e): return 401, "Authentication failure" else: return 404, "Object not found" except Exception as e: raise e
def login(self, username, password): login_path = "/rest/authentication/login/primary" data = { "dsCredentials": { "password": to_text(password), "userName": to_text(username), } } code, auth_token = self.send_request("POST", login_path, data=data) try: # This is still sent as an HTTP header, so we can set our connection's _auth # variable manually. If the token is returned to the device in another way, # you will have to keep track of it another way and make sure that it is sent # with the rest of the request from send_request() self.connection._auth = {"Cookie": "sID={0}".format(auth_token)} # Have to carry this around because variuous Trend Micro Deepsecurity REST # API endpoints want the sID as a querystring parameter instead of honoring # the session Cookie self._auth_token = auth_token except KeyError: raise AnsibleAuthenticationFailure( message="Failed to acquire login token." )
def get_locked_adom_list(self): """ Gets the list of locked adoms """ try: locked_list = list() locked_by_user_list = list() for adom in self._adom_list: adom_lock_info = self.get_lock_info(adom=adom) try: if adom_lock_info[1]["status"]["message"] == "OK": continue except IndexError as err: pass try: if adom_lock_info[1][0]["lock_user"]: locked_list.append(to_text(adom)) if adom_lock_info[1][0][ "lock_user"] == self._logged_in_user: locked_by_user_list.append({ "adom": to_text(adom), "user": to_text(adom_lock_info[1][0]["lock_user"]) }) except Exception as err: raise FMGBaseException(err) self._locked_adom_list = locked_list self._locked_adoms_by_user = locked_by_user_list except Exception as err: raise FMGBaseException(msg=( "An error occurred while trying to get the locked adom list. Error: " + to_text(err)))
def send_request(self, method, params): """ Responsible for actual sending of data to the connection httpapi base plugin. Does some formatting too. :param params: A formatted dictionary that was returned by self.common_datagram_params() before being called here. :param method: The preferred API Request method (GET, ADD, POST, etc....) :type method: basestring :return: Dictionary of status, if it logged in or not. """ try: if self.sid is None and params[0]["url"] != "sys/login/user": try: self.connection._connect() except Exception as err: raise FMGBaseException( msg= "An problem happened with the httpapi plugin self-init connection process. " "Error: " + to_text(err)) except IndexError: raise FMGBaseException( "An attempt was made at communicating with a FMG with " "no valid session and an incorrectly formatted request.") except Exception as err: raise FMGBaseException( "An attempt was made at communicating with a FMG with " "no valid session and an unexpected error was discovered. \n Error: " + to_text(err)) self._update_request_id() json_request = { "method": method, "params": params, "session": self.sid, "id": self.req_id, "verbose": 1 } data = json.dumps(json_request, ensure_ascii=False).replace('\\\\', '\\') try: # Sending URL and Data in Unicode, per Ansible Specifications for Connection Plugins response, response_data = self.connection.send( path=to_text(self._url), data=to_text(data), headers=BASE_HEADERS) # Get Unicode Response - Must convert from StringIO to unicode first so we can do a replace function below result = json.loads(to_text(response_data.getvalue())) self._update_self_from_response(result, self._url, data) return self._handle_response(result) except Exception as err: raise FMGBaseException(err)
def _validate_response(http_code, http_response): # XML API piggybacks on HTTP 400 and 403 error codes. if http_code not in [200, 400, 403]: raise ConnectionError("Invalid response from API") data = to_text(http_response) root = ET.fromstring(data) display.vvvv("_validate_response(): response = {0}".format(data)) status = root.attrib.get("status") api_code = root.attrib.get("code") msg = root.findtext(".//msg/line") # Successful responses that AREN'T keygen type all have 'success' # attributes. if status != "success": message = "" if api_code and api_code in PANOS_API_CODES: message = "{0} ({1}): {2}".format(api_code, PANOS_API_CODES[api_code], msg) else: message = "{0}".format(msg) raise ConnectionError(message) # For whatever reason, Ansible wants a JSON serializable response ONLY, # so return unparsed data. return data
def send_request(self, **message_kwargs): """ Responsible for actual sending of data to the connection httpapi base plugin. :param message_kwargs: A formatted dictionary containing request info: url, data, method :return: Status code and response data. """ url = message_kwargs.get('url', '/') data = message_kwargs.get('data', '') method = message_kwargs.get('method', 'GET') if self._ccsrftoken == '' and not (method == 'POST' and 'logincheck' in url): raise Exception('Not logged in. Please login first') headers = {} if self._ccsrftoken != '': headers['x-csrftoken'] = self._ccsrftoken if method == 'POST' or 'PUT': headers['Content-Type'] = 'application/json' try: response, response_data = self._connection.send(url, data, headers=headers, method=method) return response.status, to_text(response_data.getvalue()) except Exception as err: raise Exception(err)
def send_request(self, url_path, http_method, body_params=None, path_params=None, query_params=None): url = construct_url_path(url_path, path_params, query_params) data = json.dumps(body_params) if body_params else None try: self._display(http_method, 'url', url) if data: self._display(http_method, 'data', data) response, response_data = self.connection.send( url, data, method=http_method, headers=BASE_HEADERS) value = self._get_response_value(response_data) self._display(http_method, 'response', value) return { ResponseParams.SUCCESS: True, ResponseParams.STATUS_CODE: response.getcode(), ResponseParams.RESPONSE: self._response_to_json(value) } # Being invoked via JSON-RPC, this method does not serialize and pass HTTPError correctly to the method caller. # Thus, in order to handle non-200 responses, we need to wrap them into a simple structure and pass explicitly. except HTTPError as e: error_msg = to_text(e.read()) self._display(http_method, 'error', error_msg) return { ResponseParams.SUCCESS: False, ResponseParams.STATUS_CODE: e.code, ResponseParams.RESPONSE: self._response_to_json(error_msg) }
def get_devices(self, name=None, osversion=None, devicefamily=None, serialnumber=None, platform=None, managedstatus=None, connectionstatus=None, attribute_column_0=None, ip_address=None): ''' Querries Space API and returns list of any devices matching filter(s) or None ''' query_strs = [] self.space_request.headers = { "Accept": "application/vnd.net.juniper.space.device-management.devices+json;version=2" } if name: query_strs.append(quote("name eq '{0}'".format(to_text(name)))) if osversion: query_strs.append(quote("OSversion eq '{0}'".format(osversion))) if devicefamily: query_strs.append( quote("deviceFamily eq '{0}'".format(devicefamily))) if serialnumber: query_strs.append( quote("SerialNumber eq '{0}'".format(serialnumber))) if platform: query_strs.append(quote("platform eq '{0}'".format(platform))) if managedstatus: query_strs.append( quote("managedStatus eq '{0}'".format(managedstatus))) if connectionstatus: query_strs.append( quote("connectionStatus eq '{0}'".format(connectionstatus))) if attribute_column_0: query_strs.append( quote( "attribute-column-0 eq '{0}'".format(attribute_column_0))) if ip_address: query_strs.append(quote("ipAddr eq '{0}'".format(ip_address))) if query_strs: code, response = self.space_request.get_by_path( "/api/space/device-management/devices?filter=({0})".format( "%20and%20".join(query_strs))) return self._return_list(response['devices']) else: code, response = self.space_request.get_by_path( "/api/space/device-management/devices") return self._return_list(response['devices'])
def send_request(self, **message_kwargs): """ Responsible for actual sending of data to the connection httpapi base plugin. :param message_kwargs: A formatted dictionary containing request info: url, data, method :return: Status code and response data. """ url = message_kwargs.get('url', '/') if self.get_access_token() is not None: url = self._concat_token(message_kwargs.get('url', '/')) data = message_kwargs.get('data', '') method = message_kwargs.get('method', 'GET') params = message_kwargs.get('params', {}) url = self._concat_params(url, params) self.log('send request: METHOD:%s URL:%s DATA:%s' % (method, url, data)) try: response, response_data = self.connection.send(url, data, method=method) json_formatted = to_text(response_data.getvalue()) return response.status, json_formatted except Exception as err: raise Exception(err)
def _validate_response(http_code, http_response): # Valid XML-API responses can be contained in the following HTTP status # codes: # 400 - Bad Request (malformed request) # 403 - Forbidden (invalid credentials) # 200 - OK (HTTP request was OK, but still can be an XML-API error) if http_code not in [200, 400, 403]: raise ConnectionError("Invalid response from API") data = to_text(http_response) root = ET.fromstring(data) display.vvvv("_validate_response(): response = {0}".format(data)) status = root.attrib.get("status") api_code = root.attrib.get("code") if status == "error": msg = None # Error messages can be in multiple locations in the response. if root.findtext("./result/msg"): msg = root.findtext("./result/msg") elif len(root.findall("./msg/line")) > 0: lines = root.findall("./msg/line") msg = ", ".join([line.text for line in lines]) raise PanOSAPIError(api_code, msg) # For whatever reason, Ansible wants a JSON serializable response ONLY, # so return unparsed data. return data
def _response_to_json(response_data): response_text = to_text(response_data) try: return json.loads(response_text) if response_text else {} # JSONDecodeError only available on Python 3.5+ except getattr(json.decoder, 'JSONDecodeError', ValueError): raise ConnectionError('Invalid JSON response: %s' % response_text)
def get_s3_connection(module, aws_connect_kwargs, location, rgw, s3_url, sig_4=False): if s3_url and rgw: # TODO - test this rgw = urlparse(s3_url) params = dict(module=module, conn_type='client', resource='s3', use_ssl=rgw.scheme == 'https', region=location, endpoint=s3_url, **aws_connect_kwargs) elif is_fakes3(s3_url): fakes3 = urlparse(s3_url) port = fakes3.port if fakes3.scheme == 'fakes3s': protocol = "https" if port is None: port = 443 else: protocol = "http" if port is None: port = 80 params = dict(module=module, conn_type='client', resource='s3', region=location, endpoint="%s://%s:%s" % (protocol, fakes3.hostname, to_text(port)), use_ssl=fakes3.scheme == 'fakes3s', **aws_connect_kwargs) else: params = dict(module=module, conn_type='client', resource='s3', region=location, endpoint=s3_url, **aws_connect_kwargs) if module.params['mode'] == 'put' and module.params['encryption_mode'] == 'aws:kms': params['config'] = botocore.client.Config(signature_version='s3v4') elif module.params['mode'] in ('get', 'getstr') and sig_4: params['config'] = botocore.client.Config(signature_version='s3v4') if module.params['dualstack']: dualconf = botocore.client.Config(s3={'use_dualstack_endpoint': True}) if 'config' in params: params['config'] = params['config'].merge(dualconf) else: params['config'] = dualconf return boto3_conn(**params)
def destroy_bucket(s3_client, module): force = module.params.get("force") name = module.params.get("name") try: bucket_is_present = bucket_exists(s3_client, name) except EndpointConnectionError as e: module.fail_json_aws(e, msg="Invalid endpoint provided: %s" % to_text(e)) except (BotoCoreError, ClientError) as e: module.fail_json_aws(e, msg="Failed to check bucket presence") if not bucket_is_present: module.exit_json(changed=False) if force: # if there are contents then we need to delete them before we can delete the bucket try: for keys in paginated_list(s3_client, Bucket=name): formatted_keys = [{'Key': key} for key in keys] if formatted_keys: s3_client.delete_objects(Bucket=name, Delete={'Objects': formatted_keys}) except (BotoCoreError, ClientError) as e: module.fail_json_aws(e, msg="Failed while deleting bucket") try: delete_bucket(s3_client, name) s3_client.get_waiter('bucket_not_exists').wait(Bucket=name) except WaiterError as e: module.fail_json_aws(e, msg='An error occurred waiting for the bucket to be deleted.') except (BotoCoreError, ClientError) as e: module.fail_json_aws(e, msg="Failed to delete bucket") module.exit_json(changed=True)
def get_lock_info(self, adom=None): """ Gets ADOM lock info so it can be displayed with the error messages. Or if determined to be locked by ansible for some reason, then unlock it. """ if not adom or adom == "root": url = "/dvmdb/adom/root/workspace/lockinfo" else: if adom.lower() == "global": url = "/dvmdb/global/workspace/lockinfo/" else: url = "/dvmdb/adom/{adom}/workspace/lockinfo/".format( adom=adom) datagram = {} data = self._tools.format_request(FMGRMethods.GET, url, **datagram) resp_obj = self.send_request(FMGRMethods.GET, data) code = resp_obj[0] if code != 0: self._module.fail_json( msg=("An error occurred trying to get the ADOM Lock Info. " "Error: " + to_text(resp_obj))) elif code == 0: try: if resp_obj[1]["status"]["message"] == "OK": self._lock_info = None except Exception: self._lock_info = resp_obj[1] return resp_obj
def destroy_bucket(s3_client, module): force = module.params.get("force") name = module.params.get("name") try: bucket_is_present = bucket_exists(s3_client, name) except EndpointConnectionError as e: module.fail_json_aws(e, msg="Invalid endpoint provided: %s" % to_text(e)) except (BotoCoreError, ClientError) as e: module.fail_json_aws(e, msg="Failed to check bucket presence") if not bucket_is_present: module.exit_json(changed=False) if force: # if there are contents then we need to delete them (including versions) before we can delete the bucket try: for key_version_pairs in paginated_versions_list(s3_client, Bucket=name): formatted_keys = [{ 'Key': key, 'VersionId': version } for key, version in key_version_pairs] for fk in formatted_keys: # remove VersionId from cases where they are `None` so that # unversioned objects are deleted using `DeleteObject` # rather than `DeleteObjectVersion`, improving backwards # compatibility with older IAM policies. if not fk.get('VersionId'): fk.pop('VersionId') if formatted_keys: resp = s3_client.delete_objects( Bucket=name, Delete={'Objects': formatted_keys}) if resp.get('Errors'): module.fail_json( msg= 'Could not empty bucket before deleting. Could not delete objects: {0}' .format(', '.join( [k['Key'] for k in resp['Errors']])), errors=resp['Errors'], response=resp) except (BotoCoreError, ClientError) as e: module.fail_json_aws(e, msg="Failed while deleting bucket") try: delete_bucket(s3_client, name) s3_client.get_waiter('bucket_not_exists').wait(Bucket=name, WaiterConfig=dict( Delay=5, MaxAttempts=60)) except WaiterError as e: module.fail_json_aws( e, msg='An error occurred waiting for the bucket to be deleted.') except (BotoCoreError, ClientError) as e: module.fail_json_aws(e, msg="Failed to delete bucket") module.exit_json(changed=True)
def send_request(self, request_method, path, payload=None): data = json.dumps(payload) if payload else '{}' try: self._display_request(request_method) response, response_data = self.connection.send(path, payload, method=request_method, headers=BASE_HEADERS, force_basic_auth=True) value = self._get_response_value(response_data) return response.getcode(), self._response_to_json(value) except AnsibleConnectionFailure as e: if to_text('401') in to_text(e): return 401, 'Authentication failure' else: return 404, 'Object not found' except HTTPError as e: error = json.loads(e.read()) return e.code, error
def handle_httperror(self, exc): """ propogate exceptions to users :param exc: Exception """ self.log('Exception thrown from handling http: ' + to_text(exc)) return exc
def decode_rules_as_hcl_string(rules_as_hcl): """ Converts the given HCL (string) representation of rules into a list of rule domain models. :param rules_as_hcl: the HCL (string) representation of a collection of rules :return: the equivalent domain model to the given rules """ rules_as_hcl = to_text(rules_as_hcl) rules_as_json = hcl.loads(rules_as_hcl) return decode_rules_as_json(rules_as_json)
def send_request(self, request_method, path, payload=None, headers=None, basic_auth=False, **kwargs): headers = headers if headers else BASE_HEADERS # space platform API endpoints which are asyncrhonous require basic_auth and no JSESSIONID cookies set. if basic_auth: self.connection._auth = None try: self._display_request(request_method) response, response_data = self.connection.send(path, payload, method=request_method, headers=headers, **kwargs) value = self._get_response_value(response_data) return response.getcode(), self._response_to_json(value) except AnsibleConnectionFailure as e: if to_text('401') in to_text(e): return 401, 'Authentication failure' else: return 404, 'Object not found' except HTTPError as e: error = json.loads(e.read()) return e.code, error
def update_system_version(self): """ retrieve the system status of fortigate device """ url = '/api/v2/cmdb/system/interface?vdom=root&action=schema' status, result = self.send_request(url=url) self.log('update sys ver: ' + str(status) + ' len=' + str(len(to_text(result)))) result_json = json.loads(result) self._system_version = result_json.get('version', 'undefined') self.log('system version: %s' % (self._system_version)) self.log('ansible version: %s' % (self._ansible_fos_version))
def _send_service_request(self, path, error_msg_prefix, data=None, **kwargs): try: self._ignore_http_errors = True return self.connection.send(path, data, **kwargs) except HTTPError as e: # HttpApi connection does not read the error response from HTTPError, so we do it here and wrap it up in # ConnectionError, so the actual error message is displayed to the user. error_msg = self._response_to_json(to_text(e.read())) raise ConnectionError('%s: %s' % (error_msg_prefix, error_msg), http_code=e.code) finally: self._ignore_http_errors = False
def paginated_versioned_list_with_fallback(s3, **pagination_params): try: versioned_pg = s3.get_paginator('list_object_versions') for page in versioned_pg.paginate(**pagination_params): delete_markers = [{'Key': data['Key'], 'VersionId': data['VersionId']} for data in page.get('DeleteMarkers', [])] current_objects = [{'Key': data['Key'], 'VersionId': data['VersionId']} for data in page.get('Versions', [])] yield delete_markers + current_objects except botocore.exceptions.ClientError as e: if to_text(e.response['Error']['Code']) in IGNORE_S3_DROP_IN_EXCEPTIONS + ['AccessDenied']: for page in paginated_list(s3, **pagination_params): yield [{'Key': data['Key']} for data in page]
def _send_auth_request(self, path, data, **kwargs): try: self._ignore_http_errors = True return self.connection.send(path, data, **kwargs) except HTTPError as e: # HttpApi connection does not read the error response from HTTPError, so we do it here and wrap it up in # ConnectionError, so the actual error message is displayed to the user. error_msg = self._response_to_json(to_text(e.read())) raise ConnectionError('Server returned an error during authentication request: %s' % error_msg) finally: self._ignore_http_errors = False
def send_request(self, **message_kwargs): """ Responsible for actual sending of data to the connection httpapi base plugin. :param message_kwargs: A formatted dictionary containing request info: url, data, method :return: Status code and response data. """ url = message_kwargs.get('url', '/') if self.get_access_token() is not None: url = self._concat_token(message_kwargs.get('url', '/')) data = message_kwargs.get('data', '') method = message_kwargs.get('method', 'GET') params = message_kwargs.get('params', {}) url = self._concat_params(url, params) self.log('send request: METHOD:%s URL:%s DATA:%s' % (method, url, to_text(data)[:100] + "...")) try: response, response_data = self.connection.send(url, data, method=method) response_text = to_text(response_data.getvalue()) self.log('whole response for METHOD:%s URL:%s ' % (method, url)) self.log(to_text(response.headers['Content-Type'])) self.log(response.status) self.log(response_text[:100]) if response.headers['Content-Type'] == "application/json": return response.status, response_text else: ## convert raw text responses into a json string for some APIs like config/backup return response.status, json.dumps({ 'text': response_text, 'http_status': response.status }) except Exception as err: raise Exception(err)
def test_send_request(self, params, headers, data): self.connection_mock.send.return_value = self._send_response( 200, "<request status='success'></request>", ) (code, response) = self.plugin.send_request(data=data, params=params, headers=headers) assert code == 200 assert "success" in to_text(response)
def send_request(self, request_method, path, data={}): '''Prepares, sends and format resonse of Contrail API requests. :param str request_method: HTTP method :param str path: API path :param dict data: Request data, serializable as JSON :return: A tuple as (status_code: int, content: dict) :rtype: tuple ''' # Contrail API call may fail, and response may be empty or not in JSON format. # In such cases, a **custom** JSON content is returned as { "message": "..." } try: response, response_data = self.connection.send( path, json.dumps(data), method=request_method, headers=BASE_HEADERS) # Request succeed if response.getcode() in [ 200, ]: try: content = json.loads(to_text(response_data.getvalue())) # Request succeed, but response not in JSON -> Generate custom response content except Exception: content = {"message": to_text(response_data.getvalue())} return response.getcode(), content # Request failed -> Generate custom response content else: return response.getcode(), { "message": to_text(response_data.getvalue()) } # Generic errors -> Generate custom response content except AnsibleConnectionFailure as error: if to_text('401') in to_text(error): return 401, {"message": "Authentication failure"} except HTTPError as error: return error.code, json.loads(error.read())
def get_adom_list(self): """ Gets the list of ADOMs for the FortiManager """ if self._uses_adoms: url = "/dvmdb/adom" datagram = {} data = self._tools.format_request(FMGRMethods.GET, url, **datagram) resp_obj = self.send_request(FMGRMethods.GET, data) code = resp_obj[0] if code != 0: self._module.fail_json(msg=("An error occurred trying to get the ADOM Info. " "Error: " + to_text(resp_obj))) elif code == 0: num_of_adoms = len(resp_obj[1]['data']) append_list = ['root', 'global'] for adom in resp_obj[1]['data']: if adom["tab_status"] != "": append_list.append(to_text(adom["name"])) self._adom_list = append_list self.log('adom list: %s' % (str(self._adom_list))) return resp_obj
def handle_errors(error): try: error_data = json.loads(error.read()) except ValueError: error_data = error.read() if error_data: if "errors" in error_data: errors = error_data["errors"]["error"] error_text = "\n".join( (error["error-message"] for error in errors) ) else: error_text = error_data return error_text return to_text(error)
def bucket_check(module, s3, bucket, validate=True): exists = True try: s3.head_bucket(Bucket=bucket) except botocore.exceptions.ClientError as e: # If a client error is thrown, then check that it was a 404 error. # If it was a 404 error, then the bucket does not exist. error_code = int(e.response['Error']['Code']) if error_code == 404: exists = False elif error_code == 403 and validate is False: pass else: module.fail_json(msg="Failed while looking up bucket (during bucket_check) %s." % bucket, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) except botocore.exceptions.EndpointConnectionError as e: module.fail_json(msg="Invalid endpoint provided: %s" % to_text(e), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) return exists
def get_s3_client(module, aws_connect_kwargs, location, ceph, s3_url): if s3_url and ceph: # TODO - test this ceph = urlparse(s3_url) params = dict(module=module, conn_type='client', resource='s3', use_ssl=ceph.scheme == 'https', region=location, endpoint=s3_url, **aws_connect_kwargs) elif is_fakes3(s3_url): fakes3 = urlparse(s3_url) port = fakes3.port if fakes3.scheme == 'fakes3s': protocol = "https" if port is None: port = 443 else: protocol = "http" if port is None: port = 80 params = dict(module=module, conn_type='client', resource='s3', region=location, endpoint="%s://%s:%s" % (protocol, fakes3.hostname, to_text(port)), use_ssl=fakes3.scheme == 'fakes3s', **aws_connect_kwargs) elif is_walrus(s3_url): walrus = urlparse(s3_url).hostname params = dict(module=module, conn_type='client', resource='s3', region=location, endpoint=walrus, **aws_connect_kwargs) else: params = dict(module=module, conn_type='client', resource='s3', region=location, endpoint=s3_url, **aws_connect_kwargs) return boto3_conn(**params)
def get_s3_connection(module, aws_connect_kwargs, location, rgw, s3_url): if s3_url and rgw: # TODO - test this rgw = urlparse(s3_url) params = dict(module=module, conn_type='client', resource='s3', use_ssl=rgw.scheme == 'https', region=location, endpoint=s3_url, **aws_connect_kwargs) elif is_fakes3(s3_url): for kw in ['is_secure', 'host', 'port'] and list(aws_connect_kwargs.keys()): del aws_connect_kwargs[kw] fakes3 = urlparse(s3_url) if fakes3.scheme == 'fakes3s': protocol = "https" else: protocol = "http" params = dict(service_name='s3', endpoint_url="%s://%s:%s" % (protocol, fakes3.hostname, to_text(fakes3.port)), use_ssl=fakes3.scheme == 'fakes3s', region_name=None, **aws_connect_kwargs) elif is_walrus(s3_url): walrus = urlparse(s3_url).hostname params = dict(module=module, conn_type='client', resource='s3', region=location, endpoint=walrus, **aws_connect_kwargs) else: params = dict(module=module, conn_type='client', resource='s3', region=location, endpoint=s3_url, **aws_connect_kwargs) return boto3_conn(**params)
def create_or_update_bucket(s3_client, module, location): policy = module.params.get("policy") name = module.params.get("name") requester_pays = module.params.get("requester_pays") tags = module.params.get("tags") versioning = module.params.get("versioning") changed = False try: bucket_is_present = bucket_exists(s3_client, name) except EndpointConnectionError as e: module.fail_json_aws(e, msg="Invalid endpoint provided: %s" % to_text(e)) except (BotoCoreError, ClientError) as e: module.fail_json_aws(e, msg="Failed to check bucket presence") if not bucket_is_present: try: bucket_changed = create_bucket(s3_client, name, location) s3_client.get_waiter('bucket_exists').wait(Bucket=name) changed = changed or bucket_changed except WaiterError as e: module.fail_json_aws(e, msg='An error occurred waiting for the bucket to become available') except (BotoCoreError, ClientError) as e: module.fail_json_aws(e, msg="Failed while creating bucket") # Versioning try: versioning_status = get_bucket_versioning(s3_client, name) except (ClientError, BotoCoreError) as e: module.fail_json_aws(e, msg="Failed to get bucket versioning") if versioning is not None: required_versioning = None if versioning and versioning_status.get('Status') != "Enabled": required_versioning = 'Enabled' elif not versioning and versioning_status.get('Status') == "Enabled": required_versioning = 'Suspended' if required_versioning: try: put_bucket_versioning(s3_client, name, required_versioning) changed = True except (BotoCoreError, ClientError) as e: module.fail_json_aws(e, msg="Failed to update bucket versioning") versioning_status = wait_versioning_is_applied(module, s3_client, name, required_versioning) # This output format is there to ensure compatibility with previous versions of the module versioning_return_value = { 'Versioning': versioning_status.get('Status', 'Disabled'), 'MfaDelete': versioning_status.get('MFADelete', 'Disabled'), } # Requester pays try: requester_pays_status = get_bucket_request_payment(s3_client, name) except (BotoCoreError, ClientError) as e: module.fail_json_aws(e, msg="Failed to get bucket request payment") payer = 'Requester' if requester_pays else 'BucketOwner' if requester_pays_status != payer: put_bucket_request_payment(s3_client, name, payer) requester_pays_status = wait_payer_is_applied(module, s3_client, name, payer, should_fail=False) if requester_pays_status is None: # We have seen that it happens quite a lot of times that the put request was not taken into # account, so we retry one more time put_bucket_request_payment(s3_client, name, payer) requester_pays_status = wait_payer_is_applied(module, s3_client, name, payer, should_fail=True) changed = True # Policy try: current_policy = get_bucket_policy(s3_client, name) except (ClientError, BotoCoreError) as e: module.fail_json_aws(e, msg="Failed to get bucket policy") if policy is not None: if isinstance(policy, string_types): policy = json.loads(policy) if not policy and current_policy: try: delete_bucket_policy(s3_client, name) except (BotoCoreError, ClientError) as e: module.fail_json_aws(e, msg="Failed to delete bucket policy") current_policy = wait_policy_is_applied(module, s3_client, name, policy) changed = True elif compare_policies(current_policy, policy): try: put_bucket_policy(s3_client, name, policy) except (BotoCoreError, ClientError) as e: module.fail_json_aws(e, msg="Failed to update bucket policy") current_policy = wait_policy_is_applied(module, s3_client, name, policy, should_fail=False) if current_policy is None: # As for request payement, it happens quite a lot of times that the put request was not taken into # account, so we retry one more time put_bucket_policy(s3_client, name, policy) current_policy = wait_policy_is_applied(module, s3_client, name, policy, should_fail=True) changed = True # Tags try: current_tags_dict = get_current_bucket_tags_dict(s3_client, name) except (ClientError, BotoCoreError) as e: module.fail_json_aws(e, msg="Failed to get bucket tags") if tags is not None: if current_tags_dict != tags: if tags: try: put_bucket_tagging(s3_client, name, tags) except (BotoCoreError, ClientError) as e: module.fail_json_aws(e, msg="Failed to update bucket tags") else: try: delete_bucket_tagging(s3_client, name) except (BotoCoreError, ClientError) as e: module.fail_json_aws(e, msg="Failed to delete bucket tags") wait_tags_are_applied(module, s3_client, name, tags) current_tags_dict = tags changed = True module.exit_json(changed=changed, name=name, versioning=versioning_return_value, requester_pays=requester_pays, policy=current_policy, tags=current_tags_dict)