def find_connection_id(client, connection_id=None, connection_name=None): params = {} if connection_id: params['connectionId'] = connection_id try: response = describe_connections(client, params) except (BotoCoreError, ClientError) as e: if connection_id: msg = "Failed to describe DirectConnect ID {0}".format( connection_id) else: msg = "Failed to describe DirectConnect connections" raise DirectConnectError(msg=msg, last_traceback=traceback.format_exc(), exception=e) match = [] if len(response.get('connections', [])) == 1 and connection_id: if response['connections'][0]['connectionState'] != 'deleted': match.append(response['connections'][0]['connectionId']) for conn in response.get('connections', []): if connection_name == conn[ 'connectionName'] and conn['connectionState'] != 'deleted': match.append(conn['connectionId']) if len(match) == 1: return match[0] else: raise DirectConnectError( msg="Could not find a valid DirectConnect connection")
def update_lag(client, lag_id, lag_name, min_links, num_connections, wait, wait_timeout): start = time.time() if min_links and min_links > num_connections: raise DirectConnectError( msg= "The number of connections {0} must be greater than the minimum number of links " "{1} to update the LAG {2}".format(num_connections, min_links, lag_id), last_traceback=None, exception=None) while True: try: _update_lag(client, lag_id, lag_name, min_links) except botocore.exceptions.ClientError as e: if wait and time.time() - start <= wait_timeout: continue msg = "Failed to update Direct Connect link aggregation group {0}.".format( lag_id) if "MinimumLinks cannot be set higher than the number of connections" in e.response[ 'Error']['Message']: msg += "Unable to set the min number of links to {0} while the LAG connections are being requested".format( min_links) raise DirectConnectError(msg=msg, last_traceback=traceback.format_exc(), exception=e) else: break
def create_lag(client, num_connections, location, bandwidth, name, connection_id): if not name: raise DirectConnectError( msg= "Failed to create a Direct Connect link aggregation group: name required.", last_traceback=None, exception="") parameters = dict(numberOfConnections=num_connections, location=location, connectionsBandwidth=bandwidth, lagName=name) if connection_id: parameters.update(connectionId=connection_id) try: lag = client.create_lag(**parameters) except botocore.exceptions.ClientError as e: raise DirectConnectError( msg="Failed to create DirectConnect link aggregation group {0}". format(name), last_traceback=traceback.format_exc(), exception=e) return lag['lagId']
def delete_lag(client, lag_id): try: client.delete_lag(lagId=lag_id) except botocore.exceptions.ClientError as e: raise DirectConnectError(msg="Failed to delete Direct Connect link aggregation group {0}.".format(lag_id), last_traceback=traceback.format_exc(), exception=e)
def ensure_present(client, num_connections, lag_id, lag_name, location, bandwidth, connection_id, min_links, wait, wait_timeout): exists = lag_exists(client, lag_id, lag_name) if not exists and lag_id: raise DirectConnectError( msg="The Direct Connect link aggregation group {0} does not exist." .format(lag_id), last_traceback=None, exception="") # the connection is found; get the latest state and see if it needs to be updated if exists: lag_id = exists latest_state = lag_status(client, lag_id) if lag_changed(latest_state, lag_name, min_links): update_lag(client, lag_id, lag_name, min_links, num_connections, wait, wait_timeout) return True, lag_id return False, lag_id # no connection found; create a new one else: lag_id = create_lag(client, num_connections, location, bandwidth, lag_name, connection_id) update_lag(client, lag_id, lag_name, min_links, num_connections, wait, wait_timeout) return True, lag_id
def describe_virtual_interfaces(client, lag_id): try: response = client.describe_virtual_interfaces(connectionId=lag_id) except botocore.exceptions.ClientError as e: raise DirectConnectError(msg="Failed to describe any virtual interfaces associated with LAG: {0}".format(lag_id), last_traceback=traceback.format_exc(), exception=e) return response.get('virtualInterfaces', [])
def get_connection_state(client, connection_id): try: response = describe_connections(client, dict(connectionId=connection_id)) return response['connections'][0]['connectionState'] except (BotoCoreError, ClientError, IndexError) as e: raise DirectConnectError(msg="Failed to describe DirectConnect connection {0} state".format(connection_id), last_traceback=traceback.format_exc(), exception=e)
def disassociate_vis(client, lag_id, virtual_interfaces): for vi in virtual_interfaces: delete_virtual_interface(client, vi['virtualInterfaceId']) try: response = client.delete_virtual_interface(virtualInterfaceId=vi['virtualInterfaceId']) except botocore.exceptions.ClientError as e: raise DirectConnectError(msg="Could not delete virtual interface {0} to delete link aggregation group {1}.".format(vi, lag_id), last_traceback=traceback.format_exc(), exception=e)
def create_connection(client, location, bandwidth, name, lag_id): if not name: raise DirectConnectError(msg="Failed to create a Direct Connect connection: name required.") params = { 'location': location, 'bandwidth': bandwidth, 'connectionName': name, } if lag_id: params['lagId'] = lag_id try: connection = AWSRetry.backoff(**retry_params)(client.create_connection)(**params) except (BotoCoreError, ClientError) as e: raise DirectConnectError(msg="Failed to create DirectConnect connection {0}".format(name), last_traceback=traceback.format_exc(), exception=e) return connection['connectionId']
def run_func(*args, **kwargs): try: result = AWSRetry.jittered_backoff( retries=8, delay=5, catch_extra_error_codes=['DirectConnectClientException' ])(f)(*args, **kwargs) except (ClientError, BotoCoreError) as e: raise DirectConnectError(failure_msg, traceback.format_exc(), e) return result
def lag_exists(client, lag_id=None, lag_name=None, verify=True): """ If verify=True, returns the LAG ID or None If verify=False, returns the LAG's data (or an empty dict) """ try: if lag_id: response = client.describe_lags(lagId=lag_id) else: response = client.describe_lags() except botocore.exceptions.ClientError as e: if lag_id and verify: return False elif lag_id: return {} else: failed_op = "Failed to describe DirectConnect link aggregation groups." raise DirectConnectError(msg=failed_op, last_traceback=traceback.format_exc(), exception=e) match = [] # List of LAG IDs that are exact matches lag = [] # List of LAG data that are exact matches # look for matching connections if len(response.get('lags', [])) == 1 and lag_id: if response['lags'][0]['lagState'] != 'deleted': match.append(response['lags'][0]['lagId']) lag.append(response['lags'][0]) else: for each in response.get('lags', []): if each['lagState'] != 'deleted': if not lag_id: if lag_name == each['lagName']: match.append(each['lagId']) else: match.append(each['lagId']) # verifying if the connections exists; if true, return connection identifier, otherwise return False if verify and len(match) == 1: return match[0] elif verify: return False # not verifying if the connection exists; just return current connection info else: if len(lag) == 1: return lag[0] else: return {}
def ensure_absent(client, lag_id, lag_name, force_delete, delete_with_disassociation, wait, wait_timeout): lag_id = lag_exists(client, lag_id, lag_name) if not lag_id: return False latest_status = lag_status(client, lag_id) # determine the associated connections and virtual interfaces to disassociate virtual_interfaces, connections = get_connections_and_virtual_interfaces( client, lag_id) # If min_links is not 0, there are associated connections, or if there are virtual interfaces, ask for force_delete if any((latest_status['minimumLinks'], virtual_interfaces, connections)) and not force_delete: raise DirectConnectError( msg= "There are a minimum number of links, hosted connections, or associated virtual interfaces for LAG {0}. " "To force deletion of the LAG use delete_force: True (if the LAG has virtual interfaces they will be deleted). " "Optionally, to ensure hosted connections are deleted after disassociation use delete_with_disassociation: True " "and wait: True (as Virtual Interfaces may take a few moments to delete)" .format(lag_id), last_traceback=None, exception=None) # update min_links to be 0 so we can remove the LAG update_lag(client, lag_id, None, 0, len(connections), wait, wait_timeout) # if virtual_interfaces and not delete_vi_with_disassociation: Raise failure; can't delete while vi attached for connection in connections: disassociate_connection_and_lag(client, connection['connectionId'], lag_id) if delete_with_disassociation: delete_connection(client, connection['connectionId']) for vi in virtual_interfaces: delete_virtual_interface(client, vi['virtualInterfaceId']) start_time = time.time() while True: try: delete_lag(client, lag_id) except DirectConnectError as e: if ('until its Virtual Interfaces are deleted' in e.exception ) and (time.time() - start_time < wait_timeout) and wait: continue else: return True
def connection_exists(client, connection_id=None, connection_name=None, verify=True): params = {} if connection_id: params['connectionId'] = connection_id try: response = AWSRetry.backoff(**retry_params)( client.describe_connections)(**params) except (BotoCoreError, ClientError) as e: if connection_id: msg = "Failed to describe DirectConnect ID {0}".format( connection_id) else: msg = "Failed to describe DirectConnect connections" raise DirectConnectError(msg=msg, last_traceback=traceback.format_exc(), exception=e) match = [] connection = [] # look for matching connections if len(response.get('connections', [])) == 1 and connection_id: if response['connections'][0]['connectionState'] != 'deleted': match.append(response['connections'][0]['connectionId']) connection.extend(response['connections']) for conn in response.get('connections', []): if connection_name == conn[ 'connectionName'] and conn['connectionState'] != 'deleted': match.append(conn['connectionId']) connection.append(conn) # verifying if the connections exists; if true, return connection identifier, otherwise return False if verify and len(match) == 1: return match[0] elif verify: return False # not verifying if the connection exists; just return current connection info elif len(connection) == 1: return {'connection': connection[0]} return {'connection': {}}