diff --git a/lib/ansible/module_utils/aws/direct_connect.py b/lib/ansible/module_utils/aws/direct_connect.py new file mode 100644 index 00000000000..49c67534812 --- /dev/null +++ b/lib/ansible/module_utils/aws/direct_connect.py @@ -0,0 +1,86 @@ +# This code is part of Ansible, but is an independent component. +# This particular file snippet, and this file snippet only, is BSD licensed. +# Modules you write using this snippet, which is embedded dynamically by Ansible +# still belong to the author of the module, and may assign their own license +# to the complete work. +# +# Copyright (c) 2017 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +""" +This module adds shared support for Direct Connect modules. +""" + +import traceback +try: + import botocore +except ImportError: + pass +from ansible.module_utils.ec2 import camel_dict_to_snake_dict + + +class DirectConnectError(Exception): + def __init__(self, msg, last_traceback=None, response={}): + self.msg = msg + self.last_traceback = last_traceback + self.response = camel_dict_to_snake_dict(response) + + +def delete_connection(client, connection_id): + try: + client.delete_connection(connectionId=connection_id) + except botocore.exceptions.ClientError as e: + raise DirectConnectError(msg="Failed to delete DirectConnection {0}.".format(connection_id), + last_traceback=traceback.format_exc(), + response=e.response) + + +def associate_connection_and_lag(client, connection_id, lag_id): + try: + client.associate_connection_with_lag(connectionId=connection_id, + lagId=lag_id) + except botocore.exceptions.ClientError as e: + raise DirectConnectError(msg="Failed to associate Direct Connect connection {0}" + " with link aggregation group {1}.".format(connection_id, lag_id), + last_traceback=traceback.format_exc(), + response=e.response) + + +def disassociate_connection_and_lag(client, connection_id, lag_id): + try: + client.disassociate_connection_from_lag(connectionId=connection_id, + lagId=lag_id) + except botocore.exceptions.ClientError as e: + raise DirectConnectError(msg="Failed to disassociate Direct Connect connection {0}" + " from link aggregation group {1}.".format(connection_id, lag_id), + last_traceback=traceback.format_exc(), + response=e.response) + + +def delete_virtual_interface(client, virtual_interface): + try: + client.delete_virtual_interface(virtualInterfaceId=virtual_interface) + except botocore.exceptions.ClientError as e: + raise DirectConnectError(msg="Could not delete virtual interface {0}".format(virtual_interface), + last_traceback=traceback.format_exc(), + response=e.response) diff --git a/lib/ansible/modules/cloud/amazon/aws_direct_connect_link_aggregation_group.py b/lib/ansible/modules/cloud/amazon/aws_direct_connect_link_aggregation_group.py new file mode 100644 index 00000000000..a849627c049 --- /dev/null +++ b/lib/ansible/modules/cloud/amazon/aws_direct_connect_link_aggregation_group.py @@ -0,0 +1,453 @@ +#!/usr/bin/python +# Copyright (c) 2017 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + + +DOCUMENTATION = """ +--- +module: aws_direct_connect_link_aggregation_group +short_description: Manage Direct Connect LAG bundles. +description: + - Create, delete, or modify a Direct Connect link aggregation group. +version_added: "2.4" +author: "Sloane Hertel (@s-hertel)" +requirements: + - boto3 + - botocore +options: + state: + description: + - The state of the Direct Connect link aggregation group. + choices: + - present + - absent + name: + description: + - The name of the Direct Connect link aggregation group. + link_aggregation_group_id: + description: + - The ID of the Direct Connect link aggregation group. + num_connections: + description: + - The number of connections with which to intialize the link aggregation group. + min_links: + description: + - The minimum number of physical connections that must be operational for the LAG itself to be operational. + location: + description: + - The location of the link aggregation group. + bandwidth: + description: + - The bandwidth of the link aggregation group. + force_delete: + description: + - This allows the minimum number of links to be set to 0, any hosted connections disassociated, + and any virtual interfaces associated to the LAG deleted. + connection_id: + description: + - A connection ID to link with the link aggregation group upon creation. + delete_with_disassociation: + description: + - To be used with I(state=absent) to delete connections after disassociating them with the LAG. + wait: + description: + - Whether or not to wait for the operation to complete. May be useful when waiting for virtual interfaces + to be deleted. May modify the time of waiting with C(wait_timeout). + type: bool + wait_timeout: + description: + - The duration in seconds to wait if I(wait) is True. + default: 120 +""" + +EXAMPLES = """ + +# create a Direct Connect connection +- aws_direct_connect_link_aggregation_group: + state: present + location: EqDC2 + lag_id: dxlag-xxxxxxxx + bandwidth: 1Gbps + +""" + +RETURN = """ +changed: + type: str + description: Whether or not the LAG has changed. + returned: always +aws_device: + type: str + description: The AWS Direct Connection endpoint that hosts the LAG. + sample: "EqSe2-1bwfvazist2k0" + returned: when I(state=present) +connections: + type: list + description: A list of connections bundled by this LAG. + sample: + "connections": [ + { + "aws_device": "EqSe2-1bwfvazist2k0", + "bandwidth": "1Gbps", + "connection_id": "dxcon-fgzjah5a", + "connection_name": "Requested Connection 1 for Lag dxlag-fgtoh97h", + "connection_state": "down", + "lag_id": "dxlag-fgnsp4rq", + "location": "EqSe2", + "owner_account": "448830907657", + "region": "us-west-2" + } + ] + returned: when I(state=present) +connections_bandwidth: + type: str + description: The individual bandwidth of the physical connections bundled by the LAG. + sample: "1Gbps" + returned: when I(state=present) +lag_id: + type: str + description: Unique identifier for the link aggregation group. + sample: "dxlag-fgnsp4rq" + returned: when I(state=present) +lag_name: + type: str + description: User-provided name for the link aggregation group. + returned: when I(state=present) +lag_state: + type: str + description: State of the LAG. + sample: "pending" + returned: when I(state=present) +location: + type: str + description: Where the connection is located. + sample: "EqSe2" + returned: when I(state=present) +minimum_links: + type: int + description: The minimum number of physical connections that must be operational for the LAG itself to be operational. + returned: when I(state=present) +number_of_connections: + type: int + description: The number of physical connections bundled by the LAG. + returned: when I(state=present) +owner_account: + type: str + description: Owner account ID of the LAG. + returned: when I(state=present) +region: + type: str + description: The region in which the LAG exists. + returned: when I(state=present) +""" + +from ansible.module_utils.ec2 import (camel_dict_to_snake_dict, ec2_argument_spec, HAS_BOTO3, + get_aws_connection_info, boto3_conn, AWSRetry) +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.aws.direct_connect import (delete_connection, + delete_virtual_interface, + disassociate_connection_and_lag) +import traceback +import time + +try: + import botocore +except: + pass + # handled by imported HAS_BOTO3 + + +class DirectConnectError(Exception): + def __init__(self, msg, last_traceback, response): + self.msg = msg + self.last_traceback = last_traceback + self.response = response + + +def lag_status(client, lag_id): + return lag_exists(client, lag_id=lag_id, lag_name=None, verify=False) + + +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(), + response=e.response) + + 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 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.") + + 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(), + response=e.response) + + 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(), + response=e.response) + + +@AWSRetry.backoff(tries=5, delay=2, backoff=2.0, catch_extra_error_codes=['DirectConnectClientException']) +def _update_lag(client, lag_id, lag_name, min_links): + params = {} + if min_links: + params.update(minimumLinks=min_links) + if lag_name: + params.update(lagName=lag_name) + + client.update_lag(lagId=lag_id, **params) + + +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, + response=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(), + response=e.response) + else: + break + + +def lag_changed(current_status, name, min_links): + """ Determines if a modifiable link aggregation group attribute has been modified. """ + return (name and name != current_status['lagName']) or (min_links and min_links != current_status['minimumLinks']) + + +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, response="") + + # 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(), + response=e.response) + return response.get('virtualInterfaces', []) + + +def get_connections_and_virtual_interfaces(client, lag_id): + virtual_interfaces = describe_virtual_interfaces(client, lag_id) + connections = lag_status(client, lag_id=lag_id).get('connections', []) + return virtual_interfaces, connections + + +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(), + response=e.response) + + +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) + + # determinine 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 disassocation use delete_with_disassocation: True " + "and wait: True (as Virtual Interfaces may take a few moments to delete)".format(lag_id), + last_traceback=None, + response=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.response) and (time.time() - start_time < wait_timeout) and wait: + continue + else: + return True + + +def main(): + argument_spec = ec2_argument_spec() + argument_spec.update(dict( + state=dict(required=True, choices=['present', 'absent']), + name=dict(), + link_aggregation_group_id=dict(), + num_connections=dict(type='int'), + min_links=dict(type='int'), + location=dict(), + bandwidth=dict(), + connection_id=dict(), + delete_with_disassociation=dict(type='bool', default=False), + force_delete=dict(type='bool', default=False), + wait=dict(type='bool', default=False), + wait_timeout=dict(type='int', default=120), + )) + + module = AnsibleModule(argument_spec=argument_spec, + required_one_of=[('link_aggregation_group_id', 'name')], + required_if=[('state', 'present', ('location', 'bandwidth'))]) + + if not HAS_BOTO3: + module.fail_json(msg='boto3 required for this module') + + region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True) + if not region: + module.fail_json(msg="Either region or AWS_REGION or EC2_REGION environment variable or boto config aws_region or ec2_region must be set.") + + connection = boto3_conn(module, conn_type='client', + resource='directconnect', region=region, + endpoint=ec2_url, **aws_connect_kwargs) + + state = module.params.get('state') + try: + if state == 'present': + changed, lag_id = ensure_present(connection, + num_connections=module.params.get("num_connections"), + lag_id=module.params.get("link_aggregation_group_id"), + lag_name=module.params.get("name"), + location=module.params.get("location"), + bandwidth=module.params.get("bandwidth"), + connection_id=module.params.get("connection_id"), + min_links=module.params.get("min_links"), + wait=module.params.get("wait"), + wait_timeout=module.params.get("wait_timeout")) + response = lag_status(connection, lag_id) + elif state == "absent": + changed = ensure_absent(connection, + lag_id=module.params.get("link_aggregation_group_id"), + lag_name=module.params.get("name"), + force_delete=module.params.get("force_delete"), + delete_with_disassociation=module.params.get("delete_with_disassociation"), + wait=module.params.get('wait'), + wait_timeout=module.params.get('wait_timeout')) + response = {} + except DirectConnectError as e: + if e.response: + module.fail_json(msg=e.msg, exception=e.last_traceback, **e.response) + elif e.last_traceback: + module.fail_json(msg=e.msg, exception=e.last_traceback) + else: + module.fail_json(msg=e.msg) + + module.exit_json(changed=changed, **camel_dict_to_snake_dict(response)) + + +if __name__ == '__main__': + main() diff --git a/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections/directconnect.DeleteConnection_1.json b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections/directconnect.DeleteConnection_1.json new file mode 100644 index 00000000000..3916cfc4be5 --- /dev/null +++ b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections/directconnect.DeleteConnection_1.json @@ -0,0 +1,23 @@ +{ + "data": { + "bandwidth": "1Gbps", + "connectionName": "Requested Connection 1 for Lag dxlag-fgkk4dja", + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "HTTPHeaders": { + "content-type": "application/x-amz-json-1.1", + "x-amzn-requestid": "bf2372eb-70a7-11e7-83ab-ef16f9ac5159", + "content-length": "216", + "date": "Mon, 24 Jul 2017 19:39:02 GMT" + }, + "RequestId": "bf2372eb-70a7-11e7-83ab-ef16f9ac5159" + }, + "location": "EqSe2", + "ownerAccount": "448830907657", + "region": "us-west-2", + "connectionState": "deleted", + "connectionId": "dxcon-ffx41o23" + }, + "status_code": 200 +} \ No newline at end of file diff --git a/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections/directconnect.DeleteLag_1.json b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections/directconnect.DeleteLag_1.json new file mode 100644 index 00000000000..6fcdcb87f80 --- /dev/null +++ b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections/directconnect.DeleteLag_1.json @@ -0,0 +1,27 @@ +{ + "data": { + "lagState": "deleted", + "location": "EqSe2", + "region": "us-west-2", + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "HTTPHeaders": { + "content-type": "application/x-amz-json-1.1", + "x-amzn-requestid": "bf437e0e-70a7-11e7-83ab-ef16f9ac5159", + "content-length": "266", + "date": "Mon, 24 Jul 2017 19:39:02 GMT" + }, + "RequestId": "bf437e0e-70a7-11e7-83ab-ef16f9ac5159" + }, + "lagId": "dxlag-fgkk4dja", + "awsDevice": "EqSe2-1bwfvazist2k0", + "connections": [], + "connectionsBandwidth": "1Gbps", + "minimumLinks": 0, + "ownerAccount": "448830907657", + "numberOfConnections": 0, + "lagName": "ansible_lag_1" + }, + "status_code": 200 +} \ No newline at end of file diff --git a/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections/directconnect.DescribeLags_1.json b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections/directconnect.DescribeLags_1.json new file mode 100644 index 00000000000..3f97b141f15 --- /dev/null +++ b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections/directconnect.DescribeLags_1.json @@ -0,0 +1,42 @@ +{ + "data": { + "lags": [ + { + "awsDevice": "EqSe2-1bwfvazist2k0", + "connections": [ + { + "bandwidth": "1Gbps", + "connectionName": "Requested Connection 1 for Lag dxlag-fgkk4dja", + "location": "EqSe2", + "ownerAccount": "448830907657", + "lagId": "dxlag-fgkk4dja", + "region": "us-west-2", + "connectionState": "requested", + "connectionId": "dxcon-ffx41o23" + } + ], + "lagState": "pending", + "minimumLinks": 0, + "location": "EqSe2", + "connectionsBandwidth": "1Gbps", + "ownerAccount": "448830907657", + "region": "us-west-2", + "numberOfConnections": 1, + "lagName": "ansible_lag_1", + "lagId": "dxlag-fgkk4dja" + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "HTTPHeaders": { + "content-type": "application/x-amz-json-1.1", + "x-amzn-requestid": "bd224baf-70a7-11e7-83ab-ef16f9ac5159", + "content-length": "520", + "date": "Mon, 24 Jul 2017 19:38:59 GMT" + }, + "RequestId": "bd224baf-70a7-11e7-83ab-ef16f9ac5159" + } + }, + "status_code": 200 +} \ No newline at end of file diff --git a/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections/directconnect.DescribeLags_2.json b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections/directconnect.DescribeLags_2.json new file mode 100644 index 00000000000..a6e0198e9db --- /dev/null +++ b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections/directconnect.DescribeLags_2.json @@ -0,0 +1,42 @@ +{ + "data": { + "lags": [ + { + "awsDevice": "EqSe2-1bwfvazist2k0", + "connections": [ + { + "bandwidth": "1Gbps", + "connectionName": "Requested Connection 1 for Lag dxlag-fgkk4dja", + "location": "EqSe2", + "ownerAccount": "448830907657", + "lagId": "dxlag-fgkk4dja", + "region": "us-west-2", + "connectionState": "requested", + "connectionId": "dxcon-ffx41o23" + } + ], + "lagState": "pending", + "minimumLinks": 0, + "location": "EqSe2", + "connectionsBandwidth": "1Gbps", + "ownerAccount": "448830907657", + "region": "us-west-2", + "numberOfConnections": 1, + "lagName": "ansible_lag_1", + "lagId": "dxlag-fgkk4dja" + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "HTTPHeaders": { + "content-type": "application/x-amz-json-1.1", + "x-amzn-requestid": "bda84490-70a7-11e7-83ab-ef16f9ac5159", + "content-length": "520", + "date": "Mon, 24 Jul 2017 19:38:59 GMT" + }, + "RequestId": "bda84490-70a7-11e7-83ab-ef16f9ac5159" + } + }, + "status_code": 200 +} \ No newline at end of file diff --git a/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections/directconnect.DescribeLags_3.json b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections/directconnect.DescribeLags_3.json new file mode 100644 index 00000000000..cb819f0f42d --- /dev/null +++ b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections/directconnect.DescribeLags_3.json @@ -0,0 +1,42 @@ +{ + "data": { + "lags": [ + { + "awsDevice": "EqSe2-1bwfvazist2k0", + "connections": [ + { + "bandwidth": "1Gbps", + "connectionName": "Requested Connection 1 for Lag dxlag-fgkk4dja", + "location": "EqSe2", + "ownerAccount": "448830907657", + "lagId": "dxlag-fgkk4dja", + "region": "us-west-2", + "connectionState": "requested", + "connectionId": "dxcon-ffx41o23" + } + ], + "lagState": "pending", + "minimumLinks": 0, + "location": "EqSe2", + "connectionsBandwidth": "1Gbps", + "ownerAccount": "448830907657", + "region": "us-west-2", + "numberOfConnections": 1, + "lagName": "ansible_lag_1", + "lagId": "dxlag-fgkk4dja" + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "HTTPHeaders": { + "content-type": "application/x-amz-json-1.1", + "x-amzn-requestid": "be79c564-70a7-11e7-83ab-ef16f9ac5159", + "content-length": "520", + "date": "Mon, 24 Jul 2017 19:39:01 GMT" + }, + "RequestId": "be79c564-70a7-11e7-83ab-ef16f9ac5159" + } + }, + "status_code": 200 +} \ No newline at end of file diff --git a/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections/directconnect.DescribeVirtualInterfaces_1.json b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections/directconnect.DescribeVirtualInterfaces_1.json new file mode 100644 index 00000000000..19a8af38959 --- /dev/null +++ b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections/directconnect.DescribeVirtualInterfaces_1.json @@ -0,0 +1,17 @@ +{ + "data": { + "virtualInterfaces": [], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "HTTPHeaders": { + "content-type": "application/x-amz-json-1.1", + "x-amzn-requestid": "be66d9a3-70a7-11e7-83ab-ef16f9ac5159", + "content-length": "24", + "date": "Mon, 24 Jul 2017 19:39:00 GMT" + }, + "RequestId": "be66d9a3-70a7-11e7-83ab-ef16f9ac5159" + } + }, + "status_code": 200 +} \ No newline at end of file diff --git a/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections/directconnect.DisassociateConnectionFromLag_1.json b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections/directconnect.DisassociateConnectionFromLag_1.json new file mode 100644 index 00000000000..70d1ed2f001 --- /dev/null +++ b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections/directconnect.DisassociateConnectionFromLag_1.json @@ -0,0 +1,23 @@ +{ + "data": { + "bandwidth": "1Gbps", + "connectionName": "Requested Connection 1 for Lag dxlag-fgkk4dja", + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "HTTPHeaders": { + "content-type": "application/x-amz-json-1.1", + "x-amzn-requestid": "bf0c687a-70a7-11e7-83ab-ef16f9ac5159", + "content-length": "218", + "date": "Mon, 24 Jul 2017 19:39:01 GMT" + }, + "RequestId": "bf0c687a-70a7-11e7-83ab-ef16f9ac5159" + }, + "location": "EqSe2", + "ownerAccount": "448830907657", + "region": "us-west-2", + "connectionState": "requested", + "connectionId": "dxcon-ffx41o23" + }, + "status_code": 200 +} \ No newline at end of file diff --git a/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections/directconnect.UpdateLag_1.json b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections/directconnect.UpdateLag_1.json new file mode 100644 index 00000000000..e464a0dd8cd --- /dev/null +++ b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections/directconnect.UpdateLag_1.json @@ -0,0 +1,38 @@ +{ + "data": { + "lagState": "pending", + "location": "EqSe2", + "region": "us-west-2", + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "HTTPHeaders": { + "content-type": "application/x-amz-json-1.1", + "x-amzn-requestid": "bef64869-70a7-11e7-83ab-ef16f9ac5159", + "content-length": "509", + "date": "Mon, 24 Jul 2017 19:39:01 GMT" + }, + "RequestId": "bef64869-70a7-11e7-83ab-ef16f9ac5159" + }, + "lagId": "dxlag-fgkk4dja", + "awsDevice": "EqSe2-1bwfvazist2k0", + "connections": [ + { + "bandwidth": "1Gbps", + "connectionName": "Requested Connection 1 for Lag dxlag-fgkk4dja", + "location": "EqSe2", + "ownerAccount": "448830907657", + "lagId": "dxlag-fgkk4dja", + "region": "us-west-2", + "connectionState": "requested", + "connectionId": "dxcon-ffx41o23" + } + ], + "connectionsBandwidth": "1Gbps", + "minimumLinks": 0, + "ownerAccount": "448830907657", + "numberOfConnections": 1, + "lagName": "ansible_lag_1" + }, + "status_code": 200 +} \ No newline at end of file diff --git a/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections_without_force_delete/directconnect.DescribeLags_1.json b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections_without_force_delete/directconnect.DescribeLags_1.json new file mode 100644 index 00000000000..1aa22210e2b --- /dev/null +++ b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections_without_force_delete/directconnect.DescribeLags_1.json @@ -0,0 +1,42 @@ +{ + "data": { + "lags": [ + { + "awsDevice": "EqSe2-1bwfvazist2k0", + "connections": [ + { + "bandwidth": "1Gbps", + "connectionName": "Requested Connection 1 for Lag dxlag-fgkk4dja", + "location": "EqSe2", + "ownerAccount": "448830907657", + "lagId": "dxlag-fgkk4dja", + "region": "us-west-2", + "connectionState": "requested", + "connectionId": "dxcon-ffx41o23" + } + ], + "lagState": "pending", + "minimumLinks": 0, + "location": "EqSe2", + "connectionsBandwidth": "1Gbps", + "ownerAccount": "448830907657", + "region": "us-west-2", + "numberOfConnections": 1, + "lagName": "ansible_lag_1", + "lagId": "dxlag-fgkk4dja" + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "HTTPHeaders": { + "content-type": "application/x-amz-json-1.1", + "x-amzn-requestid": "bc1aedd9-70a7-11e7-a2a8-21d8bda1f5ec", + "content-length": "520", + "date": "Mon, 24 Jul 2017 19:38:57 GMT" + }, + "RequestId": "bc1aedd9-70a7-11e7-a2a8-21d8bda1f5ec" + } + }, + "status_code": 200 +} \ No newline at end of file diff --git a/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections_without_force_delete/directconnect.DescribeLags_2.json b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections_without_force_delete/directconnect.DescribeLags_2.json new file mode 100644 index 00000000000..5643faeeac6 --- /dev/null +++ b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections_without_force_delete/directconnect.DescribeLags_2.json @@ -0,0 +1,42 @@ +{ + "data": { + "lags": [ + { + "awsDevice": "EqSe2-1bwfvazist2k0", + "connections": [ + { + "bandwidth": "1Gbps", + "connectionName": "Requested Connection 1 for Lag dxlag-fgkk4dja", + "location": "EqSe2", + "ownerAccount": "448830907657", + "lagId": "dxlag-fgkk4dja", + "region": "us-west-2", + "connectionState": "requested", + "connectionId": "dxcon-ffx41o23" + } + ], + "lagState": "pending", + "minimumLinks": 0, + "location": "EqSe2", + "connectionsBandwidth": "1Gbps", + "ownerAccount": "448830907657", + "region": "us-west-2", + "numberOfConnections": 1, + "lagName": "ansible_lag_1", + "lagId": "dxlag-fgkk4dja" + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "HTTPHeaders": { + "content-type": "application/x-amz-json-1.1", + "x-amzn-requestid": "bc4902ba-70a7-11e7-a2a8-21d8bda1f5ec", + "content-length": "520", + "date": "Mon, 24 Jul 2017 19:38:57 GMT" + }, + "RequestId": "bc4902ba-70a7-11e7-a2a8-21d8bda1f5ec" + } + }, + "status_code": 200 +} \ No newline at end of file diff --git a/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections_without_force_delete/directconnect.DescribeLags_3.json b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections_without_force_delete/directconnect.DescribeLags_3.json new file mode 100644 index 00000000000..a1792e1c2f7 --- /dev/null +++ b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections_without_force_delete/directconnect.DescribeLags_3.json @@ -0,0 +1,42 @@ +{ + "data": { + "lags": [ + { + "awsDevice": "EqSe2-1bwfvazist2k0", + "connections": [ + { + "bandwidth": "1Gbps", + "connectionName": "Requested Connection 1 for Lag dxlag-fgkk4dja", + "location": "EqSe2", + "ownerAccount": "448830907657", + "lagId": "dxlag-fgkk4dja", + "region": "us-west-2", + "connectionState": "requested", + "connectionId": "dxcon-ffx41o23" + } + ], + "lagState": "pending", + "minimumLinks": 0, + "location": "EqSe2", + "connectionsBandwidth": "1Gbps", + "ownerAccount": "448830907657", + "region": "us-west-2", + "numberOfConnections": 1, + "lagName": "ansible_lag_1", + "lagId": "dxlag-fgkk4dja" + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "HTTPHeaders": { + "content-type": "application/x-amz-json-1.1", + "x-amzn-requestid": "bc7ff13c-70a7-11e7-a2a8-21d8bda1f5ec", + "content-length": "520", + "date": "Mon, 24 Jul 2017 19:38:57 GMT" + }, + "RequestId": "bc7ff13c-70a7-11e7-a2a8-21d8bda1f5ec" + } + }, + "status_code": 200 +} \ No newline at end of file diff --git a/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections_without_force_delete/directconnect.DescribeVirtualInterfaces_1.json b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections_without_force_delete/directconnect.DescribeVirtualInterfaces_1.json new file mode 100644 index 00000000000..5092b8726f3 --- /dev/null +++ b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_lag_with_connections_without_force_delete/directconnect.DescribeVirtualInterfaces_1.json @@ -0,0 +1,17 @@ +{ + "data": { + "virtualInterfaces": [], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "HTTPHeaders": { + "content-type": "application/x-amz-json-1.1", + "x-amzn-requestid": "bc6dc8cb-70a7-11e7-a2a8-21d8bda1f5ec", + "content-length": "24", + "date": "Mon, 24 Jul 2017 19:38:57 GMT" + }, + "RequestId": "bc6dc8cb-70a7-11e7-a2a8-21d8bda1f5ec" + } + }, + "status_code": 200 +} \ No newline at end of file diff --git a/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_nonexistent_lag/directconnect.DescribeLags_1.json b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_nonexistent_lag/directconnect.DescribeLags_1.json new file mode 100644 index 00000000000..6bc71aa7307 --- /dev/null +++ b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/delete_nonexistent_lag/directconnect.DescribeLags_1.json @@ -0,0 +1,21 @@ +{ + "data": { + "Error": { + "Code": "DirectConnectClientException", + "Message": "Could not find Lag with ID dxlag-XXXXXXXX" + }, + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 400, + "HTTPHeaders": { + "connection": "close", + "content-type": "application/x-amz-json-1.1", + "x-amzn-requestid": "bb67ca78-70a7-11e7-a2a8-21d8bda1f5ec", + "content-length": "95", + "date": "Mon, 24 Jul 2017 19:38:56 GMT" + }, + "RequestId": "bb67ca78-70a7-11e7-a2a8-21d8bda1f5ec" + } + }, + "status_code": 400 +} \ No newline at end of file diff --git a/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/lag_changed_true/directconnect.DescribeLags_1.json b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/lag_changed_true/directconnect.DescribeLags_1.json new file mode 100644 index 00000000000..2b29b0bf412 --- /dev/null +++ b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/lag_changed_true/directconnect.DescribeLags_1.json @@ -0,0 +1,42 @@ +{ + "data": { + "lags": [ + { + "awsDevice": "EqSe2-1bwfvazist2k0", + "connections": [ + { + "bandwidth": "1Gbps", + "connectionName": "Requested Connection 1 for Lag dxlag-fgkk4dja", + "location": "EqSe2", + "ownerAccount": "448830907657", + "lagId": "dxlag-fgkk4dja", + "region": "us-west-2", + "connectionState": "requested", + "connectionId": "dxcon-ffx41o23" + } + ], + "lagState": "pending", + "minimumLinks": 0, + "location": "EqSe2", + "connectionsBandwidth": "1Gbps", + "ownerAccount": "448830907657", + "region": "us-west-2", + "numberOfConnections": 1, + "lagName": "ansible_lag_1", + "lagId": "dxlag-fgkk4dja" + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "HTTPHeaders": { + "content-type": "application/x-amz-json-1.1", + "x-amzn-requestid": "b8662323-70a7-11e7-83ab-ef16f9ac5159", + "content-length": "520", + "date": "Mon, 24 Jul 2017 19:38:50 GMT" + }, + "RequestId": "b8662323-70a7-11e7-83ab-ef16f9ac5159" + } + }, + "status_code": 200 +} \ No newline at end of file diff --git a/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/lag_changed_true_no/directconnect.DescribeLags_1.json b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/lag_changed_true_no/directconnect.DescribeLags_1.json new file mode 100644 index 00000000000..e73e68b8d3f --- /dev/null +++ b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/lag_changed_true_no/directconnect.DescribeLags_1.json @@ -0,0 +1,42 @@ +{ + "data": { + "lags": [ + { + "awsDevice": "EqSe2-1bwfvazist2k0", + "connections": [ + { + "bandwidth": "1Gbps", + "connectionName": "Requested Connection 1 for Lag dxlag-fgkk4dja", + "location": "EqSe2", + "ownerAccount": "448830907657", + "lagId": "dxlag-fgkk4dja", + "region": "us-west-2", + "connectionState": "requested", + "connectionId": "dxcon-ffx41o23" + } + ], + "lagState": "pending", + "minimumLinks": 0, + "location": "EqSe2", + "connectionsBandwidth": "1Gbps", + "ownerAccount": "448830907657", + "region": "us-west-2", + "numberOfConnections": 1, + "lagName": "ansible_lag_1", + "lagId": "dxlag-fgkk4dja" + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "HTTPHeaders": { + "content-type": "application/x-amz-json-1.1", + "x-amzn-requestid": "b91b4255-70a7-11e7-83ab-ef16f9ac5159", + "content-length": "520", + "date": "Mon, 24 Jul 2017 19:38:52 GMT" + }, + "RequestId": "b91b4255-70a7-11e7-83ab-ef16f9ac5159" + } + }, + "status_code": 200 +} \ No newline at end of file diff --git a/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/lag_exists/directconnect.DescribeLags_1.json b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/lag_exists/directconnect.DescribeLags_1.json new file mode 100644 index 00000000000..01821531b81 --- /dev/null +++ b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/lag_exists/directconnect.DescribeLags_1.json @@ -0,0 +1,42 @@ +{ + "data": { + "lags": [ + { + "awsDevice": "EqSe2-1bwfvazist2k0", + "connections": [ + { + "bandwidth": "1Gbps", + "connectionName": "Requested Connection 1 for Lag dxlag-fgkk4dja", + "location": "EqSe2", + "ownerAccount": "448830907657", + "lagId": "dxlag-fgkk4dja", + "region": "us-west-2", + "connectionState": "requested", + "connectionId": "dxcon-ffx41o23" + } + ], + "lagState": "pending", + "minimumLinks": 0, + "location": "EqSe2", + "connectionsBandwidth": "1Gbps", + "ownerAccount": "448830907657", + "region": "us-west-2", + "numberOfConnections": 1, + "lagName": "ansible_lag_1", + "lagId": "dxlag-fgkk4dja" + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "HTTPHeaders": { + "content-type": "application/x-amz-json-1.1", + "x-amzn-requestid": "b5e4a858-70a7-11e7-a69f-95e467ba41d7", + "content-length": "520", + "date": "Mon, 24 Jul 2017 19:38:46 GMT" + }, + "RequestId": "b5e4a858-70a7-11e7-a69f-95e467ba41d7" + } + }, + "status_code": 200 +} \ No newline at end of file diff --git a/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/lag_exists_using_name/directconnect.DescribeLags_1.json b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/lag_exists_using_name/directconnect.DescribeLags_1.json new file mode 100644 index 00000000000..26ebd06311a --- /dev/null +++ b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/lag_exists_using_name/directconnect.DescribeLags_1.json @@ -0,0 +1,157 @@ +{ + "data": { + "lags": [ + { + "awsDevice": "EqSe2-9uinh2jjnuu9", + "connections": [], + "lagState": "pending", + "minimumLinks": 0, + "location": "EqSe2", + "connectionsBandwidth": "1Gbps", + "ownerAccount": "448830907657", + "region": "us-west-2", + "numberOfConnections": 0, + "lagName": "sherteltestlag", + "lagId": "dxlag-fgr4lfqt" + }, + { + "awsDevice": "EqSe2-1bwfvazist2k0", + "connections": [ + { + "bandwidth": "1Gbps", + "connectionName": "Requested Connection 1 for Lag dxlag-fgkk4dja", + "location": "EqSe2", + "ownerAccount": "448830907657", + "lagId": "dxlag-fgkk4dja", + "region": "us-west-2", + "connectionState": "requested", + "connectionId": "dxcon-ffx41o23" + } + ], + "lagState": "pending", + "minimumLinks": 0, + "location": "EqSe2", + "connectionsBandwidth": "1Gbps", + "ownerAccount": "448830907657", + "region": "us-west-2", + "numberOfConnections": 1, + "lagName": "ansible_lag_1", + "lagId": "dxlag-fgkk4dja" + }, + { + "awsDevice": "EqSe2-2bii1jufy4y7p", + "connections": [ + { + "bandwidth": "1Gbps", + "connectionName": "Requested Connection 1 for Lag dxlag-fgytkicv", + "location": "EqSe2", + "ownerAccount": "448830907657", + "lagId": "dxlag-fgytkicv", + "region": "us-west-2", + "connectionState": "requested", + "connectionId": "dxcon-fgsxammv" + } + ], + "lagState": "pending", + "minimumLinks": 0, + "location": "EqSe2", + "connectionsBandwidth": "1Gbps", + "ownerAccount": "448830907657", + "region": "us-west-2", + "numberOfConnections": 1, + "lagName": "ansible_lag_2", + "lagId": "dxlag-fgytkicv" + }, + { + "awsDevice": "EqSe2-1bwfvazist2k0", + "connections": [], + "lagState": "deleted", + "minimumLinks": 0, + "location": "EqSe2", + "connectionsBandwidth": "1Gbps", + "ownerAccount": "448830907657", + "region": "us-west-2", + "numberOfConnections": 0, + "lagName": "ansible_lag_1", + "lagId": "dxlag-fgee5gk5" + }, + { + "awsDevice": "EqSe2-2bii1jufy4y7p", + "connections": [], + "lagState": "deleted", + "minimumLinks": 0, + "location": "EqSe2", + "connectionsBandwidth": "1Gbps", + "ownerAccount": "448830907657", + "region": "us-west-2", + "numberOfConnections": 0, + "lagName": "ansible_lag_2_update", + "lagId": "dxlag-fg0hj0n3" + }, + { + "awsDevice": "EqSe2-1bwfvazist2k0", + "connections": [], + "lagState": "deleted", + "minimumLinks": 0, + "location": "EqSe2", + "connectionsBandwidth": "1Gbps", + "ownerAccount": "448830907657", + "region": "us-west-2", + "numberOfConnections": 0, + "lagName": "ansible_lag_1", + "lagId": "dxlag-ffg1zmo4" + }, + { + "awsDevice": "EqSe2-2oqu43nde4cs1", + "connections": [], + "lagState": "deleted", + "minimumLinks": 0, + "location": "EqSe2", + "connectionsBandwidth": "1Gbps", + "ownerAccount": "448830907657", + "region": "us-west-2", + "numberOfConnections": 0, + "lagName": "ansible_lag_2_update", + "lagId": "dxlag-ffzm4jk8" + }, + { + "awsDevice": "EqSe2-1bwfvazist2k0", + "connections": [], + "lagState": "deleted", + "minimumLinks": 0, + "location": "EqSe2", + "connectionsBandwidth": "1Gbps", + "ownerAccount": "448830907657", + "region": "us-west-2", + "numberOfConnections": 0, + "lagName": "ansible_lag_1", + "lagId": "dxlag-ffuid1ql" + }, + { + "awsDevice": "EqSe2-2oqu43nde4cs1", + "connections": [], + "lagState": "deleted", + "minimumLinks": 0, + "location": "EqSe2", + "connectionsBandwidth": "1Gbps", + "ownerAccount": "448830907657", + "region": "us-west-2", + "numberOfConnections": 0, + "lagName": "ansible_lag_2_update", + "lagId": "dxlag-ffpq2qa7" + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "HTTPHeaders": { + "content-type": "application/x-amz-json-1.1", + "x-amzn-requestid": "b6a0a55a-70a7-11e7-a69f-95e467ba41d7", + "content-length": "2920", + "date": "Mon, 24 Jul 2017 19:38:49 GMT" + }, + "RequestId": "b6a0a55a-70a7-11e7-a69f-95e467ba41d7" + } + }, + "status_code": 200 +} \ No newline at end of file diff --git a/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/lag_status/directconnect.DescribeLags_1.json b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/lag_status/directconnect.DescribeLags_1.json new file mode 100644 index 00000000000..fa32a2cf457 --- /dev/null +++ b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/lag_status/directconnect.DescribeLags_1.json @@ -0,0 +1,42 @@ +{ + "data": { + "lags": [ + { + "awsDevice": "EqSe2-1bwfvazist2k0", + "connections": [ + { + "bandwidth": "1Gbps", + "connectionName": "Requested Connection 1 for Lag dxlag-fgkk4dja", + "location": "EqSe2", + "ownerAccount": "448830907657", + "lagId": "dxlag-fgkk4dja", + "region": "us-west-2", + "connectionState": "requested", + "connectionId": "dxcon-ffx41o23" + } + ], + "lagState": "pending", + "minimumLinks": 0, + "location": "EqSe2", + "connectionsBandwidth": "1Gbps", + "ownerAccount": "448830907657", + "region": "us-west-2", + "numberOfConnections": 1, + "lagName": "ansible_lag_1", + "lagId": "dxlag-fgkk4dja" + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "HTTPHeaders": { + "content-type": "application/x-amz-json-1.1", + "x-amzn-requestid": "b4aa057e-70a7-11e7-83ab-ef16f9ac5159", + "content-length": "520", + "date": "Mon, 24 Jul 2017 19:38:44 GMT" + }, + "RequestId": "b4aa057e-70a7-11e7-83ab-ef16f9ac5159" + } + }, + "status_code": 200 +} \ No newline at end of file diff --git a/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/nonexistent_lag_does_not_exist/directconnect.DescribeLags_1.json b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/nonexistent_lag_does_not_exist/directconnect.DescribeLags_1.json new file mode 100644 index 00000000000..0b394f15eae --- /dev/null +++ b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/nonexistent_lag_does_not_exist/directconnect.DescribeLags_1.json @@ -0,0 +1,21 @@ +{ + "data": { + "Error": { + "Code": "DirectConnectClientException", + "Message": "Could not find Lag with ID dxlag-XXXXXXXX" + }, + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 400, + "HTTPHeaders": { + "connection": "close", + "content-type": "application/x-amz-json-1.1", + "x-amzn-requestid": "b7f55ff2-70a7-11e7-83ab-ef16f9ac5159", + "content-length": "95", + "date": "Mon, 24 Jul 2017 19:38:50 GMT" + }, + "RequestId": "b7f55ff2-70a7-11e7-83ab-ef16f9ac5159" + } + }, + "status_code": 400 +} \ No newline at end of file diff --git a/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/nonexistent_lag_status/directconnect.DescribeLags_1.json b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/nonexistent_lag_status/directconnect.DescribeLags_1.json new file mode 100644 index 00000000000..e1464bd7c2b --- /dev/null +++ b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/nonexistent_lag_status/directconnect.DescribeLags_1.json @@ -0,0 +1,21 @@ +{ + "data": { + "Error": { + "Code": "DirectConnectClientException", + "Message": "Lag ID doesntexist has an invalid format." + }, + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 400, + "HTTPHeaders": { + "connection": "close", + "content-type": "application/x-amz-json-1.1", + "x-amzn-requestid": "b3c76dc0-70a7-11e7-a2a8-21d8bda1f5ec", + "content-length": "95", + "date": "Mon, 24 Jul 2017 19:38:42 GMT" + }, + "RequestId": "b3c76dc0-70a7-11e7-a2a8-21d8bda1f5ec" + } + }, + "status_code": 400 +} \ No newline at end of file diff --git a/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/update_lag/directconnect.DescribeLags_1.json b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/update_lag/directconnect.DescribeLags_1.json new file mode 100644 index 00000000000..20434bbd084 --- /dev/null +++ b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/update_lag/directconnect.DescribeLags_1.json @@ -0,0 +1,42 @@ +{ + "data": { + "lags": [ + { + "awsDevice": "EqSe2-2bii1jufy4y7p", + "connections": [ + { + "bandwidth": "1Gbps", + "connectionName": "Requested Connection 1 for Lag dxlag-fgytkicv", + "location": "EqSe2", + "ownerAccount": "448830907657", + "lagId": "dxlag-fgytkicv", + "region": "us-west-2", + "connectionState": "requested", + "connectionId": "dxcon-fgsxammv" + } + ], + "lagState": "pending", + "minimumLinks": 0, + "location": "EqSe2", + "connectionsBandwidth": "1Gbps", + "ownerAccount": "448830907657", + "region": "us-west-2", + "numberOfConnections": 1, + "lagName": "ansible_lag_2", + "lagId": "dxlag-fgytkicv" + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "HTTPHeaders": { + "content-type": "application/x-amz-json-1.1", + "x-amzn-requestid": "b9cc69e9-70a7-11e7-83ab-ef16f9ac5159", + "content-length": "520", + "date": "Mon, 24 Jul 2017 19:38:53 GMT" + }, + "RequestId": "b9cc69e9-70a7-11e7-83ab-ef16f9ac5159" + } + }, + "status_code": 200 +} \ No newline at end of file diff --git a/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/update_lag/directconnect.DescribeLags_2.json b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/update_lag/directconnect.DescribeLags_2.json new file mode 100644 index 00000000000..08847e89d9c --- /dev/null +++ b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/update_lag/directconnect.DescribeLags_2.json @@ -0,0 +1,42 @@ +{ + "data": { + "lags": [ + { + "awsDevice": "EqSe2-2bii1jufy4y7p", + "connections": [ + { + "bandwidth": "1Gbps", + "connectionName": "Requested Connection 1 for Lag dxlag-fgytkicv", + "location": "EqSe2", + "ownerAccount": "448830907657", + "lagId": "dxlag-fgytkicv", + "region": "us-west-2", + "connectionState": "requested", + "connectionId": "dxcon-fgsxammv" + } + ], + "lagState": "pending", + "minimumLinks": 0, + "location": "EqSe2", + "connectionsBandwidth": "1Gbps", + "ownerAccount": "448830907657", + "region": "us-west-2", + "numberOfConnections": 1, + "lagName": "ansible_lag_2_update", + "lagId": "dxlag-fgytkicv" + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "HTTPHeaders": { + "content-type": "application/x-amz-json-1.1", + "x-amzn-requestid": "ba91197b-70a7-11e7-83ab-ef16f9ac5159", + "content-length": "527", + "date": "Mon, 24 Jul 2017 19:38:54 GMT" + }, + "RequestId": "ba91197b-70a7-11e7-83ab-ef16f9ac5159" + } + }, + "status_code": 200 +} \ No newline at end of file diff --git a/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/update_lag/directconnect.UpdateLag_1.json b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/update_lag/directconnect.UpdateLag_1.json new file mode 100644 index 00000000000..da5475f7132 --- /dev/null +++ b/test/units/modules/cloud/amazon/placebo_recordings/aws_direct_connect_link_aggregation_group/update_lag/directconnect.UpdateLag_1.json @@ -0,0 +1,38 @@ +{ + "data": { + "lagState": "pending", + "location": "EqSe2", + "region": "us-west-2", + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "HTTPHeaders": { + "content-type": "application/x-amz-json-1.1", + "x-amzn-requestid": "ba76658a-70a7-11e7-83ab-ef16f9ac5159", + "content-length": "516", + "date": "Mon, 24 Jul 2017 19:38:54 GMT" + }, + "RequestId": "ba76658a-70a7-11e7-83ab-ef16f9ac5159" + }, + "lagId": "dxlag-fgytkicv", + "awsDevice": "EqSe2-2bii1jufy4y7p", + "connections": [ + { + "bandwidth": "1Gbps", + "connectionName": "Requested Connection 1 for Lag dxlag-fgytkicv", + "location": "EqSe2", + "ownerAccount": "448830907657", + "lagId": "dxlag-fgytkicv", + "region": "us-west-2", + "connectionState": "requested", + "connectionId": "dxcon-fgsxammv" + } + ], + "connectionsBandwidth": "1Gbps", + "minimumLinks": 0, + "ownerAccount": "448830907657", + "numberOfConnections": 1, + "lagName": "ansible_lag_2_update" + }, + "status_code": 200 +} \ No newline at end of file diff --git a/test/units/modules/cloud/amazon/test_aws_direct_connect_link_aggregation_group.py b/test/units/modules/cloud/amazon/test_aws_direct_connect_link_aggregation_group.py new file mode 100644 index 00000000000..56472df9f0a --- /dev/null +++ b/test/units/modules/cloud/amazon/test_aws_direct_connect_link_aggregation_group.py @@ -0,0 +1,176 @@ +# (c) 2017 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +import pytest +import os +import collections +from . placebo_fixtures import placeboify, maybe_sleep +from ansible.modules.cloud.amazon import aws_direct_connect_link_aggregation_group as lag_module +from ansible.module_utils.ec2 import get_aws_connection_info, boto3_conn + + +@pytest.fixture(scope="module") +def dependencies(): + + # each LAG dict will contain the keys: module, connections, virtual_interfaces + Dependencies = collections.namedtuple("Dependencies", ["lag_1", "lag_2"]) + lag_1 = dict() + lag_2 = dict() + + vanilla_params = {"name": "ansible_lag_1", + "location": "EqSe2", + "num_connections": 1, + "min_links": 0, + "bandwidth": "1Gbps"} + + for lag in ("ansible_lag_1", "ansible_lag_2"): + params = dict(vanilla_params) + params["name"] = lag + if lag == "ansible_lag_1": + lag_1["module"] = FakeModule(**params) + else: + lag_2["module"] = FakeModule(**params) + + if os.getenv("PLACEBO_RECORD"): + region, ec2_url, aws_connect_kwargs = get_aws_connection_info(lag_1["module"], boto3=True) + client = boto3_conn(lag_1["module"], conn_type="client", resource="directconnect", region=region, endpoint=ec2_url, **aws_connect_kwargs) + # See if link aggregation groups exist + for name in ("ansible_lag_1", "ansible_lag_2"): + lag_id = lag_module.create_lag(client, num_connections=1, location="EqSe2", bandwidth="1Gbps", name=name, connection_id=None) + if name == "ansible_lag_1": + lag_1["lag_id"] = lag_id + lag_1["name"] = name + else: + lag_2["lag_id"] = lag_id + lag_2["name"] = name + yield Dependencies(lag_1=lag_1, lag_2=lag_2) + else: + lag_1.update(lag_id="dxlag-fgkk4dja", name="ansible_lag_1") + lag_2.update(lag_id="dxlag-fgytkicv", name="ansible_lag_2") + yield Dependencies(lag_1=lag_1, lag_2=lag_2) + + if os.getenv("PLACEBO_RECORD"): + # clean up + lag_module.ensure_absent(client, lag_1["lag_id"], lag_1["name"], True, True, True, 120) + lag_module.ensure_absent(client, lag_2["lag_id"], lag_2["name"], True, True, True, 120) + + +class FakeModule(object): + def __init__(self, **kwargs): + self.params = kwargs + + def fail_json(self, *args, **kwargs): + self.exit_args = args + self.exit_kwargs = kwargs + raise Exception("FAIL") + + def exit_json(self, *args, **kwargs): + self.exit_args = args + self.exit_kwargs = kwargs + + +def test_nonexistent_lag_status(placeboify, maybe_sleep): + client = placeboify.client("directconnect") + exists = lag_module.lag_exists(client=client, + lag_id="doesntexist", + lag_name="doesntexist", + verify=True) + assert not exists + + +def test_lag_status(placeboify, maybe_sleep, dependencies): + client = placeboify.client("directconnect") + status = lag_module.lag_status(client, lag_id=dependencies.lag_1.get("lag_id")) + assert status.get("lagId") == dependencies.lag_1.get("lag_id") + assert status.get("lagName") == "ansible_lag_1" + + +def test_lag_exists(placeboify, maybe_sleep, dependencies): + client = placeboify.client("directconnect") + exists = lag_module.lag_exists(client=client, + lag_id=dependencies.lag_1.get("lag_id"), + lag_name=None, + verify=True) + assert exists + + +def test_lag_exists_using_name(placeboify, maybe_sleep, dependencies): + client = placeboify.client("directconnect") + exists = lag_module.lag_exists(client=client, + lag_id=None, + lag_name=dependencies.lag_1.get("name"), + verify=True) + assert exists + + +def test_nonexistent_lag_does_not_exist(placeboify, maybe_sleep): + client = placeboify.client("directconnect") + exists = lag_module.lag_exists(client=client, + lag_id="dxlag-XXXXXXXX", + lag_name="doesntexist", + verify=True) + assert not exists + + +def test_lag_changed_true(placeboify, maybe_sleep, dependencies): + client = placeboify.client("directconnect") + status = lag_module.lag_status(client=client, lag_id=dependencies.lag_1.get("lag_id")) + assert lag_module.lag_changed(status, "new_name", 1) + + +def test_lag_changed_true_no(placeboify, maybe_sleep, dependencies): + client = placeboify.client("directconnect") + status = lag_module.lag_status(client=client, lag_id=dependencies.lag_1.get("lag_id")) + assert not lag_module.lag_changed(status, "ansible_lag_1", 0) + + +def test_update_lag(placeboify, maybe_sleep, dependencies): + client = placeboify.client("directconnect") + status_before = lag_module.lag_status(client=client, lag_id=dependencies.lag_2.get("lag_id")) + lag_module.update_lag(client, + lag_id=dependencies.lag_2.get("lag_id"), + lag_name="ansible_lag_2_update", + min_links=0, + wait=False, + wait_timeout=0, + num_connections=1) + status_after = lag_module.lag_status(client=client, lag_id=dependencies.lag_2.get("lag_id")) + assert status_before != status_after + + # remove the lag name from the statuses and verify it was the only thing changed + del status_before['lagName'] + del status_after['lagName'] + assert status_before == status_after + + +def test_delete_nonexistent_lag(placeboify, maybe_sleep): + client = placeboify.client("directconnect") + changed = lag_module.ensure_absent(client, "dxlag-XXXXXXXX", "doesntexist", True, True, True, 120) + assert not changed + + +def test_delete_lag_with_connections_without_force_delete(placeboify, maybe_sleep, dependencies): + client = placeboify.client("directconnect") + with pytest.raises(Exception) as error_message: + lag_module.ensure_absent(client, dependencies.lag_1.get("lag_id"), "ansible_lag_1", False, True, True, 120) + assert "To force deletion of the LAG use delete_force: True" in error_message + + +def test_delete_lag_with_connections(placeboify, maybe_sleep, dependencies): + client = placeboify.client("directconnect") + changed = lag_module.ensure_absent(client, dependencies.lag_1.get("lag_id"), "ansible_lag_1", True, True, True, 120) + assert changed