From ccb7909cc565c28633e03ba55821c4d5300d054d Mon Sep 17 00:00:00 2001 From: Hai Cao Date: Thu, 30 Aug 2018 00:18:53 -0400 Subject: [PATCH] [new module] Azure Traffic Manager profile module (#43812) * add traffic manager client and models in azure_rm_common - add traffic manager management client - import traffic manager models to 'traffic_manager_models' proprety - azure.mgmt.trafficmanager doesn't have models(), so use direct import for now * add traffic manager facts module * add traffic manager module * add integration test for two modules * fix package info in requirements * fix sanity check * fix monitor config default value * fix facts module doc * move model import into module * resolve comments except seprating endpoint * remove endpoint operation * fix test after removing endpoint operation * change module name to azure_rm_trafficmanagerprofile/facts * fix sanity test * seperate endpoint into delegate module * fix typo * fix lint * fix lint * fix test * fix test * resolve comments * fix test * fix test * fix test * fix bug in return * resolve comments * fix lint * fix lint * add sample in endpoint module --- lib/ansible/module_utils/azure_rm_common.py | 15 + .../azure/azure_rm_trafficmanagerendpoint.py | 371 ++++++++++++++ .../azure_rm_trafficmanagerendpoint_facts.py | 297 ++++++++++++ .../azure/azure_rm_trafficmanagerprofile.py | 457 ++++++++++++++++++ .../azure_rm_trafficmanagerprofile_facts.py | 378 +++++++++++++++ packaging/requirements/requirements-azure.txt | 1 + .../azure_rm_trafficmanagerprofile/aliases | 3 + .../meta/main.yml | 2 + .../tasks/main.yml | 289 +++++++++++ .../requirements/integration.cloud.azure.txt | 1 + 10 files changed, 1814 insertions(+) create mode 100644 lib/ansible/modules/cloud/azure/azure_rm_trafficmanagerendpoint.py create mode 100644 lib/ansible/modules/cloud/azure/azure_rm_trafficmanagerendpoint_facts.py create mode 100644 lib/ansible/modules/cloud/azure/azure_rm_trafficmanagerprofile.py create mode 100644 lib/ansible/modules/cloud/azure/azure_rm_trafficmanagerprofile_facts.py create mode 100644 test/integration/targets/azure_rm_trafficmanagerprofile/aliases create mode 100644 test/integration/targets/azure_rm_trafficmanagerprofile/meta/main.yml create mode 100644 test/integration/targets/azure_rm_trafficmanagerprofile/tasks/main.yml diff --git a/lib/ansible/module_utils/azure_rm_common.py b/lib/ansible/module_utils/azure_rm_common.py index 248b5d8eae0..fb97186b548 100644 --- a/lib/ansible/module_utils/azure_rm_common.py +++ b/lib/ansible/module_utils/azure_rm_common.py @@ -150,6 +150,7 @@ try: from azure.mgmt.web import WebSiteManagementClient from azure.mgmt.containerservice import ContainerServiceClient from azure.mgmt.marketplaceordering import MarketplaceOrderingAgreements + from azure.mgmt.trafficmanager import TrafficManagerManagementClient from azure.storage.cloudstorageaccount import CloudStorageAccount from adal.authentication_context import AuthenticationContext from azure.mgmt.rdbms.postgresql import PostgreSQLManagementClient @@ -222,6 +223,10 @@ AZURE_PKG_VERSIONS = { 'package_name': 'web', 'expected_version': '0.32.0' }, + 'TrafficManagerManagementClient': { + 'package_name': 'trafficmanager', + 'expected_version': '0.50.0' + }, } if HAS_AZURE else {} @@ -282,6 +287,8 @@ class AzureRMModuleBase(object): self._postgresql_client = None self._containerregistry_client = None self._containerinstance_client = None + self._traffic_manager_management_client = None + self._adfs_authority_url = None self._resource = None @@ -1109,3 +1116,11 @@ class AzureRMModuleBase(object): self._marketplace_client = self.get_mgmt_svc_client(MarketplaceOrderingAgreements, base_url=self._cloud_environment.endpoints.resource_manager) return self._marketplace_client + + @property + def traffic_manager_management_client(self): + self.log('Getting traffic manager client') + if not self._traffic_manager_management_client: + self._traffic_manager_management_client = self.get_mgmt_svc_client(TrafficManagerManagementClient, + base_url=self._cloud_environment.endpoints.resource_manager) + return self._traffic_manager_management_client diff --git a/lib/ansible/modules/cloud/azure/azure_rm_trafficmanagerendpoint.py b/lib/ansible/modules/cloud/azure/azure_rm_trafficmanagerendpoint.py new file mode 100644 index 00000000000..37a7d0884d7 --- /dev/null +++ b/lib/ansible/modules/cloud/azure/azure_rm_trafficmanagerendpoint.py @@ -0,0 +1,371 @@ +#!/usr/bin/python +# +# Copyright (c) 2018 Hai Cao, , Yunge Zhu +# +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = ''' +--- +module: azure_rm_trafficmanagerendpoint +version_added: "2.7" +short_description: Manage Azure Traffic Manager endpoint. +description: + - Create, update and delete Azure Traffic Manager endpoint. + +options: + resource_group: + description: + - Name of a resource group where the Traffic Manager endpoint exists or will be created. + type: str + required: true + name: + description: + - The name of the endpoint. + type: str + required: true + profile_name: + description: Name of Traffic Manager profile where this endpoints attaches to. + type: str + required: true + type: + description: + - The type of the endpoint. + required: true + choices: + - azure_endpoints + - external_endpoints + - nested_endpoints + target_resource_id: + description: + - The Azure Resource URI of the of the endpoint. + - Not applicable to endpoints of I(type) C(external_endpoints). + type: str + target: + description: + - The fully-qualified DNS name of the endpoint. + type: str + enabled: + description: + - The status of the endpoint. + type: bool + default: true + weight: + description: + - The weight of this endpoint when traffic manager profile has routing_method of C(weighted). + - Possible values are from 1 to 1000. + type: int + priority: + description: + - The priority of this endpoint when traffic manager profile has routing_method of C(priority). + - Possible values are from 1 to 1000, lower values represent higher priority. + - This is an optional parameter. If specified, it must be specified on all endpoints. + - No two endpoints can share the same priority value. + type: int + location: + description: + - Specifies the location of the external or nested endpoints when using the 'Performance' traffic routing method. + type: str + min_child_endpoints: + description: + - The minimum number of endpoints that must be available in the child profile in order for the parent profile to be considered available. + - Only applicable to endpoint of I(type) (nested_endpoints). + type: int + geo_mapping: + description: + - The list of countries/regions mapped to this endpoint when traffic manager profile has routing_method of C(geographic). + type: str + state: + description: + - Assert the state of the Traffic Manager endpoint. Use C(present) to create or update a Traffic Manager endpoint and C(absent) to delete it. + default: present + choices: + - absent + - present + +extends_documentation_fragment: + - azure + +author: + - "Hai Cao " + - "Yunge Zhu " + +''' + +EXAMPLES = ''' + - name: create a endpoint for a traffic manager profile + azure_rm_trafficmanagerendpoint: + resource_group: testresourcegroup + profile_name: myprofilename + name: testendpoint1 + type: external_endpoints + location: westus + priority: 2 + weight: 1 + target: 1.2.3.4 +''' + +RETURN = ''' +id: + description: The ID of the traffic manager endpoint + returned: when traffic manager endpoint exists + type: str + example: + "/subscriptions//resourceGroups/testRg/providers/Microsoft.Network/trafficManagerProfiles/testProfile/externalEndpoints/testendpoint" +''' +from ansible.module_utils.azure_rm_common import AzureRMModuleBase, normalize_location_name +from ansible.module_utils.common.dict_transformations import _snake_to_camel + +try: + from msrestazure.azure_exceptions import CloudError + from azure.mgmt.trafficmanager.models import ( + Endpoint, DnsConfig, MonitorConfig + ) +except ImportError: + # This is handled in azure_rm_common + pass + + +def traffic_manager_endpoint_to_dict(endpoint): + return dict( + id=endpoint.id, + name=endpoint.name, + type=endpoint.type, + target_resource_id=endpoint.target_resource_id, + target=endpoint.target, + status=endpoint.endpoint_status, + weight=endpoint.weight, + priority=endpoint.priority, + location=endpoint.endpoint_location, + monitor_status=endpoint.endpoint_monitor_status, + min_child_endpoints=endpoint.min_child_endpoints, + geo_mapping=endpoint.geo_mapping + ) + + +class Actions: + NoAction, CreateOrUpdate, Delete = range(3) + + +class AzureRMTrafficManagerEndpoint(AzureRMModuleBase): + + def __init__(self): + self.module_arg_spec = dict( + resource_group=dict( + type='str', + required=True + ), + name=dict( + type='str', + required=True + ), + profile_name=dict( + type='str', + required=True + ), + type=dict( + type='str', + choices=['azure_endpoints', 'external_endpoints', 'nested_endpoints'], + required=True + ), + target=dict(type='str'), + target_resource_id=dict(type='str'), + enabled=dict(type='bool', default=True), + weight=dict(type='int'), + priority=dict(type='int'), + location=dict(type='str'), + min_child_endpoints=dict(type='int'), + geo_mapping=dict(type='list', elements='str'), + state=dict( + type='str', + default='present', + choices=['present', 'absent'] + ), + ) + + self.resource_group = None + self.name = None + self.state = None + + self.profile_name = None + self.type = None + self.target_resource_id = None + self.enabled = None + self.weight = None + self.priority = None + self.location = None + self.min_child_endpoints = None + self.geo_mapping = None + self.endpoint_status = 'Enabled' + + self.action = Actions.NoAction + + self.results = dict( + changed=False + ) + + super(AzureRMTrafficManagerEndpoint, self).__init__(derived_arg_spec=self.module_arg_spec, + supports_check_mode=True, + supports_tags=False) + + def exec_module(self, **kwargs): + + for key in list(self.module_arg_spec.keys()): + setattr(self, key, kwargs[key]) + + if self.type: + self.type = _snake_to_camel(self.type) + + to_be_updated = False + + resource_group = self.get_resource_group(self.resource_group) + if not self.location: + self.location = resource_group.location + + if self.enabled is not None and self.enabled is False: + self.endpoint_status = 'Disabled' + + response = self.get_traffic_manager_endpoint() + + if response: + self.log('Results : {0}'.format(response)) + self.results['id'] = response['id'] + if self.state == 'present': + # check update + to_be_update = self.check_update(response) + if to_be_update: + self.action = Actions.CreateOrUpdate + + elif self.state == 'absent': + # delete + self.action = Actions.Delete + else: + if self.state == 'present': + self.action = Actions.CreateOrUpdate + elif self.state == 'absent': + # delete when no exists + self.fail("Traffic Manager endpoint {0} not exists.".format(self.name)) + + if self.action == Actions.CreateOrUpdate: + self.results['changed'] = True + if self.check_mode: + return self.results + + response = self.create_update_traffic_manager_endpoint() + self.results['id'] = response['id'] + + if self.action == Actions.Delete: + self.results['changed'] = True + if self.check_mode: + return self.results + response = self.delete_traffic_manager_endpoint() + + return self.results + + def get_traffic_manager_endpoint(self): + ''' + Gets the properties of the specified Traffic Manager endpoint + + :return: deserialized Traffic Manager endpoint dict + ''' + self.log("Checking if Traffic Manager endpoint {0} is present".format(self.name)) + try: + response = self.traffic_manager_management_client.endpoints.get(self.resource_group, self.profile_name, self.type, self.name) + self.log("Response : {0}".format(response)) + return traffic_manager_endpoint_to_dict(response) + except CloudError: + self.log('Did not find the Traffic Manager endpoint.') + return False + + def delete_traffic_manager_endpoint(self): + ''' + Deletes the specified Traffic Manager endpoint. + :return: True + ''' + + self.log("Deleting the Traffic Manager endpoint {0}".format(self.name)) + try: + operation_result = self.traffic_manager_management_client.endpoints.delete(self.resource_group, self.profile_name, self.type, self.name) + return True + except CloudError as exc: + request_id = exc.request_id if exc.request_id else '' + self.fail("Error deleting the Traffic Manager endpoint {0}, request id {1} - {2}".format(self.name, request_id, str(exc))) + return False + + def create_update_traffic_manager_endpoint(self): + ''' + Creates or updates a Traffic Manager endpoint. + + :return: deserialized Traffic Manager endpoint state dictionary + ''' + self.log("Creating / Updating the Traffic Manager endpoint {0}".format(self.name)) + + parameters = Endpoint(target_resource_id=self.target_resource_id, + target=self.target, + endpoint_status=self.endpoint_status, + weight=self.weight, + priority=self.priority, + endpoint_location=self.location, + min_child_endpoints=self.min_child_endpoints, + geo_mapping=self.geo_mapping) + + try: + response = self.traffic_manager_management_client.endpoints.create_or_update(self.resource_group, + self.profile_name, + self.type, + self.name, + parameters) + return traffic_manager_endpoint_to_dict(response) + except CloudError as exc: + request_id = exc.request_id if exc.request_id else '' + self.fail("Error creating the Traffic Manager endpoint {0}, request id {1} - {2}".format(self.name, request_id, str(exc))) + + def check_update(self, response): + if self.endpoint_status is not None and response['status'].lower() != self.endpoint_status.lower(): + self.log("Status Diff - Origin {0} / Update {1}".format(response['status'], self.endpoint_status)) + return True + + if self.type and response['type'].lower() != "Microsoft.network/TrafficManagerProfiles/{0}".format(self.type).lower(): + self.log("Type Diff - Origin {0} / Update {1}".format(response['type'], self.type)) + return True + + if self.target_resource_id and response['target_resource_id'] != self.target_resource_id: + self.log("target_resource_id Diff - Origin {0} / Update {1}".format(response['target_resource_id'], self.target_resource_id)) + return True + + if self.target and response['target'] != self.target: + self.log("target Diff - Origin {0} / Update {1}".format(response['target'], self.target)) + return True + + if self.weight and int(response['weight']) != self.weight: + self.log("weight Diff - Origin {0} / Update {1}".format(response['weight'], self.weight)) + return True + + if self.priority and int(response['priority']) != self.priority: + self.log("priority Diff - Origin {0} / Update {1}".format(response['priority'], self.priority)) + return True + + if self.min_child_endpoints and int(response['min_child_endpoints']) != self.min_child_endpoints: + self.log("min_child_endpoints Diff - Origin {0} / Update {1}".format(response['min_child_endpoints'], self.min_child_endpoints)) + return True + + if self.geo_mapping and response['geo_mapping'] != self.geo_mapping: + self.log("geo_mapping Diff - Origin {0} / Update {1}".format(response['geo_mapping'], self.geo_mapping)) + return True + + return False + + +def main(): + """Main execution""" + AzureRMTrafficManagerEndpoint() + + +if __name__ == '__main__': + main() diff --git a/lib/ansible/modules/cloud/azure/azure_rm_trafficmanagerendpoint_facts.py b/lib/ansible/modules/cloud/azure/azure_rm_trafficmanagerendpoint_facts.py new file mode 100644 index 00000000000..566828fbea8 --- /dev/null +++ b/lib/ansible/modules/cloud/azure/azure_rm_trafficmanagerendpoint_facts.py @@ -0,0 +1,297 @@ +#!/usr/bin/python +# +# Copyright (c) 2018 Hai Cao, , Yunge Zhu +# +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = ''' +--- +module: azure_rm_trafficmanagerendpoint_facts + +version_added: "2.7" + +short_description: Get Azure Traffic Manager endpoint facts + +description: + - Get facts for a specific Traffic Manager endpoints or all endpoints in a Traffic Manager profile + +options: + name: + description: + - Limit results to a specific Traffic Manager endpoint. + resource_group: + description: + - The resource group to search for the desired Traffic Manager profile + required: True + profile_name: + description: + - Name of Traffic Manager Profile + required: True + type: + description: + - Type of endpoint. + choices: + - azure_endpoints + - external_endpoints + - nested_endpoints + +extends_documentation_fragment: + - azure + +author: + - "Hai Cao " + - "Yunge Zhu " +''' + +EXAMPLES = ''' + - name: Get endpoints facts of a Traffic Manager profile + azure_rm_trafficmanagerendpoint_facts: + resource_group: TestRG + profile_name: Testing + + - name: Get specific endpoint of a Traffic Manager profie + azure_rm_trafficmanager_facts: + resource_group: TestRG + profile_name: Testing + name: test_external_endpoint + +''' + +RETURN = ''' +endpoints: + description: List of Traffic Manager endpoints. + returned: always + type: complex + contains: + resource_group: + description: + - Name of a resource group. + returned: always + type: str + sample: testGroup + name: + description: + - Name of the Traffic Manager endpoint. + returned: always + type: str + sample: testendpoint + type: + description: + - The type of the endpoint. + type: str + sample: external_endpoints + target_resource_id: + description: + - The Azure Resource URI of the of the endpoint. + type: str + sample: /subscriptions/XXXXXX...XXXXXXXXX/resourceGroups/vscjavaci/providers/Microsoft.ClassicCompute/domainNames/vscjavaci + target: + description: + - The fully-qualified DNS name of the endpoint. + type: str + sample: 8.8.8.8 + enabled: + description: + - The status of the endpoint. + type: str + sample: Enabled + weight: + description: + - The weight of this endpoint when using the 'Weighted' traffic routing method. + type: int + sample: 10 + priority: + description: + - The priority of this endpoint when using the 'Priority' traffic routing method. + type: str + sample: 3 + location: + description: + - The location of the external or nested endpoints when using the 'Performance' traffic routing method. + type: str + sample: East US + min_child_endpoints: + description: + - The minimum number of endpoints that must be available in the child profile to make the parent profile available. + type: int + sample: 3 + geo_mapping: + description: + - The list of countries/regions mapped to this endpoint when using the 'Geographic' traffic routing method. + type: list + sample: [ + "GEO-NA", + "GEO-AS" + ] +''' + +from ansible.module_utils.azure_rm_common import AzureRMModuleBase +from ansible.module_utils.common.dict_transformations import ( + _snake_to_camel, _camel_to_snake +) + +try: + from msrestazure.azure_exceptions import CloudError + from azure.common import AzureHttpError +except: + # handled in azure_rm_common + pass + +import re + +AZURE_OBJECT_CLASS = 'TrafficManagerEndpoints' + + +def serialize_endpoint(endpoint, resource_group): + result = dict( + id=endpoint.id, + name=endpoint.name, + target_resource_id=endpoint.target_resource_id, + target=endpoint.target, + enabled=True, + weight=endpoint.weight, + priority=endpoint.priority, + location=endpoint.endpoint_location, + min_child_endpoints=endpoint.min_child_endpoints, + geo_mapping=endpoint.geo_mapping, + monitor_status=endpoint.endpoint_monitor_status, + resource_group=resource_group + ) + + if endpoint.endpoint_status and endpoint.endpoint_status == 'Disabled': + result['enabled'] = False + + if endpoint.type: + result['type'] = _camel_to_snake(endpoint.type.split("/")[-1]) + + return result + + +class AzureRMTrafficManagerEndpointFacts(AzureRMModuleBase): + """Utility class to get Azure Traffic Manager Endpoint facts""" + + def __init__(self): + + self.module_args = dict( + profile_name=dict( + type='str', + required=True), + resource_group=dict( + type='str', + required=True), + name=dict(type='str'), + type=dict( + type='str', + choices=[ + 'azure_endpoints', + 'external_endpoints', + 'nested_endpoints' + ]) + ) + + self.results = dict( + changed=False, + endpoints=[] + ) + + self.profile_name = None + self.name = None + self.resource_group = None + self.type = None + + super(AzureRMTrafficManagerEndpointFacts, self).__init__( + derived_arg_spec=self.module_args, + supports_tags=False, + facts_module=True + ) + + def exec_module(self, **kwargs): + + for key in self.module_args: + setattr(self, key, kwargs[key]) + + if self.type: + self.type = _snake_to_camel(self.type) + + if self.name and not self.resource_group: + self.fail("Parameter error: resource group required when filtering by name.") + + if self.name: + self.results['endpoints'] = self.get_item() + elif self.type: + self.results['endpoints'] = self.list_by_type() + else: + self.results['endpoints'] = self.list_by_profile() + + return self.results + + def get_item(self): + """Get a single Azure Traffic Manager endpoint""" + + self.log('Get properties for {0}'.format(self.name)) + + item = None + result = [] + + try: + item = self.traffic_manager_management_client.endpoints.get( + self.resource_group, self.profile_name, self.type, self.name) + except CloudError: + pass + + if item: + if (self.type and self.type == item.type) or self.type is None: + result = [self.serialize_tm(item)] + + return result + + def list_by_profile(self): + """Get all Azure Traffic Manager endpoints of a profile""" + + self.log('List all endpoints belongs to a Traffic Manager profile') + + try: + response = self.traffic_manager_management_client.profiles.get(self.resource_group, self.profile_name) + except AzureHttpError as exc: + self.fail('Failed to list all items - {0}'.format(str(exc))) + + results = [] + if response and response.endpoints: + for endpoint in response.endpoints: + results.append(serialize_endpoint(endpoint, self.resource_group)) + + return results + + def list_by_type(self): + """Get all Azure Traffic Managers endpoints of a profile by type""" + self.log('List all Traffic Manager endpoints of a profile by type') + try: + response = self.traffic_manager_management_client.profiles.get(self.resource_group, self.profile_name) + except AzureHttpError as exc: + self.fail('Failed to list all items - {0}'.format(str(exc))) + + results = [] + for item in response: + if item.endpoints: + for endpoint in item.endpoints: + if endpoint.type == self.type: + results.append(serialize_endpoint(endpoint, self.resource_group)) + return results + + +def main(): + """Main module execution code path""" + + AzureRMTrafficManagerEndpointFacts() + + +if __name__ == '__main__': + main() diff --git a/lib/ansible/modules/cloud/azure/azure_rm_trafficmanagerprofile.py b/lib/ansible/modules/cloud/azure/azure_rm_trafficmanagerprofile.py new file mode 100644 index 00000000000..046bb293f96 --- /dev/null +++ b/lib/ansible/modules/cloud/azure/azure_rm_trafficmanagerprofile.py @@ -0,0 +1,457 @@ +#!/usr/bin/python +# +# Copyright (c) 2018 Hai Cao, Yunge Zhu +# +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = ''' +--- +module: azure_rm_trafficmanagerprofile +version_added: "2.7" +short_description: Manage Azure Traffic Manager profile. +description: + - Create, update and delete a Traffic Manager profile. + +options: + resource_group: + description: + - Name of a resource group where the Traffic Manager profile exists or will be created. + required: true + name: + description: + - Name of the Traffic Manager profile. + required: true + state: + description: + - Assert the state of the Traffic Manager profile. Use C(present) to create or update a Traffic Manager profile and C(absent) to delete it. + default: present + choices: + - absent + - present + location: + description: + - Valid azure location. Defaults to 'global' because in default public Azure cloud, Traffic Manager profile can only be deployed globally. + - Reference https://docs.microsoft.com/en-us/azure/traffic-manager/quickstart-create-traffic-manager-profile#create-a-traffic-manager-profile + default: global + profile_status: + description: + - The status of the Traffic Manager profile. + default: enabled + choices: + - enabled + - disabled + routing_method: + description: + - The traffic routing method of the Traffic Manager profile. + default: performance + choices: + - performance + - priority + - weighted + - geographic + dns_config: + description: + - The DNS settings of the Traffic Manager profile. + suboptions: + relative_name: + description: + - The relative DNS name provided by this Traffic Manager profile. + - If not provided, name of the Traffic Manager will be used + ttl: + description: + - The DNS Time-To-Live (TTL), in seconds. + default: 60 + monitor_config: + description: + - The endpoint monitoring settings of the Traffic Manager profile. + suboptions: + protocol: + description: + - The protocol (HTTP, HTTPS or TCP) used to probe for endpoint health. + choices: + - HTTP + - HTTPS + - TCP + port: + description: + - The TCP port used to probe for endpoint health. + path: + description: + - The path relative to the endpoint domain name used to probe for endpoint health. + interval: + description: + - The monitor interval for endpoints in this profile in seconds. + timeout: + description: + - The monitor timeout for endpoints in this profile in seconds. + tolerated_failures: + description: + - The number of consecutive failed health check before declaring an endpoint in this profile Degraded after the next failed health check. + default: + protocol: HTTP + port: 80 + path: / + +extends_documentation_fragment: + - azure + - azure_tags + +author: + - "Hai Cao " + - "Yunge Zhu " + +''' + +EXAMPLES = ''' + - name: Create a Traffic Manager Profile + azure_rm_trafficmanager: + name: tmtest + resource_group: tmt + location: global + profile_status: enabled + routing_method: priority + dns_config: + relative_name: tmtest + ttl: 60 + monitor_config: + protocol: HTTPS + port: 80 + path: '/' + tags: + Environment: Test + + - name: Delete a Traffic Manager Profile + azure_rm_trafficmanager: + state: absent + name: tmtest + resource_group: tmt +''' +RETURN = ''' +id: + description: The ID of the traffic manager profile + returned: when traffic manager profile exists + type: str + example: "/subscriptions/XXXXXX...XXXXXXXXX/resourceGroups/tmt/providers/Microsoft.Network/trafficManagerProfiles/tmtest" +endpoints: + description: List of endpoint IDs attached to the profile + returned: when traffic manager endpoints exists + type: list + sample: [ + "/subscriptions/XXXXXX...XXXXXXXXX/resourceGroups/tmt/providers/Microsoft.Network/trafficManagerProfiles/tm049b1ae293/externalEndpoints/e2", + "/subscriptions/XXXXXX...XXXXXXXXX/resourceGroups/tmt/providers/Microsoft.Network/trafficManagerProfiles/tm049b1ae293/externalEndpoints/e1" + ] +''' +from ansible.module_utils.azure_rm_common import AzureRMModuleBase, normalize_location_name + +try: + from msrestazure.azure_exceptions import CloudError + from azure.mgmt.trafficmanager.models import ( + Profile, Endpoint, DnsConfig, MonitorConfig + ) +except ImportError: + # This is handled in azure_rm_common + pass + + +def shorten_traffic_manager_dict(tmd): + return dict( + id=tmd['id'], + endpoints=[endpoint['id'] for endpoint in tmd['endpoints']] if tmd['endpoints'] else [] + ) + + +def traffic_manager_profile_to_dict(tmp): + result = dict( + id=tmp.id, + name=tmp.name, + type=tmp.type, + tags=tmp.tags, + location=tmp.location, + profile_status=tmp.profile_status, + routing_method=tmp.traffic_routing_method, + dns_config=dict(), + monitor_config=dict(), + endpoints=[] + ) + if tmp.dns_config: + result['dns_config']['relative_name'] = tmp.dns_config.relative_name + result['dns_config']['fqdn'] = tmp.dns_config.fqdn + result['dns_config']['ttl'] = tmp.dns_config.ttl + if tmp.monitor_config: + result['monitor_config']['profile_monitor_status'] = tmp.monitor_config.profile_monitor_status + result['monitor_config']['protocol'] = tmp.monitor_config.protocol + result['monitor_config']['port'] = tmp.monitor_config.port + result['monitor_config']['path'] = tmp.monitor_config.path + result['monitor_config']['interval'] = tmp.monitor_config.interval_in_seconds + result['monitor_config']['timeout'] = tmp.monitor_config.timeout_in_seconds + result['monitor_config']['tolerated_failures'] = tmp.monitor_config.tolerated_number_of_failures + if tmp.endpoints: + for endpoint in tmp.endpoints: + result['endpoints'].append(dict( + id=endpoint.id, + name=endpoint.name, + type=endpoint.type, + target_resource_id=endpoint.target_resource_id, + target=endpoint.target, + endpoint_status=endpoint.endpoint_status, + weight=endpoint.weight, + priority=endpoint.priority, + endpoint_location=endpoint.endpoint_location, + endpoint_monitor_status=endpoint.endpoint_monitor_status, + min_child_endpoints=endpoint.min_child_endpoints, + geo_mapping=endpoint.geo_mapping + )) + return result + + +def create_dns_config_instance(dns_config): + return DnsConfig( + relative_name=dns_config['relative_name'], + ttl=dns_config['ttl'] + ) + + +def create_monitor_config_instance(monitor_config): + return MonitorConfig( + profile_monitor_status=monitor_config['profile_monitor_status'], + protocol=monitor_config['protocol'], + port=monitor_config['port'], + path=monitor_config['path'], + interval_in_seconds=monitor_config['interval'], + timeout_in_seconds=monitor_config['timeout'], + tolerated_number_of_failures=monitor_config['tolerated_failures'] + ) + + +dns_config_spec = dict( + relative_name=dict(type='str'), + ttl=dict(type='int') +) + +monitor_config_spec = dict( + profile_monitor_status=dict(type='str'), + protocol=dict(type='str'), + port=dict(type='int'), + path=dict(type='str'), + interval=dict(type='int'), + timeout=dict(type='int'), + tolerated_failures=dict(type='int') +) + + +class AzureRMTrafficManagerProfile(AzureRMModuleBase): + + def __init__(self): + self.module_arg_spec = dict( + resource_group=dict( + type='str', + required=True + ), + name=dict( + type='str', + required=True + ), + state=dict( + type='str', + default='present', + choices=['present', 'absent'] + ), + location=dict( + type='str', + default='global' + ), + profile_status=dict( + type='str', + default='enabled', + choices=['enabled', 'disabled'] + ), + routing_method=dict( + type='str', + default='performance', + choices=['performance', 'priority', 'weighted', 'geographic'] + ), + dns_config=dict( + type='dict', + options=dns_config_spec + ), + monitor_config=dict( + type='dict', + default=dict( + protocol='HTTP', + port=80, + path='/' + ), + options=monitor_config_spec + ), + ) + + self.resource_group = None + self.name = None + self.state = None + self.tags = None + self.location = None + self.profile_status = None + self.routing_method = None + self.dns_config = None + self.monitor_config = None + self.endpoints_copy = None + + self.results = dict( + changed=False + ) + + super(AzureRMTrafficManagerProfile, self).__init__(derived_arg_spec=self.module_arg_spec, + supports_check_mode=True, + supports_tags=True) + + def exec_module(self, **kwargs): + + for key in list(self.module_arg_spec.keys()) + ['tags']: + setattr(self, key, kwargs[key]) + + to_be_updated = False + + if not self.dns_config: + self.dns_config = dict( + relative_name=self.name, + ttl=60 + ) + + if not self.location: + self.location = 'global' + + response = self.get_traffic_manager_profile() + + if self.state == 'present': + if not response: + to_be_updated = True + else: + self.results = shorten_traffic_manager_dict(response) + self.log('Results : {0}'.format(response)) + update_tags, response['tags'] = self.update_tags(response['tags']) + + if update_tags: + to_be_updated = True + + to_be_updated = to_be_updated or self.check_update(response) + + if to_be_updated: + self.log("Need to Create / Update the Traffic Manager profile") + + if not self.check_mode: + self.results = shorten_traffic_manager_dict(self.create_update_traffic_manager_profile()) + self.log("Creation / Update done.") + + self.results['changed'] = True + return self.results + + elif self.state == 'absent' and response: + self.log("Need to delete the Traffic Manager profile") + self.results = shorten_traffic_manager_dict(response) + self.results['changed'] = True + + if self.check_mode: + return self.results + + self.delete_traffic_manager_profile() + + self.log("Traffic Manager profile deleted") + + return self.results + + def get_traffic_manager_profile(self): + ''' + Gets the properties of the specified Traffic Manager profile + + :return: deserialized Traffic Manager profile dict + ''' + self.log("Checking if Traffic Manager profile {0} is present".format(self.name)) + try: + response = self.traffic_manager_management_client.profiles.get(self.resource_group, self.name) + self.log("Response : {0}".format(response)) + self.log("Traffic Manager profile : {0} found".format(response.name)) + self.endpoints_copy = response.endpoints if response and response.endpoints else None + return traffic_manager_profile_to_dict(response) + except CloudError: + self.log('Did not find the Traffic Manager profile.') + return False + + def delete_traffic_manager_profile(self): + ''' + Deletes the specified Traffic Manager profile in the specified subscription and resource group. + :return: True + ''' + + self.log("Deleting the Traffic Manager profile {0}".format(self.name)) + try: + operation_result = self.traffic_manager_management_client.profiles.delete(self.resource_group, self.name) + return True + except CloudError as e: + self.log('Error attempting to delete the Traffic Manager profile.') + self.fail("Error deleting the Traffic Manager profile: {0}".format(e.message)) + return False + + def create_update_traffic_manager_profile(self): + ''' + Creates or updates a Traffic Manager profile. + + :return: deserialized Traffic Manager profile state dictionary + ''' + self.log("Creating / Updating the Traffic Manager profile {0}".format(self.name)) + + parameters = Profile( + tags=self.tags, + location=self.location, + profile_status=self.profile_status, + traffic_routing_method=self.routing_method, + dns_config=create_dns_config_instance(self.dns_config) if self.dns_config else None, + monitor_config=create_monitor_config_instance(self.monitor_config) if self.monitor_config else None, + endpoints=self.endpoints_copy + ) + try: + response = self.traffic_manager_management_client.profiles.create_or_update(self.resource_group, self.name, parameters) + return traffic_manager_profile_to_dict(response) + except CloudError as exc: + self.log('Error attempting to create the Traffic Manager.') + self.fail("Error creating the Traffic Manager: {0}".format(exc.message)) + + def check_update(self, response): + if self.location and normalize_location_name(response['location']) != normalize_location_name(self.location): + self.log("Location Diff - Origin {0} / Update {1}".format(response['location'], self.location)) + return True + + if self.profile_status and response['profile_status'].lower() != self.profile_status: + self.log("Profile Status Diff - Origin {0} / Update {1}".format(response['profile_status'], self.profile_status)) + return True + + if self.routing_method and response['routing_method'].lower() != self.routing_method: + self.log("Traffic Routing Method Diff - Origin {0} / Update {1}".format(response['routing_method'], self.routing_method)) + return True + + if self.dns_config and \ + (response['dns_config']['relative_name'] != self.dns_config['relative_name'] or response['dns_config']['ttl'] != self.dns_config['ttl']): + self.log("DNS Config Diff - Origin {0} / Update {1}".format(response['dns_config'], self.dns_config)) + return True + + for k, v in self.monitor_config.items(): + if v: + if str(v).lower() != str(response['monitor_config'][k]).lower(): + self.log("Monitor Config Diff - Origin {0} / Update {1}".format(response['monitor_config'], self.monitor_config)) + return True + return False + + +def main(): + """Main execution""" + AzureRMTrafficManagerProfile() + + +if __name__ == '__main__': + main() diff --git a/lib/ansible/modules/cloud/azure/azure_rm_trafficmanagerprofile_facts.py b/lib/ansible/modules/cloud/azure/azure_rm_trafficmanagerprofile_facts.py new file mode 100644 index 00000000000..c4f6044e211 --- /dev/null +++ b/lib/ansible/modules/cloud/azure/azure_rm_trafficmanagerprofile_facts.py @@ -0,0 +1,378 @@ +#!/usr/bin/python +# +# Copyright (c) 2018 Hai Cao, , Yunge Zhu +# +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = ''' +--- +module: azure_rm_trafficmanagerprofile_facts + +version_added: "2.7" + +short_description: Get Azure Traffic Manager profile facts + +description: + - Get facts for a Azure specific Traffic Manager profile or all Traffic Manager profiles. + +options: + name: + description: + - Limit results to a specific Traffic Manager profile. + resource_group: + description: + - The resource group to search for the desired Traffic Manager profile + tags: + description: + - Limit results by providing a list of tags. Format tags as 'key' or 'key:value'. + +extends_documentation_fragment: + - azure + +author: + - "Hai Cao " + - "Yunge Zhu " +''' + +EXAMPLES = ''' + - name: Get facts for one Traffic Manager profile + azure_rm_trafficmanager_facts: + name: Testing + resource_group: TestRG + + - name: Get facts for all Traffic Manager profiles + azure_rm_trafficmanager_facts: + + - name: Get facts by tags + azure_rm_trafficmanager_facts: + tags: + - Environment:Test +''' + +RETURN = ''' +tms: + description: List of Traffic Manager profiles. + returned: always + type: complex + contains: + resource_group: + description: + - Name of a resource group where the Traffic Manager profile exists. + returned: always + type: str + sample: testGroup + name: + description: + - Name of the Traffic Manager profile. + returned: always + type: str + sample: testTm + state: + description: + - The state of the Traffic Manager profile. + type: str + sample: present + location: + description: + - Location of the Traffic Manager profile. + type: str + sample: global + profile_status: + description: + - The status of the Traffic Manager profile. + type: str + sample: Enabled + routing_method: + description: + - The traffic routing method of the Traffic Manager profile. + type: str + sample: performance + dns_config: + description: + - The DNS settings of the Traffic Manager profile. + type: complex + sample: + relative_name: testTm + fqdn: testTm.trafficmanager.net + ttl: 60 + monitor_config: + description: + - The endpoint monitoring settings of the Traffic Manager profile. + type: complex + contains: + protocol: + description: + - The protocol (HTTP, HTTPS or TCP) used to probe for endpoint health. + type: str + sample: HTTP + port: + description: + - The TCP port used to probe for endpoint health. + type: int + sample: 80 + path: + description: + - The path relative to the endpoint domain name used to probe for endpoint health. + type: str + sample: / + interval: + description: + - The monitor interval for endpoints in this profile in seconds. + type: int + sample: 10 + timeout: + description: + - The monitor timeout for endpoints in this profile in seconds. + type: int + sample: 30 + tolerated_failures: + description: + - The number of consecutive failed health check before declaring an endpoint Degraded after the next failed health check. + type: int + sample: 3 + endpoints: + description: + - The list of endpoints in the Traffic Manager profile. + type: list + element: complex + contains: + id: + description: + - Fully qualified resource Id for the resource. + type: str + sample: /subscriptions/XXXXXX...XXXXXXXXX/resourceGroups/tmt/providers/Microsoft.Network/trafficManagerProfiles/tmtest/externalEndpoints/e1 + name: + description: + - The name of the endpoint. + type: str + sample: e1 + type: + description: + - The type of the endpoint. + type: str + sample: external_endpoints + target_resource_id: + description: + - The Azure Resource URI of the of the endpoint. + type: str + sample: /subscriptions/XXXXXX...XXXXXXXXX/resourceGroups/vscjavaci/providers/Microsoft.ClassicCompute/domainNames/vscjavaci + target: + description: + - The fully-qualified DNS name of the endpoint. + type: str + sample: 8.8.8.8 + status: + description: + - The status of the endpoint. + type: str + sample: Enabled + weight: + description: + - The weight of this endpoint when the profile has routing_method C(weighted). + type: int + sample: 10 + priority: + description: + - The priority of this endpoint when the profile has routing_method C(priority). + type: str + sample: 3 + location: + description: + - The location of endpoints when type is C(external_endpoints) or C(nested_endpoints), and profile routing_method is (performance). + type: str + sample: East US + min_child_endpoints: + description: + - The minimum number of endpoints that must be available in the child profile to make the parent profile available. + type: int + sample: 3 + geo_mapping: + description: + - The list of countries/regions mapped to this endpoint when the profile has routing_method C(geographic). + type: list + sample: [ + "GEO-NA", + "GEO-AS" + ] +''' + +from ansible.module_utils.azure_rm_common import AzureRMModuleBase +from ansible.module_utils.common.dict_transformations import _camel_to_snake + +try: + from msrestazure.azure_exceptions import CloudError + from azure.common import AzureHttpError +except: + # handled in azure_rm_common + pass + +import re + +AZURE_OBJECT_CLASS = 'trafficManagerProfiles' + + +def serialize_endpoint(endpoint): + result = dict( + id=endpoint.id, + name=endpoint.name, + target_resource_id=endpoint.target_resource_id, + target=endpoint.target, + status=endpoint.endpoint_status, + weight=endpoint.weight, + priority=endpoint.priority, + location=endpoint.endpoint_location, + min_child_endpoints=endpoint.min_child_endpoints, + geo_mapping=endpoint.geo_mapping, + ) + + if endpoint.type: + result['type'] = _camel_to_snake(endpoint.type.split("/")[-1]) + + return result + + +class AzureRMTrafficManagerProfileFacts(AzureRMModuleBase): + """Utility class to get Azure Traffic Manager profile facts""" + + def __init__(self): + + self.module_args = dict( + name=dict(type='str'), + resource_group=dict(type='str'), + tags=dict(type='list') + ) + + self.results = dict( + changed=False, + tms=[] + ) + + self.name = None + self.resource_group = None + self.tags = None + + super(AzureRMTrafficManagerProfileFacts, self).__init__( + derived_arg_spec=self.module_args, + supports_tags=False, + facts_module=True + ) + + def exec_module(self, **kwargs): + + for key in self.module_args: + setattr(self, key, kwargs[key]) + + if self.name and not self.resource_group: + self.fail("Parameter error: resource group required when filtering by name.") + + if self.name: + self.results['tms'] = self.get_item() + elif self.resource_group: + self.results['tms'] = self.list_resource_group() + else: + self.results['tms'] = self.list_all() + + return self.results + + def get_item(self): + """Get a single Azure Traffic Manager profile""" + + self.log('Get properties for {0}'.format(self.name)) + + item = None + result = [] + + try: + item = self.traffic_manager_management_client.profiles.get( + self.resource_group, self.name) + except CloudError: + pass + + if item and self.has_tags(item.tags, self.tags): + result = [self.serialize_tm(item)] + + return result + + def list_resource_group(self): + """Get all Azure Traffic Managers profiles within a resource group""" + + self.log('List all Azure Traffic Managers within a resource group') + + try: + response = self.traffic_manager_management_client.profiles.list_by_resource_group( + self.resource_group) + except AzureHttpError as exc: + self.fail('Failed to list all items - {0}'.format(str(exc))) + + results = [] + for item in response: + if self.has_tags(item.tags, self.tags): + results.append(self.serialize_tm(item)) + + return results + + def list_all(self): + """Get all Azure Traffic Manager profiles within a subscription""" + self.log('List all Traffic Manager profiles within a subscription') + try: + response = self.traffic_manager_management_client.profiles.list_by_subscription() + except Exception as exc: + self.fail("Error listing all items - {0}".format(str(exc))) + + results = [] + for item in response: + if self.has_tags(item.tags, self.tags): + results.append(self.serialize_tm(item)) + return results + + def serialize_tm(self, tm): + ''' + Convert a Traffic Manager profile object to dict. + :param tm: Traffic Manager profile object + :return: dict + ''' + result = self.serialize_obj(tm, AZURE_OBJECT_CLASS) + + new_result = {} + new_result['id'] = tm.id + new_result['resource_group'] = re.sub('\\/.*', '', re.sub('.*resourceGroups\\/', '', result['id'])) + new_result['name'] = tm.name + new_result['state'] = 'present' + new_result['location'] = tm.location + new_result['profile_status'] = tm.profile_status + new_result['routing_method'] = tm.traffic_routing_method.lower() + new_result['dns_config'] = dict( + relative_name=tm.dns_config.relative_name, + fqdn=tm.dns_config.fqdn, + ttl=tm.dns_config.ttl + ) + new_result['monitor_config'] = dict( + profile_monitor_status=tm.monitor_config.profile_monitor_status, + protocol=tm.monitor_config.protocol, + port=tm.monitor_config.port, + path=tm.monitor_config.path, + interval=tm.monitor_config.interval_in_seconds, + timeout=tm.monitor_config.timeout_in_seconds, + tolerated_failures=tm.monitor_config.tolerated_number_of_failures + ) + new_result['endpoints'] = [serialize_endpoint(endpoint) for endpoint in tm.endpoints] + new_result['tags'] = tm.tags + return new_result + + +def main(): + """Main module execution code path""" + + AzureRMTrafficManagerProfileFacts() + + +if __name__ == '__main__': + main() diff --git a/packaging/requirements/requirements-azure.txt b/packaging/requirements/requirements-azure.txt index 80d167835e6..f897d86b4be 100644 --- a/packaging/requirements/requirements-azure.txt +++ b/packaging/requirements/requirements-azure.txt @@ -17,6 +17,7 @@ azure-mgmt-rdbms==1.2.0 azure-mgmt-resource==1.2.2 azure-mgmt-sql==0.7.1 azure-mgmt-storage==1.5.0 +azure-mgmt-trafficmanager==0.50.0 azure-mgmt-web==0.32.0 azure-nspkg==2.0.0 azure-storage==0.35.1 diff --git a/test/integration/targets/azure_rm_trafficmanagerprofile/aliases b/test/integration/targets/azure_rm_trafficmanagerprofile/aliases new file mode 100644 index 00000000000..239e3657797 --- /dev/null +++ b/test/integration/targets/azure_rm_trafficmanagerprofile/aliases @@ -0,0 +1,3 @@ +cloud/azure +shippable/azure/group4 +destructive diff --git a/test/integration/targets/azure_rm_trafficmanagerprofile/meta/main.yml b/test/integration/targets/azure_rm_trafficmanagerprofile/meta/main.yml new file mode 100644 index 00000000000..95e1952f989 --- /dev/null +++ b/test/integration/targets/azure_rm_trafficmanagerprofile/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_azure diff --git a/test/integration/targets/azure_rm_trafficmanagerprofile/tasks/main.yml b/test/integration/targets/azure_rm_trafficmanagerprofile/tasks/main.yml new file mode 100644 index 00000000000..dba81c0e259 --- /dev/null +++ b/test/integration/targets/azure_rm_trafficmanagerprofile/tasks/main.yml @@ -0,0 +1,289 @@ +- name: Prepare random number + set_fact: + tmname: "tm{{ resource_group | hash('md5') | truncate(7, True, '') }}{{ 1000 | random }}" + endpointname1: "ep1{{ resource_group | hash('md5') | truncate(7, True, '') }}{{ 1000 | random }}" + endpointname2: "ep2{{ resource_group | hash('md5') | truncate(7, True, '') }}{{ 1000 | random }}" + run_once: yes + + +- name: Create a Traffic Manager profile(check mode) + azure_rm_trafficmanagerprofile: + resource_group: "{{ resource_group }}" + name: "{{ tmname }}" + tags: + testing: testing + delete: on-exit + foo: bar + location: global + profile_status: enabled + routing_method: performance + dns_config: + relative_name: "{{ tmname }}" + ttl: 60 + monitor_config: + protocol: HTTPS + port: 80 + path: '/' + check_mode: yes + +- name: Check there is no Traffic Manager profile created + azure_rm_trafficmanagerprofile_facts: + resource_group: "{{ resource_group }}" + name: "{{ tmname }}" + register: fact + +- name: Check there is no Traffic Manager profile created + assert: { that: "{{ fact.tms | length }} == 0" } + +- name: Create a Traffic Manager profile + azure_rm_trafficmanagerprofile: + resource_group: "{{ resource_group }}" + name: "{{ tmname }}" + tags: + testing: testing + delete: on-exit + foo: bar + location: global + profile_status: enabled + routing_method: performance + dns_config: + relative_name: "{{ tmname }}" + ttl: 60 + monitor_config: + protocol: HTTPS + port: 80 + path: '/' + register: tm + +- name: Assert the Traffic Manager profile is well created + assert: + that: + - tm.changed + +- name: Gather Traffic Manager profile facts + azure_rm_trafficmanagerprofile_facts: + resource_group: "{{ resource_group }}" + name: "{{ tmname }}" + register: fact + +- name: Assert fact returns the created one + assert: + that: + - "fact.tms | length == 1" + - fact.tms[0].id == tm.id + - fact.tms[0].endpoints | length == 0 + +- name: Create a Traffic Manager profile (idempotent) + azure_rm_trafficmanagerprofile: + resource_group: "{{ resource_group }}" + name: "{{ tmname }}" + tags: + testing: testing + delete: on-exit + foo: bar + location: global + profile_status: enabled + routing_method: performance + dns_config: + relative_name: "{{ tmname }}" + ttl: 60 + monitor_config: + protocol: HTTPS + port: 80 + path: '/' + register: output + +- name: Assert idempotent + assert: + that: + - not output.changed + +- name: Update the Traffic Manager profile + azure_rm_trafficmanagerprofile: + resource_group: "{{ resource_group }}" + name: "{{ tmname }}" + tags: + testing: testing + delete: on-exit + foo: bar + location: global + profile_status: disabled + routing_method: priority + dns_config: + relative_name: "{{ tmname }}" + ttl: 60 + monitor_config: + protocol: HTTPS + port: 80 + path: '/' + register: output + +- name: Assert the Traffic Manager profile is updated + assert: + that: + - output.changed + +- name: Create Traffic Manager endpoint(check mode) + azure_rm_trafficmanagerendpoint: + resource_group: "{{ resource_group }}" + profile_name: "{{ tmname }}" + name: "{{ endpointname1 }}" + type: external_endpoints + location: westus + priority: 2 + weight: 1 + target: 1.2.3.4 + check_mode: yes + register: output + +- name: Assert check mode changed + assert: + that: + - output.changed + +- name: Get endpoint + azure_rm_trafficmanagerendpoint_facts: + resource_group: "{{ resource_group }}" + profile_name: "{{ tmname }}" + register: facts + +- name: Check no endpoint created in check mode + assert: + that: + - facts.endpoints | length == 0 + +- name: Create Traffic Manager endpoint + azure_rm_trafficmanagerendpoint: + resource_group: "{{ resource_group }}" + profile_name: "{{ tmname }}" + name: "{{ endpointname1 }}" + type: external_endpoints + location: westus + priority: 2 + weight: 1 + target: 1.2.3.4 + register: output + +- name: Assert endpoint create changed + assert: + that: + - output.changed + +- name: Get endpoint + azure_rm_trafficmanagerendpoint_facts: + resource_group: "{{ resource_group }}" + profile_name: "{{ tmname }}" + register: facts + +- name: Check endpoint created + assert: + that: + - facts.endpoints | length == 1 + - facts.endpoints[0].name == "{{ endpointname1 }}" + +- name: Create second Traffic Manager endpoint + azure_rm_trafficmanagerendpoint: + resource_group: "{{ resource_group }}" + profile_name: "{{ tmname }}" + name: "{{ endpointname2 }}" + type: external_endpoints + location: westus + priority: 1 + weight: 3 + target: 4.3.2.1 + +- name: Get endpoint + azure_rm_trafficmanagerendpoint_facts: + resource_group: "{{ resource_group }}" + profile_name: "{{ tmname }}" + register: facts + +- name: Check 2 endpoint in profile + assert: + that: + - facts.endpoints | length == 2 + +- name: Create endpoint (idempotent) + azure_rm_trafficmanagerendpoint: + resource_group: "{{ resource_group }}" + profile_name: "{{ tmname }}" + name: "{{ endpointname2 }}" + type: external_endpoints + location: westus + priority: 1 + weight: 3 + target: 4.3.2.1 + register: output + +- name: Assert endpoint creation idempotent + assert: + that: + - output.changed == False + +- name: Delete second endpoint + azure_rm_trafficmanagerendpoint: + resource_group: "{{ resource_group }}" + profile_name: "{{ tmname }}" + name: "{{ endpointname2 }}" + type: external_endpoints + state: absent + register: output + +- name: Assert endpoint deletion changed + assert: + that: + - output.changed + +- name: Get endpoint + azure_rm_trafficmanagerendpoint_facts: + resource_group: "{{ resource_group }}" + profile_name: "{{ tmname }}" + register: facts + +- name: Check 1 endpoint left in profile + assert: + that: + - facts.endpoints | length == 1 + +- name: Delete the Traffic Manager profile(check mode) + azure_rm_trafficmanagerprofile: + resource_group: "{{ resource_group }}" + name: "{{ tmname }}" + state: absent + check_mode: yes + +- name: Gather Traffic Manager profile facts + azure_rm_trafficmanagerprofile_facts: + resource_group: "{{ resource_group }}" + name: "{{ tmname }}" + register: fact + +- name: Assert the traffic manager profile is still there + assert: + that: + - "fact.tms | length == 1" + - fact.tms[0].id == tm.id + - fact.tms[0].endpoints | length == 1 + +- name: Delete the Traffic Manager profile + azure_rm_trafficmanagerprofile: + resource_group: "{{ resource_group }}" + name: "{{ tmname }}" + state: absent + register: output + +- name: Assert the Traffic Manager profile is well deleted + assert: + that: + - output.changed + +- name: Get Traffic Manager profile fact + azure_rm_trafficmanagerprofile_facts: + resource_group: "{{ resource_group }}" + name: "{{ tmname }}" + register: fact + +- name: Assert fact returns empty + assert: + that: + - "fact.tms | length == 0" diff --git a/test/runner/requirements/integration.cloud.azure.txt b/test/runner/requirements/integration.cloud.azure.txt index 80d167835e6..f897d86b4be 100644 --- a/test/runner/requirements/integration.cloud.azure.txt +++ b/test/runner/requirements/integration.cloud.azure.txt @@ -17,6 +17,7 @@ azure-mgmt-rdbms==1.2.0 azure-mgmt-resource==1.2.2 azure-mgmt-sql==0.7.1 azure-mgmt-storage==1.5.0 +azure-mgmt-trafficmanager==0.50.0 azure-mgmt-web==0.32.0 azure-nspkg==2.0.0 azure-storage==0.35.1