From eb9b7d00bcddcb9674e73d7e56fd1c286f957c92 Mon Sep 17 00:00:00 2001 From: Rick Elrod Date: Sat, 1 Aug 2020 15:34:35 -0500 Subject: [PATCH 1/3] Remove incidental_azure_rm_mariadbserver tests (#71037) Change: - Remove incidental test which has no remaining incidental coverage. Test Plan: - CI Signed-off-by: Rick Elrod (cherry picked from commit 96aa81e8ff691d644b5fd1c7a10b8726c476f5f1) --- .../incidental_azure_rm_mariadbserver/aliases | 3 - .../tasks/main.yml | 640 ------------------ .../plugins/modules/azure_rm_mariadbserver.py | 388 ----------- 3 files changed, 1031 deletions(-) delete mode 100644 test/integration/targets/incidental_azure_rm_mariadbserver/aliases delete mode 100644 test/integration/targets/incidental_azure_rm_mariadbserver/tasks/main.yml delete mode 100644 test/support/integration/plugins/modules/azure_rm_mariadbserver.py diff --git a/test/integration/targets/incidental_azure_rm_mariadbserver/aliases b/test/integration/targets/incidental_azure_rm_mariadbserver/aliases deleted file mode 100644 index 9901373af237db..00000000000000 --- a/test/integration/targets/incidental_azure_rm_mariadbserver/aliases +++ /dev/null @@ -1,3 +0,0 @@ -cloud/azure -destructive -shippable/azure/incidental diff --git a/test/integration/targets/incidental_azure_rm_mariadbserver/tasks/main.yml b/test/integration/targets/incidental_azure_rm_mariadbserver/tasks/main.yml deleted file mode 100644 index 5b33ffb951c3aa..00000000000000 --- a/test/integration/targets/incidental_azure_rm_mariadbserver/tasks/main.yml +++ /dev/null @@ -1,640 +0,0 @@ -- name: Prepare random number - set_fact: - rpfx: "{{ resource_group | hash('md5') | truncate(7, True, '') }}{{ 1000 | random }}" - run_once: yes - -- name: Create instance of MariaDB Server -- check mode - azure_rm_mariadbserver: - resource_group: "{{ resource_group }}" - name: mariadbsrv{{ rpfx }} - sku: - name: B_Gen5_1 - tier: Basic - location: westus2 - storage_mb: 51200 - version: 10.2 - enforce_ssl: True - admin_username: zimxyz - admin_password: Testpasswordxyz12! - check_mode: yes - register: output -- name: Assert the resource instance is well created - assert: - that: - - output.changed - -- name: Create instance of MariaDB Server - azure_rm_mariadbserver: - resource_group: "{{ resource_group }}" - name: mariadbsrv{{ rpfx }} - sku: - name: B_Gen5_1 - tier: Basic - location: westus2 - storage_mb: 51200 - version: 10.2 - enforce_ssl: True - admin_username: zimxyz - admin_password: Testpasswordxyz12! - register: output -- name: Assert the resource instance is well created - assert: - that: - - output.changed - - output.state == 'Ready' - -- name: Create again instance of MariaDB Server - azure_rm_mariadbserver: - resource_group: "{{ resource_group }}" - name: mariadbsrv{{ rpfx }} - sku: - name: B_Gen5_1 - tier: Basic - location: westus2 - storage_mb: 51200 - version: 10.2 - enforce_ssl: True - admin_username: zimxyz - admin_password: Testpasswordxyz12! - register: output -- name: Assert the state has not changed - assert: - that: - - output.changed == false - - output.state == 'Ready' - -- name: Update instance of MariaDB Server, change storage size - azure_rm_mariadbserver: - resource_group: "{{ resource_group }}" - name: mariadbsrv{{ rpfx }} - sku: - name: B_Gen5_1 - tier: Basic - location: westus2 - storage_mb: 128000 - version: 10.2 - enforce_ssl: True - admin_username: zimxyz - admin_password: Testpasswordxyz12! - register: output -- name: Assert the state has not changed - assert: - that: - - output.changed - - output.state == 'Ready' -- debug: - var: output - -- name: Gather facts MariaDB Server - azure_rm_mariadbserver_facts: - resource_group: "{{ resource_group }}" - name: mariadbsrv{{ rpfx }} - register: output -- name: Assert that storage size is correct - assert: - that: - - output.servers[0]['storage_mb'] == 128000 - -- name: Create second instance of MariaDB Server - azure_rm_mariadbserver: - resource_group: "{{ resource_group }}" - name: mariadbsrv{{ rpfx }}second - sku: - name: B_Gen5_1 - tier: Basic - location: westus2 - storage_mb: 51200 - version: 10.2 - enforce_ssl: True - admin_username: zimxyz - admin_password: Testpasswordxyz12! - tags: - aaa: bbb - -- name: Create second instance of MariaDB Server - azure_rm_mariadbserver: - resource_group: "{{ resource_group }}" - name: mariadbsrv{{ rpfx }}second - sku: - name: B_Gen5_1 - tier: Basic - location: westus2 - storage_mb: 51200 - version: 10.2 - enforce_ssl: True - admin_username: zimxyz - admin_password: Testpasswordxyz12! - tags: - ccc: ddd - -- name: Gather facts MariaDB Server - azure_rm_mariadbserver_facts: - resource_group: "{{ resource_group }}" - name: mariadbsrv{{ rpfx }}second - register: output - -- name: Assert that facts are returned - assert: - that: - - output.changed == False - - output.servers[0]['id'] != None - - output.servers[0]['name'] != None - - output.servers[0]['location'] != None - - output.servers[0]['sku']['name'] != None - - output.servers[0]['sku']['tier'] != None - - output.servers[0]['sku']['capacity'] != None - - output.servers[0]['version'] != None - - output.servers[0]['user_visible_state'] != None - - output.servers[0]['fully_qualified_domain_name'] != None - - output.servers[0]['tags']['aaa'] == 'bbb' - - output.servers[0]['tags']['ccc'] == 'ddd' - -- name: Gather facts MariaDB Server - azure_rm_mariadbserver_facts: - resource_group: "{{ resource_group }}" - register: output -- name: Assert that facts are returned - assert: - that: - - output.changed == False - - output.servers[0]['id'] != None - - output.servers[0]['name'] != None - - output.servers[0]['location'] != None - - output.servers[0]['sku']['name'] != None - - output.servers[0]['sku']['tier'] != None - - output.servers[0]['sku']['capacity'] != None - - output.servers[0]['version'] != None - - output.servers[0]['user_visible_state'] != None - - output.servers[0]['fully_qualified_domain_name'] != None - - output.servers[1]['id'] != None - - output.servers[1]['name'] != None - - output.servers[1]['location'] != None - - output.servers[1]['sku']['name'] != None - - output.servers[1]['sku']['tier'] != None - - output.servers[1]['sku']['capacity'] != None - - output.servers[1]['version'] != None - - output.servers[1]['user_visible_state'] != None - - output.servers[1]['fully_qualified_domain_name'] != None - -# -# azure_rm_mariadbdatabase tests below -# -- name: Create instance of MariaDB Database -- check mode - azure_rm_mariadbdatabase: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: testdatabase - check_mode: yes - register: output -- name: Assert the resource instance is well created - assert: - that: - - output.changed - -- name: Create instance of MariaDB Database - azure_rm_mariadbdatabase: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: testdatabase - collation: latin1_swedish_ci - charset: latin1 - register: output -- name: Assert the resource instance is well created - assert: - that: - - output.changed - - output.name == 'testdatabase' - -- name: Create again instance of MariaDB Database - azure_rm_mariadbdatabase: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: testdatabase - collation: latin1_swedish_ci - charset: latin1 - register: output -- name: Assert the state has not changed - assert: - that: - - output.changed == false - - output.name == 'testdatabase' - -- name: Try to update database without force_update - azure_rm_mariadbdatabase: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: testdatabase - collation: latin1_czech_ci - charset: latin1 - ignore_errors: yes - register: output -- name: Assert that nothing has changed - assert: - that: - - output.changed == False - -- name: Update instance of database using force_update - azure_rm_mariadbdatabase: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: testdatabase - collation: latin1_czech_ci - charset: latin1 - force_update: yes - register: output -- name: Assert the state has changed - assert: - that: - - output.changed - - output.name == 'testdatabase' - -- name: Create second instance of MariaDB Database - azure_rm_mariadbdatabase: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: testdatabase2 - -- name: Gather facts MariaDB Database - azure_rm_mariadbdatabase_facts: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: testdatabase - register: output - -- name: Assert that facts are returned - assert: - that: - - output.changed == False - - output.databases[0]['server_name'] != None - - output.databases[0]['name'] != None - - output.databases[0]['charset'] != None - - output.databases[0]['collation'] != None - -- name: Gather facts MariaDB Database - azure_rm_mariadbdatabase_facts: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - register: output -- name: Assert that facts are returned - assert: - that: - - output.changed == False - - output.databases[0]['server_name'] != None - - output.databases[0]['name'] != None - - output.databases[0]['charset'] != None - - output.databases[0]['collation'] != None - - output.databases[1]['server_name'] != None - - output.databases[1]['name'] != None - - output.databases[1]['charset'] != None - - output.databases[1]['collation'] != None - -- name: Delete instance of MariaDB Database -- check mode - azure_rm_mariadbdatabase: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: testdatabase - state: absent - check_mode: yes - register: output -- name: Assert the state has changed - assert: - that: - - output.changed - -- name: Delete instance of MariaDB Database - azure_rm_mariadbdatabase: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: testdatabase - state: absent - register: output -- name: Assert the state has changed - assert: - that: - - output.changed - -- name: Delete unexisting instance of MariaDB Database - azure_rm_mariadbdatabase: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: testdatabase - state: absent - register: output -- name: Assert the state has changed - assert: - that: - - output.changed == false - -# -# azure_rm_firewallrule tests below -# -- name: Create instance of Firewall Rule -- check mode - azure_rm_mariadbfirewallrule: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: firewallrule{{ rpfx }} - start_ip_address: 172.28.10.136 - end_ip_address: 172.28.10.138 - check_mode: yes - register: output -- name: Assert the resource instance is well created - assert: - that: - - output.changed - -- name: Create instance of Firewall Rule - azure_rm_mariadbfirewallrule: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: firewallrule{{ rpfx }} - start_ip_address: 172.28.10.136 - end_ip_address: 172.28.10.138 - register: output -- name: Assert the resource instance is well created - assert: - that: - - output.changed - -- name: Create again instance of Firewall Rule - azure_rm_mariadbfirewallrule: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: firewallrule{{ rpfx }} - start_ip_address: 172.28.10.136 - end_ip_address: 172.28.10.138 - register: output -- name: Assert the state has not changed - assert: - that: - - output.changed == false - -- name: Delete instance of Firewall Rule -- check mode - azure_rm_mariadbfirewallrule: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: firewallrule{{ rpfx }} - state: absent - check_mode: yes - register: output -- name: Assert the state has changed - assert: - that: - - output.changed - -- name: Create instance of Firewall Rule -- second - azure_rm_mariadbfirewallrule: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: firewallrule{{ rpfx }}second - start_ip_address: 172.28.10.136 - end_ip_address: 172.28.10.138 - register: output -- name: Assert the state has changed - assert: - that: - - output.changed - -- name: Gather facts MariaDB Firewall Rule - azure_rm_mariadbfirewallrule_facts: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: firewallrule{{ rpfx }} - register: output -- name: Assert that facts are returned - assert: - that: - - output.changed == False - - output.rules[0].id != None - - output.rules[0].server_name != None - - output.rules[0].name != None - - output.rules[0].start_ip_address != None - - output.rules[0].end_ip_address != None - - "output.rules | length == 1" - -- name: Gather facts MariaDB Firewall Rule - azure_rm_mariadbfirewallrule_facts: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - register: output -- name: Assert that facts are returned - assert: - that: - - output.changed == False - - output.rules[0].id != None - - output.rules[0].server_name != None - - output.rules[0].name != None - - output.rules[0].start_ip_address != None - - output.rules[0].end_ip_address != None - - output.rules[1].id != None - - output.rules[1].name != None - - output.rules[1].start_ip_address != None - - output.rules[1].end_ip_address != None - - "output.rules | length == 2" - -- name: Delete instance of Firewall Rule - azure_rm_mariadbfirewallrule: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: firewallrule{{ rpfx }} - state: absent - register: output -- name: Assert the state has changed - assert: - that: - - output.changed - -- name: Delete unexisting instance of Firewall Rule - azure_rm_mariadbfirewallrule: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: firewallrule{{ rpfx }} - state: absent - register: output -- name: Assert the state has changed - assert: - that: - - output.changed == false - -- name: Delete instance of Firewall Rule - second - azure_rm_mariadbfirewallrule: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: firewallrule{{ rpfx }}second - state: absent - -- name: Gather facts MariaDB Firewall Rule - azure_rm_mariadbfirewallrule_facts: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: firewallrule{{ rpfx }} - register: output -- name: Assert that empty list was returned - assert: - that: - - output.changed == False - - "output.rules | length == 0" - -# -# configuration -# -- name: Create instance of Configuration -- check mode - azure_rm_mariadbconfiguration: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: event_scheduler - value: "ON" - check_mode: yes - register: output -- name: Assert that change was registered - assert: - that: - - output.changed - -- name: Try to delete default configuraion - azure_rm_mariadbconfiguration_facts: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: event_scheduler - register: output -- name: Get facts of event_scheduler - debug: - var: output - -- name: Try to delete default configuraion - azure_rm_mariadbconfiguration: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: event_scheduler - state: absent - register: output -- name: Assert that change was registered - assert: - that: - - not output.changed - -- name: Try to change default configuraion - azure_rm_mariadbconfiguration: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: event_scheduler - value: "ON" - register: output -- name: Assert that change was registered - assert: - that: - - output.changed - -- name: Try to change default configuration -- idempotent - azure_rm_mariadbconfiguration: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: event_scheduler - value: "ON" - register: output -- name: Assert that change was registered - assert: - that: - - not output.changed - -- name: Try to reset configuration - azure_rm_mariadbconfiguration: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: event_scheduler - state: absent - register: output -- name: Assert that change was registered - assert: - that: - - output.changed - -- name: Try to reset configuration -- idempotent - azure_rm_mariadbconfiguration: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: event_scheduler - state: absent - register: output -- name: Assert that change was registered - assert: - that: - - not output.changed - -- name: Gather facts MariaDB Configuration - azure_rm_mariadbconfiguration_facts: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: event_scheduler - register: output -- name: Assert that facts are returned - assert: - that: - - output.changed == False - - output.settings[0].id != None - - output.settings[0].name != None - - output.settings[0].value != None - - output.settings[0].description != None - - output.settings[0].source != None - - output.settings | length == 1 - -- name: Gather facts MariaDB Configuration - azure_rm_mariadbconfiguration_facts: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - register: output -- name: Assert that facts are returned - assert: - that: - - output.changed == False - - output.settings[0].id != None - - output.settings[0].name != None - - output.settings[0].value != None - - output.settings[0].description != None - - output.settings[0].source != None - - output.settings | length > 1 - -# -# clean up azure_rm_mariadbserver test -# - -- name: Delete instance of MariaDB Server -- check mode - azure_rm_mariadbserver: - resource_group: "{{ resource_group }}" - name: mariadbsrv{{ rpfx }} - state: absent - check_mode: yes - register: output -- name: Assert the state has changed - assert: - that: - - output.changed - -- name: Delete instance of MariaDB Server - azure_rm_mariadbserver: - resource_group: "{{ resource_group }}" - name: mariadbsrv{{ rpfx }} - state: absent - register: output -- name: Assert the state has changed - assert: - that: - - output.changed - -- name: Delete unexisting instance of MariaDB Server - azure_rm_mariadbserver: - resource_group: "{{ resource_group }}" - name: mariadbsrv{{ rpfx }} - state: absent - register: output -- name: Assert the state has changed - assert: - that: - - output.changed == false - -- name: Delete second instance of MariaDB Server - azure_rm_mariadbserver: - resource_group: "{{ resource_group }}" - name: mariadbsrv{{ rpfx }}second - state: absent - async: 400 - poll: 0 diff --git a/test/support/integration/plugins/modules/azure_rm_mariadbserver.py b/test/support/integration/plugins/modules/azure_rm_mariadbserver.py deleted file mode 100644 index 30a2998844a6f5..00000000000000 --- a/test/support/integration/plugins/modules/azure_rm_mariadbserver.py +++ /dev/null @@ -1,388 +0,0 @@ -#!/usr/bin/python -# -# Copyright (c) 2017 Zim Kalinowski, -# Copyright (c) 2019 Matti Ranta, (@techknowlogick) -# -# 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_mariadbserver -version_added: "2.8" -short_description: Manage MariaDB Server instance -description: - - Create, update and delete instance of MariaDB Server. - -options: - resource_group: - description: - - The name of the resource group that contains the resource. You can obtain this value from the Azure Resource Manager API or the portal. - required: True - name: - description: - - The name of the server. - required: True - sku: - description: - - The SKU (pricing tier) of the server. - suboptions: - name: - description: - - The name of the SKU, typically, tier + family + cores, for example C(B_Gen4_1), C(GP_Gen5_8). - tier: - description: - - The tier of the particular SKU, for example C(Basic). - choices: - - basic - - standard - capacity: - description: - - The scale up/out capacity, representing server's compute units. - type: int - size: - description: - - The size code, to be interpreted by resource as appropriate. - location: - description: - - Resource location. If not set, location from the resource group will be used as default. - storage_mb: - description: - - The maximum storage allowed for a server. - type: int - version: - description: - - Server version. - choices: - - 10.2 - enforce_ssl: - description: - - Enable SSL enforcement. - type: bool - default: False - admin_username: - description: - - The administrator's login name of a server. Can only be specified when the server is being created (and is required for creation). - admin_password: - description: - - The password of the administrator login. - create_mode: - description: - - Create mode of SQL Server. - default: Default - state: - description: - - Assert the state of the MariaDB Server. Use C(present) to create or update a server and C(absent) to delete it. - default: present - choices: - - absent - - present - -extends_documentation_fragment: - - azure - - azure_tags - -author: - - Zim Kalinowski (@zikalino) - - Matti Ranta (@techknowlogick) - -''' - -EXAMPLES = ''' - - name: Create (or update) MariaDB Server - azure_rm_mariadbserver: - resource_group: myResourceGroup - name: testserver - sku: - name: B_Gen5_1 - tier: Basic - location: eastus - storage_mb: 1024 - enforce_ssl: True - version: 10.2 - admin_username: cloudsa - admin_password: password -''' - -RETURN = ''' -id: - description: - - Resource ID. - returned: always - type: str - sample: /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/Microsoft.DBforMariaDB/servers/mariadbsrv1b6dd89593 -version: - description: - - Server version. Possible values include C(10.2). - returned: always - type: str - sample: 10.2 -state: - description: - - A state of a server that is visible to user. Possible values include C(Ready), C(Dropping), C(Disabled). - returned: always - type: str - sample: Ready -fully_qualified_domain_name: - description: - - The fully qualified domain name of a server. - returned: always - type: str - sample: mariadbsrv1b6dd89593.mariadb.database.azure.com -''' - -import time -from ansible.module_utils.azure_rm_common import AzureRMModuleBase - -try: - from azure.mgmt.rdbms.mariadb import MariaDBManagementClient - from msrestazure.azure_exceptions import CloudError - from msrest.polling import LROPoller - from msrest.serialization import Model -except ImportError: - # This is handled in azure_rm_common - pass - - -class Actions: - NoAction, Create, Update, Delete = range(4) - - -class AzureRMMariaDbServers(AzureRMModuleBase): - """Configuration class for an Azure RM MariaDB Server resource""" - - def __init__(self): - self.module_arg_spec = dict( - resource_group=dict( - type='str', - required=True - ), - name=dict( - type='str', - required=True - ), - sku=dict( - type='dict' - ), - location=dict( - type='str' - ), - storage_mb=dict( - type='int' - ), - version=dict( - type='str', - choices=['10.2'] - ), - enforce_ssl=dict( - type='bool', - default=False - ), - create_mode=dict( - type='str', - default='Default' - ), - admin_username=dict( - type='str' - ), - admin_password=dict( - type='str', - no_log=True - ), - state=dict( - type='str', - default='present', - choices=['present', 'absent'] - ) - ) - - self.resource_group = None - self.name = None - self.parameters = dict() - self.tags = None - - self.results = dict(changed=False) - self.state = None - self.to_do = Actions.NoAction - - super(AzureRMMariaDbServers, self).__init__(derived_arg_spec=self.module_arg_spec, - supports_check_mode=True, - supports_tags=True) - - def exec_module(self, **kwargs): - """Main module execution method""" - - for key in list(self.module_arg_spec.keys()) + ['tags']: - if hasattr(self, key): - setattr(self, key, kwargs[key]) - elif kwargs[key] is not None: - if key == "sku": - ev = kwargs[key] - if 'tier' in ev: - if ev['tier'] == 'basic': - ev['tier'] = 'Basic' - elif ev['tier'] == 'standard': - ev['tier'] = 'Standard' - self.parameters["sku"] = ev - elif key == "location": - self.parameters["location"] = kwargs[key] - elif key == "storage_mb": - self.parameters.setdefault("properties", {}).setdefault("storage_profile", {})["storage_mb"] = kwargs[key] - elif key == "version": - self.parameters.setdefault("properties", {})["version"] = kwargs[key] - elif key == "enforce_ssl": - self.parameters.setdefault("properties", {})["ssl_enforcement"] = 'Enabled' if kwargs[key] else 'Disabled' - elif key == "create_mode": - self.parameters.setdefault("properties", {})["create_mode"] = kwargs[key] - elif key == "admin_username": - self.parameters.setdefault("properties", {})["administrator_login"] = kwargs[key] - elif key == "admin_password": - self.parameters.setdefault("properties", {})["administrator_login_password"] = kwargs[key] - - old_response = None - response = None - - resource_group = self.get_resource_group(self.resource_group) - - if "location" not in self.parameters: - self.parameters["location"] = resource_group.location - - old_response = self.get_mariadbserver() - - if not old_response: - self.log("MariaDB Server instance doesn't exist") - if self.state == 'absent': - self.log("Old instance didn't exist") - else: - self.to_do = Actions.Create - else: - self.log("MariaDB Server instance already exists") - if self.state == 'absent': - self.to_do = Actions.Delete - elif self.state == 'present': - self.log("Need to check if MariaDB Server instance has to be deleted or may be updated") - update_tags, newtags = self.update_tags(old_response.get('tags', {})) - if update_tags: - self.tags = newtags - self.to_do = Actions.Update - - if (self.to_do == Actions.Create) or (self.to_do == Actions.Update): - self.log("Need to Create / Update the MariaDB Server instance") - - if self.check_mode: - self.results['changed'] = True - return self.results - - response = self.create_update_mariadbserver() - - if not old_response: - self.results['changed'] = True - else: - self.results['changed'] = old_response.__ne__(response) - self.log("Creation / Update done") - elif self.to_do == Actions.Delete: - self.log("MariaDB Server instance deleted") - self.results['changed'] = True - - if self.check_mode: - return self.results - - self.delete_mariadbserver() - # make sure instance is actually deleted, for some Azure resources, instance is hanging around - # for some time after deletion -- this should be really fixed in Azure - while self.get_mariadbserver(): - time.sleep(20) - else: - self.log("MariaDB Server instance unchanged") - self.results['changed'] = False - response = old_response - - if response: - self.results["id"] = response["id"] - self.results["version"] = response["version"] - self.results["state"] = response["user_visible_state"] - self.results["fully_qualified_domain_name"] = response["fully_qualified_domain_name"] - - return self.results - - def create_update_mariadbserver(self): - ''' - Creates or updates MariaDB Server with the specified configuration. - - :return: deserialized MariaDB Server instance state dictionary - ''' - self.log("Creating / Updating the MariaDB Server instance {0}".format(self.name)) - - try: - self.parameters['tags'] = self.tags - if self.to_do == Actions.Create: - response = self.mariadb_client.servers.create(resource_group_name=self.resource_group, - server_name=self.name, - parameters=self.parameters) - else: - # structure of parameters for update must be changed - self.parameters.update(self.parameters.pop("properties", {})) - response = self.mariadb_client.servers.update(resource_group_name=self.resource_group, - server_name=self.name, - parameters=self.parameters) - if isinstance(response, LROPoller): - response = self.get_poller_result(response) - - except CloudError as exc: - self.log('Error attempting to create the MariaDB Server instance.') - self.fail("Error creating the MariaDB Server instance: {0}".format(str(exc))) - return response.as_dict() - - def delete_mariadbserver(self): - ''' - Deletes specified MariaDB Server instance in the specified subscription and resource group. - - :return: True - ''' - self.log("Deleting the MariaDB Server instance {0}".format(self.name)) - try: - response = self.mariadb_client.servers.delete(resource_group_name=self.resource_group, - server_name=self.name) - except CloudError as e: - self.log('Error attempting to delete the MariaDB Server instance.') - self.fail("Error deleting the MariaDB Server instance: {0}".format(str(e))) - - return True - - def get_mariadbserver(self): - ''' - Gets the properties of the specified MariaDB Server. - - :return: deserialized MariaDB Server instance state dictionary - ''' - self.log("Checking if the MariaDB Server instance {0} is present".format(self.name)) - found = False - try: - response = self.mariadb_client.servers.get(resource_group_name=self.resource_group, - server_name=self.name) - found = True - self.log("Response : {0}".format(response)) - self.log("MariaDB Server instance : {0} found".format(response.name)) - except CloudError as e: - self.log('Did not find the MariaDB Server instance.') - if found is True: - return response.as_dict() - - return False - - -def main(): - """Main execution""" - AzureRMMariaDbServers() - - -if __name__ == '__main__': - main() From 972680cf7c85403b8dead173a1c0d8828e1e9953 Mon Sep 17 00:00:00 2001 From: Sam Doran Date: Wed, 12 May 2021 16:36:17 -0400 Subject: [PATCH 2/3] Remove incidental azure tests (#71164) Change: - All incidental coverage from these tests has moved to intentional coverage. Test Plan: - CI Signed-off-by: Rick Elrod (cherry picked from commit 4f54fb8d439ea8a41dfe1084fbc6d8e0e3f42264) --- .../incidental_azure_rm_resource/aliases | 3 - .../tasks/main.yml | 158 -- test/sanity/ignore.txt | 4 - .../plugins/module_utils/azure_rm_common.py | 1473 ----------------- .../module_utils/azure_rm_common_rest.py | 97 -- .../_azure_rm_mariadbconfiguration_facts.py | 1 - .../_azure_rm_mariadbdatabase_facts.py | 1 - .../_azure_rm_mariadbfirewallrule_facts.py | 1 - .../modules/_azure_rm_mariadbserver_facts.py | 1 - .../modules/_azure_rm_resource_facts.py | 1 - .../plugins/modules/_azure_rm_webapp_facts.py | 1 - .../modules/azure_rm_appserviceplan.py | 379 ----- .../plugins/modules/azure_rm_functionapp.py | 421 ----- .../modules/azure_rm_functionapp_info.py | 207 --- .../modules/azure_rm_mariadbconfiguration.py | 241 --- .../azure_rm_mariadbconfiguration_info.py | 217 --- .../modules/azure_rm_mariadbdatabase.py | 304 ---- .../modules/azure_rm_mariadbdatabase_info.py | 212 --- .../modules/azure_rm_mariadbfirewallrule.py | 277 ---- .../azure_rm_mariadbfirewallrule_info.py | 208 --- .../modules/azure_rm_mariadbserver_info.py | 265 --- .../plugins/modules/azure_rm_resource.py | 427 ----- .../plugins/modules/azure_rm_resource_info.py | 432 ----- .../modules/azure_rm_storageaccount.py | 684 -------- .../plugins/modules/azure_rm_webapp.py | 1070 ------------ .../plugins/modules/azure_rm_webapp_info.py | 489 ------ .../plugins/modules/azure_rm_webappslot.py | 1058 ------------ test/utils/shippable/incidental/azure.sh | 1 - 28 files changed, 8633 deletions(-) delete mode 100644 test/integration/targets/incidental_azure_rm_resource/aliases delete mode 100644 test/integration/targets/incidental_azure_rm_resource/tasks/main.yml delete mode 100644 test/support/integration/plugins/module_utils/azure_rm_common.py delete mode 100644 test/support/integration/plugins/module_utils/azure_rm_common_rest.py delete mode 120000 test/support/integration/plugins/modules/_azure_rm_mariadbconfiguration_facts.py delete mode 120000 test/support/integration/plugins/modules/_azure_rm_mariadbdatabase_facts.py delete mode 120000 test/support/integration/plugins/modules/_azure_rm_mariadbfirewallrule_facts.py delete mode 120000 test/support/integration/plugins/modules/_azure_rm_mariadbserver_facts.py delete mode 120000 test/support/integration/plugins/modules/_azure_rm_resource_facts.py delete mode 120000 test/support/integration/plugins/modules/_azure_rm_webapp_facts.py delete mode 100644 test/support/integration/plugins/modules/azure_rm_appserviceplan.py delete mode 100644 test/support/integration/plugins/modules/azure_rm_functionapp.py delete mode 100644 test/support/integration/plugins/modules/azure_rm_functionapp_info.py delete mode 100644 test/support/integration/plugins/modules/azure_rm_mariadbconfiguration.py delete mode 100644 test/support/integration/plugins/modules/azure_rm_mariadbconfiguration_info.py delete mode 100644 test/support/integration/plugins/modules/azure_rm_mariadbdatabase.py delete mode 100644 test/support/integration/plugins/modules/azure_rm_mariadbdatabase_info.py delete mode 100644 test/support/integration/plugins/modules/azure_rm_mariadbfirewallrule.py delete mode 100644 test/support/integration/plugins/modules/azure_rm_mariadbfirewallrule_info.py delete mode 100644 test/support/integration/plugins/modules/azure_rm_mariadbserver_info.py delete mode 100644 test/support/integration/plugins/modules/azure_rm_resource.py delete mode 100644 test/support/integration/plugins/modules/azure_rm_resource_info.py delete mode 100644 test/support/integration/plugins/modules/azure_rm_storageaccount.py delete mode 100644 test/support/integration/plugins/modules/azure_rm_webapp.py delete mode 100644 test/support/integration/plugins/modules/azure_rm_webapp_info.py delete mode 100644 test/support/integration/plugins/modules/azure_rm_webappslot.py delete mode 120000 test/utils/shippable/incidental/azure.sh diff --git a/test/integration/targets/incidental_azure_rm_resource/aliases b/test/integration/targets/incidental_azure_rm_resource/aliases deleted file mode 100644 index 9901373af237db..00000000000000 --- a/test/integration/targets/incidental_azure_rm_resource/aliases +++ /dev/null @@ -1,3 +0,0 @@ -cloud/azure -destructive -shippable/azure/incidental diff --git a/test/integration/targets/incidental_azure_rm_resource/tasks/main.yml b/test/integration/targets/incidental_azure_rm_resource/tasks/main.yml deleted file mode 100644 index 7c3024a5ef469b..00000000000000 --- a/test/integration/targets/incidental_azure_rm_resource/tasks/main.yml +++ /dev/null @@ -1,158 +0,0 @@ -- name: Prepare random number - set_fact: - nsgname: "{{ resource_group | hash('md5') | truncate(7, True, '') }}{{ 1000 | random }}" - storageaccountname: "stacc{{ resource_group | hash('md5') | truncate(7, True, '') }}{{ 1000 | random }}" - dbname: "mdb{{ resource_group | hash('md5') | truncate(7, True, '') }}{{ 1000 | random }}" - run_once: yes - -- name: Call REST API - azure_rm_resource: - api_version: '2018-02-01' - resource_group: "{{ resource_group }}" - provider: network - resource_type: networksecuritygroups - resource_name: "{{ nsgname }}" - body: - location: eastus - idempotency: yes - register: output - -- name: Assert that something has changed - assert: - that: output.changed - -- name: Call REST API - azure_rm_resource: - api_version: '2018-02-01' - resource_group: "{{ resource_group }}" - provider: network - resource_type: networksecuritygroups - resource_name: "{{ nsgname }}" - body: - location: eastus - idempotency: yes - register: output - -- name: Assert that nothing has changed - assert: - that: not output.changed - -- name: Call REST API - azure_rm_resource: - api_version: '2018-02-01' - resource_group: "{{ resource_group }}" - provider: network - resource_type: networksecuritygroups - resource_name: "{{ nsgname }}" - body: - location: eastus - tags: - a: "abc" - b: "cde" - idempotency: yes - register: output - -- name: Assert that something has changed - assert: - that: output.changed - -- name: Try to get information about account - azure_rm_resource_facts: - api_version: '2018-02-01' - resource_group: "{{ resource_group }}" - provider: network - resource_type: networksecuritygroups - resource_name: "{{ nsgname }}" - register: output - -- name: Assert value was returned - assert: - that: - - not output.changed - - output.response[0]['name'] != None - - output.response | length == 1 - -- name: Try to query a list - azure_rm_resource_facts: - api_version: '2018-02-01' - resource_group: "{{ resource_group }}" - provider: network - resource_type: networksecuritygroups - register: output -- name: Assert value was returned - assert: - that: - - not output.changed - - output.response[0]['name'] != None - - output.response | length >= 1 - -- name: Try to query a list - same without API version - azure_rm_resource_facts: - resource_group: "{{ resource_group }}" - provider: network - resource_type: networksecuritygroups - register: output -- name: Assert value was returned - assert: - that: - - not output.changed - - output.response[0]['name'] != None - - output.response | length >= 1 - -- name: Query all the resources in the resource group - azure_rm_resource_facts: - resource_group: "{{ resource_group }}" - resource_type: resources - register: output -- name: Assert value was returned - assert: - that: - - not output.changed - - output.response | length >= 1 - -- name: Create storage account that requires LRO polling - azure_rm_resource: - polling_timeout: 600 - polling_interval: 60 - api_version: '2018-07-01' - resource_group: "{{ resource_group }}" - provider: Storage - resource_type: storageAccounts - resource_name: "{{ storageaccountname }}" - body: - sku: - name: Standard_GRS - kind: Storage - location: eastus - register: output - -- name: Assert that storage was successfully created - assert: - that: "output['response']['name'] == '{{ storageaccountname }}'" - - -- name: Try to storage keys -- special case when subresource part has no name - azure_rm_resource: - resource_group: "{{ resource_group }}" - provider: storage - resource_type: storageAccounts - resource_name: "{{ storageaccountname }}" - subresource: - - type: listkeys - api_version: '2018-03-01-preview' - method: POST - register: keys - -- name: Assert that key was returned - assert: - that: keys['response']['keys'][0]['value'] | length > 0 - -- name: Delete storage - without API version - azure_rm_resource: - polling_timeout: 600 - polling_interval: 60 - method: DELETE - resource_group: "{{ resource_group }}" - provider: Storage - resource_type: storageAccounts - resource_name: "{{ storageaccountname }}" diff --git a/test/sanity/ignore.txt b/test/sanity/ignore.txt index d6efc8af07740a..425a5216939495 100644 --- a/test/sanity/ignore.txt +++ b/test/sanity/ignore.txt @@ -325,10 +325,6 @@ test/lib/ansible_test/_data/requirements/sanity.ps1 pslint:PSCustomUseLiteralPat test/lib/ansible_test/_data/sanity/pylint/plugins/string_format.py use-compat-six test/lib/ansible_test/_data/setup/ConfigureRemotingForAnsible.ps1 pslint:PSCustomUseLiteralPath test/lib/ansible_test/_data/setup/windows-httptester.ps1 pslint:PSCustomUseLiteralPath -test/support/integration/plugins/module_utils/azure_rm_common.py future-import-boilerplate -test/support/integration/plugins/module_utils/azure_rm_common.py metaclass-boilerplate -test/support/integration/plugins/module_utils/azure_rm_common_rest.py future-import-boilerplate -test/support/integration/plugins/module_utils/azure_rm_common_rest.py metaclass-boilerplate test/support/integration/plugins/module_utils/cloud.py future-import-boilerplate test/support/integration/plugins/module_utils/cloud.py metaclass-boilerplate test/support/integration/plugins/module_utils/compat/ipaddress.py future-import-boilerplate diff --git a/test/support/integration/plugins/module_utils/azure_rm_common.py b/test/support/integration/plugins/module_utils/azure_rm_common.py deleted file mode 100644 index a7b55e972e5004..00000000000000 --- a/test/support/integration/plugins/module_utils/azure_rm_common.py +++ /dev/null @@ -1,1473 +0,0 @@ -# Copyright (c) 2016 Matt Davis, -# Chris Houseknecht, -# -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -import os -import re -import types -import copy -import inspect -import traceback -import json - -from os.path import expanduser - -from ansible.module_utils.basic import AnsibleModule, missing_required_lib -try: - from ansible.module_utils.ansible_release import __version__ as ANSIBLE_VERSION -except Exception: - ANSIBLE_VERSION = 'unknown' -from ansible.module_utils.six.moves import configparser -import ansible.module_utils.six.moves.urllib.parse as urlparse - -AZURE_COMMON_ARGS = dict( - auth_source=dict( - type='str', - choices=['auto', 'cli', 'env', 'credential_file', 'msi'] - ), - profile=dict(type='str'), - subscription_id=dict(type='str'), - client_id=dict(type='str', no_log=True), - secret=dict(type='str', no_log=True), - tenant=dict(type='str', no_log=True), - ad_user=dict(type='str', no_log=True), - password=dict(type='str', no_log=True), - cloud_environment=dict(type='str', default='AzureCloud'), - cert_validation_mode=dict(type='str', choices=['validate', 'ignore']), - api_profile=dict(type='str', default='latest'), - adfs_authority_url=dict(type='str', default=None) -) - -AZURE_CREDENTIAL_ENV_MAPPING = dict( - profile='AZURE_PROFILE', - subscription_id='AZURE_SUBSCRIPTION_ID', - client_id='AZURE_CLIENT_ID', - secret='AZURE_SECRET', - tenant='AZURE_TENANT', - ad_user='AZURE_AD_USER', - password='AZURE_PASSWORD', - cloud_environment='AZURE_CLOUD_ENVIRONMENT', - cert_validation_mode='AZURE_CERT_VALIDATION_MODE', - adfs_authority_url='AZURE_ADFS_AUTHORITY_URL' -) - - -class SDKProfile(object): # pylint: disable=too-few-public-methods - - def __init__(self, default_api_version, profile=None): - """Constructor. - - :param str default_api_version: Default API version if not overridden by a profile. Nullable. - :param profile: A dict operation group name to API version. - :type profile: dict[str, str] - """ - self.profile = profile if profile is not None else {} - self.profile[None] = default_api_version - - @property - def default_api_version(self): - return self.profile[None] - - -# FUTURE: this should come from the SDK or an external location. -# For now, we have to copy from azure-cli -AZURE_API_PROFILES = { - 'latest': { - 'ContainerInstanceManagementClient': '2018-02-01-preview', - 'ComputeManagementClient': dict( - default_api_version='2018-10-01', - resource_skus='2018-10-01', - disks='2018-06-01', - snapshots='2018-10-01', - virtual_machine_run_commands='2018-10-01' - ), - 'NetworkManagementClient': '2018-08-01', - 'ResourceManagementClient': '2017-05-10', - 'StorageManagementClient': '2017-10-01', - 'WebSiteManagementClient': '2018-02-01', - 'PostgreSQLManagementClient': '2017-12-01', - 'MySQLManagementClient': '2017-12-01', - 'MariaDBManagementClient': '2019-03-01', - 'ManagementLockClient': '2016-09-01' - }, - '2019-03-01-hybrid': { - 'StorageManagementClient': '2017-10-01', - 'NetworkManagementClient': '2017-10-01', - 'ComputeManagementClient': SDKProfile('2017-12-01', { - 'resource_skus': '2017-09-01', - 'disks': '2017-03-30', - 'snapshots': '2017-03-30' - }), - 'ManagementLinkClient': '2016-09-01', - 'ManagementLockClient': '2016-09-01', - 'PolicyClient': '2016-12-01', - 'ResourceManagementClient': '2018-05-01', - 'SubscriptionClient': '2016-06-01', - 'DnsManagementClient': '2016-04-01', - 'KeyVaultManagementClient': '2016-10-01', - 'AuthorizationManagementClient': SDKProfile('2015-07-01', { - 'classic_administrators': '2015-06-01', - 'policy_assignments': '2016-12-01', - 'policy_definitions': '2016-12-01' - }), - 'KeyVaultClient': '2016-10-01', - 'azure.multiapi.storage': '2017-11-09', - 'azure.multiapi.cosmosdb': '2017-04-17' - }, - '2018-03-01-hybrid': { - 'StorageManagementClient': '2016-01-01', - 'NetworkManagementClient': '2017-10-01', - 'ComputeManagementClient': SDKProfile('2017-03-30'), - 'ManagementLinkClient': '2016-09-01', - 'ManagementLockClient': '2016-09-01', - 'PolicyClient': '2016-12-01', - 'ResourceManagementClient': '2018-02-01', - 'SubscriptionClient': '2016-06-01', - 'DnsManagementClient': '2016-04-01', - 'KeyVaultManagementClient': '2016-10-01', - 'AuthorizationManagementClient': SDKProfile('2015-07-01', { - 'classic_administrators': '2015-06-01' - }), - 'KeyVaultClient': '2016-10-01', - 'azure.multiapi.storage': '2017-04-17', - 'azure.multiapi.cosmosdb': '2017-04-17' - }, - '2017-03-09-profile': { - 'StorageManagementClient': '2016-01-01', - 'NetworkManagementClient': '2015-06-15', - 'ComputeManagementClient': SDKProfile('2016-03-30'), - 'ManagementLinkClient': '2016-09-01', - 'ManagementLockClient': '2015-01-01', - 'PolicyClient': '2015-10-01-preview', - 'ResourceManagementClient': '2016-02-01', - 'SubscriptionClient': '2016-06-01', - 'DnsManagementClient': '2016-04-01', - 'KeyVaultManagementClient': '2016-10-01', - 'AuthorizationManagementClient': SDKProfile('2015-07-01', { - 'classic_administrators': '2015-06-01' - }), - 'KeyVaultClient': '2016-10-01', - 'azure.multiapi.storage': '2015-04-05' - } -} - -AZURE_TAG_ARGS = dict( - tags=dict(type='dict'), - append_tags=dict(type='bool', default=True), -) - -AZURE_COMMON_REQUIRED_IF = [ - ('log_mode', 'file', ['log_path']) -] - -ANSIBLE_USER_AGENT = 'Ansible/{0}'.format(ANSIBLE_VERSION) -CLOUDSHELL_USER_AGENT_KEY = 'AZURE_HTTP_USER_AGENT' -VSCODEEXT_USER_AGENT_KEY = 'VSCODEEXT_USER_AGENT' - -CIDR_PATTERN = re.compile(r"(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1" - r"[0-9]{2}|2[0-4][0-9]|25[0-5])(/([0-9]|[1-2][0-9]|3[0-2]))") - -AZURE_SUCCESS_STATE = "Succeeded" -AZURE_FAILED_STATE = "Failed" - -HAS_AZURE = True -HAS_AZURE_EXC = None -HAS_AZURE_CLI_CORE = True -HAS_AZURE_CLI_CORE_EXC = None - -HAS_MSRESTAZURE = True -HAS_MSRESTAZURE_EXC = None - -try: - import importlib -except ImportError: - # This passes the sanity import test, but does not provide a user friendly error message. - # Doing so would require catching Exception for all imports of Azure dependencies in modules and module_utils. - importlib = None - -try: - from packaging.version import Version - HAS_PACKAGING_VERSION = True - HAS_PACKAGING_VERSION_EXC = None -except ImportError: - Version = None - HAS_PACKAGING_VERSION = False - HAS_PACKAGING_VERSION_EXC = traceback.format_exc() - -# NB: packaging issue sometimes cause msrestazure not to be installed, check it separately -try: - from msrest.serialization import Serializer -except ImportError: - HAS_MSRESTAZURE_EXC = traceback.format_exc() - HAS_MSRESTAZURE = False - -try: - from enum import Enum - from msrestazure.azure_active_directory import AADTokenCredentials - from msrestazure.azure_exceptions import CloudError - from msrestazure.azure_active_directory import MSIAuthentication - from msrestazure.tools import parse_resource_id, resource_id, is_valid_resource_id - from msrestazure import azure_cloud - from azure.common.credentials import ServicePrincipalCredentials, UserPassCredentials - from azure.mgmt.monitor.version import VERSION as monitor_client_version - from azure.mgmt.network.version import VERSION as network_client_version - from azure.mgmt.storage.version import VERSION as storage_client_version - from azure.mgmt.compute.version import VERSION as compute_client_version - from azure.mgmt.resource.version import VERSION as resource_client_version - from azure.mgmt.dns.version import VERSION as dns_client_version - from azure.mgmt.web.version import VERSION as web_client_version - from azure.mgmt.network import NetworkManagementClient - from azure.mgmt.resource.resources import ResourceManagementClient - from azure.mgmt.resource.subscriptions import SubscriptionClient - from azure.mgmt.storage import StorageManagementClient - from azure.mgmt.compute import ComputeManagementClient - from azure.mgmt.dns import DnsManagementClient - from azure.mgmt.monitor import MonitorManagementClient - 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 azure.storage.blob import PageBlobService, BlockBlobService - from adal.authentication_context import AuthenticationContext - from azure.mgmt.sql import SqlManagementClient - from azure.mgmt.servicebus import ServiceBusManagementClient - import azure.mgmt.servicebus.models as ServicebusModel - from azure.mgmt.rdbms.postgresql import PostgreSQLManagementClient - from azure.mgmt.rdbms.mysql import MySQLManagementClient - from azure.mgmt.rdbms.mariadb import MariaDBManagementClient - from azure.mgmt.containerregistry import ContainerRegistryManagementClient - from azure.mgmt.containerinstance import ContainerInstanceManagementClient - from azure.mgmt.loganalytics import LogAnalyticsManagementClient - import azure.mgmt.loganalytics.models as LogAnalyticsModels - from azure.mgmt.automation import AutomationClient - import azure.mgmt.automation.models as AutomationModel - from azure.mgmt.iothub import IotHubClient - from azure.mgmt.iothub import models as IoTHubModels - from msrest.service_client import ServiceClient - from msrestazure import AzureConfiguration - from msrest.authentication import Authentication - from azure.mgmt.resource.locks import ManagementLockClient -except ImportError as exc: - Authentication = object - HAS_AZURE_EXC = traceback.format_exc() - HAS_AZURE = False - -from base64 import b64encode, b64decode -from hashlib import sha256 -from hmac import HMAC -from time import time - -try: - from urllib import (urlencode, quote_plus) -except ImportError: - from urllib.parse import (urlencode, quote_plus) - -try: - from azure.cli.core.util import CLIError - from azure.common.credentials import get_azure_cli_credentials, get_cli_profile - from azure.common.cloud import get_cli_active_cloud -except ImportError: - HAS_AZURE_CLI_CORE = False - HAS_AZURE_CLI_CORE_EXC = None - CLIError = Exception - - -def azure_id_to_dict(id): - pieces = re.sub(r'^\/', '', id).split('/') - result = {} - index = 0 - while index < len(pieces) - 1: - result[pieces[index]] = pieces[index + 1] - index += 1 - return result - - -def format_resource_id(val, subscription_id, namespace, types, resource_group): - return resource_id(name=val, - resource_group=resource_group, - namespace=namespace, - type=types, - subscription=subscription_id) if not is_valid_resource_id(val) else val - - -def normalize_location_name(name): - return name.replace(' ', '').lower() - - -# FUTURE: either get this from the requirements file (if we can be sure it's always available at runtime) -# or generate the requirements files from this so we only have one source of truth to maintain... -AZURE_PKG_VERSIONS = { - 'StorageManagementClient': { - 'package_name': 'storage', - 'expected_version': '3.1.0' - }, - 'ComputeManagementClient': { - 'package_name': 'compute', - 'expected_version': '4.4.0' - }, - 'ContainerInstanceManagementClient': { - 'package_name': 'containerinstance', - 'expected_version': '0.4.0' - }, - 'NetworkManagementClient': { - 'package_name': 'network', - 'expected_version': '2.3.0' - }, - 'ResourceManagementClient': { - 'package_name': 'resource', - 'expected_version': '2.1.0' - }, - 'DnsManagementClient': { - 'package_name': 'dns', - 'expected_version': '2.1.0' - }, - 'WebSiteManagementClient': { - 'package_name': 'web', - 'expected_version': '0.41.0' - }, - 'TrafficManagerManagementClient': { - 'package_name': 'trafficmanager', - 'expected_version': '0.50.0' - }, -} if HAS_AZURE else {} - - -AZURE_MIN_RELEASE = '2.0.0' - - -class AzureRMModuleBase(object): - def __init__(self, derived_arg_spec, bypass_checks=False, no_log=False, - mutually_exclusive=None, required_together=None, - required_one_of=None, add_file_common_args=False, supports_check_mode=False, - required_if=None, supports_tags=True, facts_module=False, skip_exec=False): - - merged_arg_spec = dict() - merged_arg_spec.update(AZURE_COMMON_ARGS) - if supports_tags: - merged_arg_spec.update(AZURE_TAG_ARGS) - - if derived_arg_spec: - merged_arg_spec.update(derived_arg_spec) - - merged_required_if = list(AZURE_COMMON_REQUIRED_IF) - if required_if: - merged_required_if += required_if - - self.module = AnsibleModule(argument_spec=merged_arg_spec, - bypass_checks=bypass_checks, - no_log=no_log, - mutually_exclusive=mutually_exclusive, - required_together=required_together, - required_one_of=required_one_of, - add_file_common_args=add_file_common_args, - supports_check_mode=supports_check_mode, - required_if=merged_required_if) - - if not HAS_PACKAGING_VERSION: - self.fail(msg=missing_required_lib('packaging'), - exception=HAS_PACKAGING_VERSION_EXC) - - if not HAS_MSRESTAZURE: - self.fail(msg=missing_required_lib('msrestazure'), - exception=HAS_MSRESTAZURE_EXC) - - if not HAS_AZURE: - self.fail(msg=missing_required_lib('ansible[azure] (azure >= {0})'.format(AZURE_MIN_RELEASE)), - exception=HAS_AZURE_EXC) - - self._network_client = None - self._storage_client = None - self._resource_client = None - self._compute_client = None - self._dns_client = None - self._web_client = None - self._marketplace_client = None - self._sql_client = None - self._mysql_client = None - self._mariadb_client = None - self._postgresql_client = None - self._containerregistry_client = None - self._containerinstance_client = None - self._containerservice_client = None - self._managedcluster_client = None - self._traffic_manager_management_client = None - self._monitor_client = None - self._resource = None - self._log_analytics_client = None - self._servicebus_client = None - self._automation_client = None - self._IoThub_client = None - self._lock_client = None - - self.check_mode = self.module.check_mode - self.api_profile = self.module.params.get('api_profile') - self.facts_module = facts_module - # self.debug = self.module.params.get('debug') - - # delegate auth to AzureRMAuth class (shared with all plugin types) - self.azure_auth = AzureRMAuth(fail_impl=self.fail, **self.module.params) - - # common parameter validation - if self.module.params.get('tags'): - self.validate_tags(self.module.params['tags']) - - if not skip_exec: - res = self.exec_module(**self.module.params) - self.module.exit_json(**res) - - def check_client_version(self, client_type): - # Ensure Azure modules are at least 2.0.0rc5. - package_version = AZURE_PKG_VERSIONS.get(client_type.__name__, None) - if package_version is not None: - client_name = package_version.get('package_name') - try: - client_module = importlib.import_module(client_type.__module__) - client_version = client_module.VERSION - except (RuntimeError, AttributeError): - # can't get at the module version for some reason, just fail silently... - return - expected_version = package_version.get('expected_version') - if Version(client_version) < Version(expected_version): - self.fail("Installed azure-mgmt-{0} client version is {1}. The minimum supported version is {2}. Try " - "`pip install ansible[azure]`".format(client_name, client_version, expected_version)) - if Version(client_version) != Version(expected_version): - self.module.warn("Installed azure-mgmt-{0} client version is {1}. The expected version is {2}. Try " - "`pip install ansible[azure]`".format(client_name, client_version, expected_version)) - - def exec_module(self, **kwargs): - self.fail("Error: {0} failed to implement exec_module method.".format(self.__class__.__name__)) - - def fail(self, msg, **kwargs): - ''' - Shortcut for calling module.fail() - - :param msg: Error message text. - :param kwargs: Any key=value pairs - :return: None - ''' - self.module.fail_json(msg=msg, **kwargs) - - def deprecate(self, msg, version=None, collection_name=None): - self.module.deprecate(msg, version, collection_name=collection_name) - - def log(self, msg, pretty_print=False): - if pretty_print: - self.module.debug(json.dumps(msg, indent=4, sort_keys=True)) - else: - self.module.debug(msg) - - def validate_tags(self, tags): - ''' - Check if tags dictionary contains string:string pairs. - - :param tags: dictionary of string:string pairs - :return: None - ''' - if not self.facts_module: - if not isinstance(tags, dict): - self.fail("Tags must be a dictionary of string:string values.") - for key, value in tags.items(): - if not isinstance(value, str): - self.fail("Tags values must be strings. Found {0}:{1}".format(str(key), str(value))) - - def update_tags(self, tags): - ''' - Call from the module to update metadata tags. Returns tuple - with bool indicating if there was a change and dict of new - tags to assign to the object. - - :param tags: metadata tags from the object - :return: bool, dict - ''' - tags = tags or dict() - new_tags = copy.copy(tags) if isinstance(tags, dict) else dict() - param_tags = self.module.params.get('tags') if isinstance(self.module.params.get('tags'), dict) else dict() - append_tags = self.module.params.get('append_tags') if self.module.params.get('append_tags') is not None else True - changed = False - # check add or update - for key, value in param_tags.items(): - if not new_tags.get(key) or new_tags[key] != value: - changed = True - new_tags[key] = value - # check remove - if not append_tags: - for key, value in tags.items(): - if not param_tags.get(key): - new_tags.pop(key) - changed = True - return changed, new_tags - - def has_tags(self, obj_tags, tag_list): - ''' - Used in fact modules to compare object tags to list of parameter tags. Return true if list of parameter tags - exists in object tags. - - :param obj_tags: dictionary of tags from an Azure object. - :param tag_list: list of tag keys or tag key:value pairs - :return: bool - ''' - - if not obj_tags and tag_list: - return False - - if not tag_list: - return True - - matches = 0 - result = False - for tag in tag_list: - tag_key = tag - tag_value = None - if ':' in tag: - tag_key, tag_value = tag.split(':') - if tag_value and obj_tags.get(tag_key) == tag_value: - matches += 1 - elif not tag_value and obj_tags.get(tag_key): - matches += 1 - if matches == len(tag_list): - result = True - return result - - def get_resource_group(self, resource_group): - ''' - Fetch a resource group. - - :param resource_group: name of a resource group - :return: resource group object - ''' - try: - return self.rm_client.resource_groups.get(resource_group) - except CloudError as cloud_error: - self.fail("Error retrieving resource group {0} - {1}".format(resource_group, cloud_error.message)) - except Exception as exc: - self.fail("Error retrieving resource group {0} - {1}".format(resource_group, str(exc))) - - def parse_resource_to_dict(self, resource): - ''' - Return a dict of the give resource, which contains name and resource group. - - :param resource: It can be a resource name, id or a dict contains name and resource group. - ''' - resource_dict = parse_resource_id(resource) if not isinstance(resource, dict) else resource - resource_dict['resource_group'] = resource_dict.get('resource_group', self.resource_group) - resource_dict['subscription_id'] = resource_dict.get('subscription_id', self.subscription_id) - return resource_dict - - def serialize_obj(self, obj, class_name, enum_modules=None): - ''' - Return a JSON representation of an Azure object. - - :param obj: Azure object - :param class_name: Name of the object's class - :param enum_modules: List of module names to build enum dependencies from. - :return: serialized result - ''' - enum_modules = [] if enum_modules is None else enum_modules - - dependencies = dict() - if enum_modules: - for module_name in enum_modules: - mod = importlib.import_module(module_name) - for mod_class_name, mod_class_obj in inspect.getmembers(mod, predicate=inspect.isclass): - dependencies[mod_class_name] = mod_class_obj - self.log("dependencies: ") - self.log(str(dependencies)) - serializer = Serializer(classes=dependencies) - return serializer.body(obj, class_name, keep_readonly=True) - - def get_poller_result(self, poller, wait=5): - ''' - Consistent method of waiting on and retrieving results from Azure's long poller - - :param poller Azure poller object - :return object resulting from the original request - ''' - try: - delay = wait - while not poller.done(): - self.log("Waiting for {0} sec".format(delay)) - poller.wait(timeout=delay) - return poller.result() - except Exception as exc: - self.log(str(exc)) - raise - - def check_provisioning_state(self, azure_object, requested_state='present'): - ''' - Check an Azure object's provisioning state. If something did not complete the provisioning - process, then we cannot operate on it. - - :param azure_object An object such as a subnet, storageaccount, etc. Must have provisioning_state - and name attributes. - :return None - ''' - - if hasattr(azure_object, 'properties') and hasattr(azure_object.properties, 'provisioning_state') and \ - hasattr(azure_object, 'name'): - # resource group object fits this model - if isinstance(azure_object.properties.provisioning_state, Enum): - if azure_object.properties.provisioning_state.value != AZURE_SUCCESS_STATE and \ - requested_state != 'absent': - self.fail("Error {0} has a provisioning state of {1}. Expecting state to be {2}.".format( - azure_object.name, azure_object.properties.provisioning_state, AZURE_SUCCESS_STATE)) - return - if azure_object.properties.provisioning_state != AZURE_SUCCESS_STATE and \ - requested_state != 'absent': - self.fail("Error {0} has a provisioning state of {1}. Expecting state to be {2}.".format( - azure_object.name, azure_object.properties.provisioning_state, AZURE_SUCCESS_STATE)) - return - - if hasattr(azure_object, 'provisioning_state') or not hasattr(azure_object, 'name'): - if isinstance(azure_object.provisioning_state, Enum): - if azure_object.provisioning_state.value != AZURE_SUCCESS_STATE and requested_state != 'absent': - self.fail("Error {0} has a provisioning state of {1}. Expecting state to be {2}.".format( - azure_object.name, azure_object.provisioning_state, AZURE_SUCCESS_STATE)) - return - if azure_object.provisioning_state != AZURE_SUCCESS_STATE and requested_state != 'absent': - self.fail("Error {0} has a provisioning state of {1}. Expecting state to be {2}.".format( - azure_object.name, azure_object.provisioning_state, AZURE_SUCCESS_STATE)) - - def get_blob_client(self, resource_group_name, storage_account_name, storage_blob_type='block'): - keys = dict() - try: - # Get keys from the storage account - self.log('Getting keys') - account_keys = self.storage_client.storage_accounts.list_keys(resource_group_name, storage_account_name) - except Exception as exc: - self.fail("Error getting keys for account {0} - {1}".format(storage_account_name, str(exc))) - - try: - self.log('Create blob service') - if storage_blob_type == 'page': - return PageBlobService(endpoint_suffix=self._cloud_environment.suffixes.storage_endpoint, - account_name=storage_account_name, - account_key=account_keys.keys[0].value) - elif storage_blob_type == 'block': - return BlockBlobService(endpoint_suffix=self._cloud_environment.suffixes.storage_endpoint, - account_name=storage_account_name, - account_key=account_keys.keys[0].value) - else: - raise Exception("Invalid storage blob type defined.") - except Exception as exc: - self.fail("Error creating blob service client for storage account {0} - {1}".format(storage_account_name, - str(exc))) - - def create_default_pip(self, resource_group, location, public_ip_name, allocation_method='Dynamic', sku=None): - ''' - Create a default public IP address to associate with a network interface. - If a PIP address matching exists, return it. Otherwise, create one. - - :param resource_group: name of an existing resource group - :param location: a valid azure location - :param public_ip_name: base name to assign the public IP address - :param allocation_method: one of 'Static' or 'Dynamic' - :param sku: sku - :return: PIP object - ''' - pip = None - - self.log("Starting create_default_pip {0}".format(public_ip_name)) - self.log("Check to see if public IP {0} exists".format(public_ip_name)) - try: - pip = self.network_client.public_ip_addresses.get(resource_group, public_ip_name) - except CloudError: - pass - - if pip: - self.log("Public ip {0} found.".format(public_ip_name)) - self.check_provisioning_state(pip) - return pip - - params = self.network_models.PublicIPAddress( - location=location, - public_ip_allocation_method=allocation_method, - sku=sku - ) - self.log('Creating default public IP {0}'.format(public_ip_name)) - try: - poller = self.network_client.public_ip_addresses.create_or_update(resource_group, public_ip_name, params) - except Exception as exc: - self.fail("Error creating {0} - {1}".format(public_ip_name, str(exc))) - - return self.get_poller_result(poller) - - def create_default_securitygroup(self, resource_group, location, security_group_name, os_type, open_ports): - ''' - Create a default security group to associate with a network interface. If a security group matching - exists, return it. Otherwise, create one. - - :param resource_group: Resource group name - :param location: azure location name - :param security_group_name: base name to use for the security group - :param os_type: one of 'Windows' or 'Linux'. Determins any default rules added to the security group. - :param ssh_port: for os_type 'Linux' port used in rule allowing SSH access. - :param rdp_port: for os_type 'Windows' port used in rule allowing RDP access. - :return: security_group object - ''' - group = None - - self.log("Create security group {0}".format(security_group_name)) - self.log("Check to see if security group {0} exists".format(security_group_name)) - try: - group = self.network_client.network_security_groups.get(resource_group, security_group_name) - except CloudError: - pass - - if group: - self.log("Security group {0} found.".format(security_group_name)) - self.check_provisioning_state(group) - return group - - parameters = self.network_models.NetworkSecurityGroup() - parameters.location = location - - if not open_ports: - # Open default ports based on OS type - if os_type == 'Linux': - # add an inbound SSH rule - parameters.security_rules = [ - self.network_models.SecurityRule(protocol='Tcp', - source_address_prefix='*', - destination_address_prefix='*', - access='Allow', - direction='Inbound', - description='Allow SSH Access', - source_port_range='*', - destination_port_range='22', - priority=100, - name='SSH') - ] - parameters.location = location - else: - # for windows add inbound RDP and WinRM rules - parameters.security_rules = [ - self.network_models.SecurityRule(protocol='Tcp', - source_address_prefix='*', - destination_address_prefix='*', - access='Allow', - direction='Inbound', - description='Allow RDP port 3389', - source_port_range='*', - destination_port_range='3389', - priority=100, - name='RDP01'), - self.network_models.SecurityRule(protocol='Tcp', - source_address_prefix='*', - destination_address_prefix='*', - access='Allow', - direction='Inbound', - description='Allow WinRM HTTPS port 5986', - source_port_range='*', - destination_port_range='5986', - priority=101, - name='WinRM01'), - ] - else: - # Open custom ports - parameters.security_rules = [] - priority = 100 - for port in open_ports: - priority += 1 - rule_name = "Rule_{0}".format(priority) - parameters.security_rules.append( - self.network_models.SecurityRule(protocol='Tcp', - source_address_prefix='*', - destination_address_prefix='*', - access='Allow', - direction='Inbound', - source_port_range='*', - destination_port_range=str(port), - priority=priority, - name=rule_name) - ) - - self.log('Creating default security group {0}'.format(security_group_name)) - try: - poller = self.network_client.network_security_groups.create_or_update(resource_group, - security_group_name, - parameters) - except Exception as exc: - self.fail("Error creating default security rule {0} - {1}".format(security_group_name, str(exc))) - - return self.get_poller_result(poller) - - @staticmethod - def _validation_ignore_callback(session, global_config, local_config, **kwargs): - session.verify = False - - def get_api_profile(self, client_type_name, api_profile_name): - profile_all_clients = AZURE_API_PROFILES.get(api_profile_name) - - if not profile_all_clients: - raise KeyError("unknown Azure API profile: {0}".format(api_profile_name)) - - profile_raw = profile_all_clients.get(client_type_name, None) - - if not profile_raw: - self.module.warn("Azure API profile {0} does not define an entry for {1}".format(api_profile_name, client_type_name)) - - if isinstance(profile_raw, dict): - if not profile_raw.get('default_api_version'): - raise KeyError("Azure API profile {0} does not define 'default_api_version'".format(api_profile_name)) - return profile_raw - - # wrap basic strings in a dict that just defines the default - return dict(default_api_version=profile_raw) - - def get_mgmt_svc_client(self, client_type, base_url=None, api_version=None): - self.log('Getting management service client {0}'.format(client_type.__name__)) - self.check_client_version(client_type) - - client_argspec = inspect.getargspec(client_type.__init__) - - if not base_url: - # most things are resource_manager, don't make everyone specify - base_url = self.azure_auth._cloud_environment.endpoints.resource_manager - - client_kwargs = dict(credentials=self.azure_auth.azure_credentials, subscription_id=self.azure_auth.subscription_id, base_url=base_url) - - api_profile_dict = {} - - if self.api_profile: - api_profile_dict = self.get_api_profile(client_type.__name__, self.api_profile) - - # unversioned clients won't accept profile; only send it if necessary - # clients without a version specified in the profile will use the default - if api_profile_dict and 'profile' in client_argspec.args: - client_kwargs['profile'] = api_profile_dict - - # If the client doesn't accept api_version, it's unversioned. - # If it does, favor explicitly-specified api_version, fall back to api_profile - if 'api_version' in client_argspec.args: - profile_default_version = api_profile_dict.get('default_api_version', None) - if api_version or profile_default_version: - client_kwargs['api_version'] = api_version or profile_default_version - if 'profile' in client_kwargs: - # remove profile; only pass API version if specified - client_kwargs.pop('profile') - - client = client_type(**client_kwargs) - - # FUTURE: remove this once everything exposes models directly (eg, containerinstance) - try: - getattr(client, "models") - except AttributeError: - def _ansible_get_models(self, *arg, **kwarg): - return self._ansible_models - - setattr(client, '_ansible_models', importlib.import_module(client_type.__module__).models) - client.models = types.MethodType(_ansible_get_models, client) - - client.config = self.add_user_agent(client.config) - - if self.azure_auth._cert_validation_mode == 'ignore': - client.config.session_configuration_callback = self._validation_ignore_callback - - return client - - def add_user_agent(self, config): - # Add user agent for Ansible - config.add_user_agent(ANSIBLE_USER_AGENT) - # Add user agent when running from Cloud Shell - if CLOUDSHELL_USER_AGENT_KEY in os.environ: - config.add_user_agent(os.environ[CLOUDSHELL_USER_AGENT_KEY]) - # Add user agent when running from VSCode extension - if VSCODEEXT_USER_AGENT_KEY in os.environ: - config.add_user_agent(os.environ[VSCODEEXT_USER_AGENT_KEY]) - return config - - def generate_sas_token(self, **kwags): - base_url = kwags.get('base_url', None) - expiry = kwags.get('expiry', time() + 3600) - key = kwags.get('key', None) - policy = kwags.get('policy', None) - url = quote_plus(base_url) - ttl = int(expiry) - sign_key = '{0}\n{1}'.format(url, ttl) - signature = b64encode(HMAC(b64decode(key), sign_key.encode('utf-8'), sha256).digest()) - result = { - 'sr': url, - 'sig': signature, - 'se': str(ttl), - } - if policy: - result['skn'] = policy - return 'SharedAccessSignature ' + urlencode(result) - - def get_data_svc_client(self, **kwags): - url = kwags.get('base_url', None) - config = AzureConfiguration(base_url='https://{0}'.format(url)) - config.credentials = AzureSASAuthentication(token=self.generate_sas_token(**kwags)) - config = self.add_user_agent(config) - return ServiceClient(creds=config.credentials, config=config) - - # passthru methods to AzureAuth instance for backcompat - @property - def credentials(self): - return self.azure_auth.credentials - - @property - def _cloud_environment(self): - return self.azure_auth._cloud_environment - - @property - def subscription_id(self): - return self.azure_auth.subscription_id - - @property - def storage_client(self): - self.log('Getting storage client...') - if not self._storage_client: - self._storage_client = self.get_mgmt_svc_client(StorageManagementClient, - base_url=self._cloud_environment.endpoints.resource_manager, - api_version='2018-07-01') - return self._storage_client - - @property - def storage_models(self): - return StorageManagementClient.models("2018-07-01") - - @property - def network_client(self): - self.log('Getting network client') - if not self._network_client: - self._network_client = self.get_mgmt_svc_client(NetworkManagementClient, - base_url=self._cloud_environment.endpoints.resource_manager, - api_version='2019-06-01') - return self._network_client - - @property - def network_models(self): - self.log("Getting network models...") - return NetworkManagementClient.models("2018-08-01") - - @property - def rm_client(self): - self.log('Getting resource manager client') - if not self._resource_client: - self._resource_client = self.get_mgmt_svc_client(ResourceManagementClient, - base_url=self._cloud_environment.endpoints.resource_manager, - api_version='2017-05-10') - return self._resource_client - - @property - def rm_models(self): - self.log("Getting resource manager models") - return ResourceManagementClient.models("2017-05-10") - - @property - def compute_client(self): - self.log('Getting compute client') - if not self._compute_client: - self._compute_client = self.get_mgmt_svc_client(ComputeManagementClient, - base_url=self._cloud_environment.endpoints.resource_manager, - api_version='2019-07-01') - return self._compute_client - - @property - def compute_models(self): - self.log("Getting compute models") - return ComputeManagementClient.models("2019-07-01") - - @property - def dns_client(self): - self.log('Getting dns client') - if not self._dns_client: - self._dns_client = self.get_mgmt_svc_client(DnsManagementClient, - base_url=self._cloud_environment.endpoints.resource_manager, - api_version='2018-05-01') - return self._dns_client - - @property - def dns_models(self): - self.log("Getting dns models...") - return DnsManagementClient.models('2018-05-01') - - @property - def web_client(self): - self.log('Getting web client') - if not self._web_client: - self._web_client = self.get_mgmt_svc_client(WebSiteManagementClient, - base_url=self._cloud_environment.endpoints.resource_manager, - api_version='2018-02-01') - return self._web_client - - @property - def containerservice_client(self): - self.log('Getting container service client') - if not self._containerservice_client: - self._containerservice_client = self.get_mgmt_svc_client(ContainerServiceClient, - base_url=self._cloud_environment.endpoints.resource_manager, - api_version='2017-07-01') - return self._containerservice_client - - @property - def managedcluster_models(self): - self.log("Getting container service models") - return ContainerServiceClient.models('2018-03-31') - - @property - def managedcluster_client(self): - self.log('Getting container service client') - if not self._managedcluster_client: - self._managedcluster_client = self.get_mgmt_svc_client(ContainerServiceClient, - base_url=self._cloud_environment.endpoints.resource_manager, - api_version='2018-03-31') - return self._managedcluster_client - - @property - def sql_client(self): - self.log('Getting SQL client') - if not self._sql_client: - self._sql_client = self.get_mgmt_svc_client(SqlManagementClient, - base_url=self._cloud_environment.endpoints.resource_manager) - return self._sql_client - - @property - def postgresql_client(self): - self.log('Getting PostgreSQL client') - if not self._postgresql_client: - self._postgresql_client = self.get_mgmt_svc_client(PostgreSQLManagementClient, - base_url=self._cloud_environment.endpoints.resource_manager) - return self._postgresql_client - - @property - def mysql_client(self): - self.log('Getting MySQL client') - if not self._mysql_client: - self._mysql_client = self.get_mgmt_svc_client(MySQLManagementClient, - base_url=self._cloud_environment.endpoints.resource_manager) - return self._mysql_client - - @property - def mariadb_client(self): - self.log('Getting MariaDB client') - if not self._mariadb_client: - self._mariadb_client = self.get_mgmt_svc_client(MariaDBManagementClient, - base_url=self._cloud_environment.endpoints.resource_manager) - return self._mariadb_client - - @property - def sql_client(self): - self.log('Getting SQL client') - if not self._sql_client: - self._sql_client = self.get_mgmt_svc_client(SqlManagementClient, - base_url=self._cloud_environment.endpoints.resource_manager) - return self._sql_client - - @property - def containerregistry_client(self): - self.log('Getting container registry mgmt client') - if not self._containerregistry_client: - self._containerregistry_client = self.get_mgmt_svc_client(ContainerRegistryManagementClient, - base_url=self._cloud_environment.endpoints.resource_manager, - api_version='2017-10-01') - - return self._containerregistry_client - - @property - def containerinstance_client(self): - self.log('Getting container instance mgmt client') - if not self._containerinstance_client: - self._containerinstance_client = self.get_mgmt_svc_client(ContainerInstanceManagementClient, - base_url=self._cloud_environment.endpoints.resource_manager, - api_version='2018-06-01') - - return self._containerinstance_client - - @property - def marketplace_client(self): - self.log('Getting marketplace agreement client') - if not self._marketplace_client: - 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 - - @property - def monitor_client(self): - self.log('Getting monitor client') - if not self._monitor_client: - self._monitor_client = self.get_mgmt_svc_client(MonitorManagementClient, - base_url=self._cloud_environment.endpoints.resource_manager) - return self._monitor_client - - @property - def log_analytics_client(self): - self.log('Getting log analytics client') - if not self._log_analytics_client: - self._log_analytics_client = self.get_mgmt_svc_client(LogAnalyticsManagementClient, - base_url=self._cloud_environment.endpoints.resource_manager) - return self._log_analytics_client - - @property - def log_analytics_models(self): - self.log('Getting log analytics models') - return LogAnalyticsModels - - @property - def servicebus_client(self): - self.log('Getting servicebus client') - if not self._servicebus_client: - self._servicebus_client = self.get_mgmt_svc_client(ServiceBusManagementClient, - base_url=self._cloud_environment.endpoints.resource_manager) - return self._servicebus_client - - @property - def servicebus_models(self): - return ServicebusModel - - @property - def automation_client(self): - self.log('Getting automation client') - if not self._automation_client: - self._automation_client = self.get_mgmt_svc_client(AutomationClient, - base_url=self._cloud_environment.endpoints.resource_manager) - return self._automation_client - - @property - def automation_models(self): - return AutomationModel - - @property - def IoThub_client(self): - self.log('Getting iothub client') - if not self._IoThub_client: - self._IoThub_client = self.get_mgmt_svc_client(IotHubClient, - base_url=self._cloud_environment.endpoints.resource_manager) - return self._IoThub_client - - @property - def IoThub_models(self): - return IoTHubModels - - @property - def automation_client(self): - self.log('Getting automation client') - if not self._automation_client: - self._automation_client = self.get_mgmt_svc_client(AutomationClient, - base_url=self._cloud_environment.endpoints.resource_manager) - return self._automation_client - - @property - def automation_models(self): - return AutomationModel - - @property - def lock_client(self): - self.log('Getting lock client') - if not self._lock_client: - self._lock_client = self.get_mgmt_svc_client(ManagementLockClient, - base_url=self._cloud_environment.endpoints.resource_manager, - api_version='2016-09-01') - return self._lock_client - - @property - def lock_models(self): - self.log("Getting lock models") - return ManagementLockClient.models('2016-09-01') - - -class AzureSASAuthentication(Authentication): - """Simple SAS Authentication. - An implementation of Authentication in - https://github.com/Azure/msrest-for-python/blob/0732bc90bdb290e5f58c675ffdd7dbfa9acefc93/msrest/authentication.py - - :param str token: SAS token - """ - def __init__(self, token): - self.token = token - - def signed_session(self): - session = super(AzureSASAuthentication, self).signed_session() - session.headers['Authorization'] = self.token - return session - - -class AzureRMAuthException(Exception): - pass - - -class AzureRMAuth(object): - def __init__(self, auth_source='auto', profile=None, subscription_id=None, client_id=None, secret=None, - tenant=None, ad_user=None, password=None, cloud_environment='AzureCloud', cert_validation_mode='validate', - api_profile='latest', adfs_authority_url=None, fail_impl=None, **kwargs): - - if fail_impl: - self._fail_impl = fail_impl - else: - self._fail_impl = self._default_fail_impl - - self._cloud_environment = None - self._adfs_authority_url = None - - # authenticate - self.credentials = self._get_credentials( - dict(auth_source=auth_source, profile=profile, subscription_id=subscription_id, client_id=client_id, secret=secret, - tenant=tenant, ad_user=ad_user, password=password, cloud_environment=cloud_environment, - cert_validation_mode=cert_validation_mode, api_profile=api_profile, adfs_authority_url=adfs_authority_url)) - - if not self.credentials: - if HAS_AZURE_CLI_CORE: - self.fail("Failed to get credentials. Either pass as parameters, set environment variables, " - "define a profile in ~/.azure/credentials, or log in with Azure CLI (`az login`).") - else: - self.fail("Failed to get credentials. Either pass as parameters, set environment variables, " - "define a profile in ~/.azure/credentials, or install Azure CLI and log in (`az login`).") - - # cert validation mode precedence: module-arg, credential profile, env, "validate" - self._cert_validation_mode = cert_validation_mode or self.credentials.get('cert_validation_mode') or \ - os.environ.get('AZURE_CERT_VALIDATION_MODE') or 'validate' - - if self._cert_validation_mode not in ['validate', 'ignore']: - self.fail('invalid cert_validation_mode: {0}'.format(self._cert_validation_mode)) - - # if cloud_environment specified, look up/build Cloud object - raw_cloud_env = self.credentials.get('cloud_environment') - if self.credentials.get('credentials') is not None and raw_cloud_env is not None: - self._cloud_environment = raw_cloud_env - elif not raw_cloud_env: - self._cloud_environment = azure_cloud.AZURE_PUBLIC_CLOUD # SDK default - else: - # try to look up "well-known" values via the name attribute on azure_cloud members - all_clouds = [x[1] for x in inspect.getmembers(azure_cloud) if isinstance(x[1], azure_cloud.Cloud)] - matched_clouds = [x for x in all_clouds if x.name == raw_cloud_env] - if len(matched_clouds) == 1: - self._cloud_environment = matched_clouds[0] - elif len(matched_clouds) > 1: - self.fail("Azure SDK failure: more than one cloud matched for cloud_environment name '{0}'".format(raw_cloud_env)) - else: - if not urlparse.urlparse(raw_cloud_env).scheme: - self.fail("cloud_environment must be an endpoint discovery URL or one of {0}".format([x.name for x in all_clouds])) - try: - self._cloud_environment = azure_cloud.get_cloud_from_metadata_endpoint(raw_cloud_env) - except Exception as e: - self.fail("cloud_environment {0} could not be resolved: {1}".format(raw_cloud_env, e.message), exception=traceback.format_exc()) - - if self.credentials.get('subscription_id', None) is None and self.credentials.get('credentials') is None: - self.fail("Credentials did not include a subscription_id value.") - self.log("setting subscription_id") - self.subscription_id = self.credentials['subscription_id'] - - # get authentication authority - # for adfs, user could pass in authority or not. - # for others, use default authority from cloud environment - if self.credentials.get('adfs_authority_url') is None: - self._adfs_authority_url = self._cloud_environment.endpoints.active_directory - else: - self._adfs_authority_url = self.credentials.get('adfs_authority_url') - - # get resource from cloud environment - self._resource = self._cloud_environment.endpoints.active_directory_resource_id - - if self.credentials.get('credentials') is not None: - # AzureCLI credentials - self.azure_credentials = self.credentials['credentials'] - elif self.credentials.get('client_id') is not None and \ - self.credentials.get('secret') is not None and \ - self.credentials.get('tenant') is not None: - self.azure_credentials = ServicePrincipalCredentials(client_id=self.credentials['client_id'], - secret=self.credentials['secret'], - tenant=self.credentials['tenant'], - cloud_environment=self._cloud_environment, - verify=self._cert_validation_mode == 'validate') - - elif self.credentials.get('ad_user') is not None and \ - self.credentials.get('password') is not None and \ - self.credentials.get('client_id') is not None and \ - self.credentials.get('tenant') is not None: - - self.azure_credentials = self.acquire_token_with_username_password( - self._adfs_authority_url, - self._resource, - self.credentials['ad_user'], - self.credentials['password'], - self.credentials['client_id'], - self.credentials['tenant']) - - elif self.credentials.get('ad_user') is not None and self.credentials.get('password') is not None: - tenant = self.credentials.get('tenant') - if not tenant: - tenant = 'common' # SDK default - - self.azure_credentials = UserPassCredentials(self.credentials['ad_user'], - self.credentials['password'], - tenant=tenant, - cloud_environment=self._cloud_environment, - verify=self._cert_validation_mode == 'validate') - else: - self.fail("Failed to authenticate with provided credentials. Some attributes were missing. " - "Credentials must include client_id, secret and tenant or ad_user and password, or " - "ad_user, password, client_id, tenant and adfs_authority_url(optional) for ADFS authentication, or " - "be logged in using AzureCLI.") - - def fail(self, msg, exception=None, **kwargs): - self._fail_impl(msg) - - def _default_fail_impl(self, msg, exception=None, **kwargs): - raise AzureRMAuthException(msg) - - def _get_profile(self, profile="default"): - path = expanduser("~/.azure/credentials") - try: - config = configparser.ConfigParser() - config.read(path) - except Exception as exc: - self.fail("Failed to access {0}. Check that the file exists and you have read " - "access. {1}".format(path, str(exc))) - credentials = dict() - for key in AZURE_CREDENTIAL_ENV_MAPPING: - try: - credentials[key] = config.get(profile, key, raw=True) - except Exception: - pass - - if credentials.get('subscription_id'): - return credentials - - return None - - def _get_msi_credentials(self, subscription_id_param=None, **kwargs): - client_id = kwargs.get('client_id', None) - credentials = MSIAuthentication(client_id=client_id) - subscription_id = subscription_id_param or os.environ.get(AZURE_CREDENTIAL_ENV_MAPPING['subscription_id'], None) - if not subscription_id: - try: - # use the first subscription of the MSI - subscription_client = SubscriptionClient(credentials) - subscription = next(subscription_client.subscriptions.list()) - subscription_id = str(subscription.subscription_id) - except Exception as exc: - self.fail("Failed to get MSI token: {0}. " - "Please check whether your machine enabled MSI or grant access to any subscription.".format(str(exc))) - return { - 'credentials': credentials, - 'subscription_id': subscription_id - } - - def _get_azure_cli_credentials(self): - credentials, subscription_id = get_azure_cli_credentials() - cloud_environment = get_cli_active_cloud() - - cli_credentials = { - 'credentials': credentials, - 'subscription_id': subscription_id, - 'cloud_environment': cloud_environment - } - return cli_credentials - - def _get_env_credentials(self): - env_credentials = dict() - for attribute, env_variable in AZURE_CREDENTIAL_ENV_MAPPING.items(): - env_credentials[attribute] = os.environ.get(env_variable, None) - - if env_credentials['profile']: - credentials = self._get_profile(env_credentials['profile']) - return credentials - - if env_credentials.get('subscription_id') is not None: - return env_credentials - - return None - - # TODO: use explicit kwargs instead of intermediate dict - def _get_credentials(self, params): - # Get authentication credentials. - self.log('Getting credentials') - - arg_credentials = dict() - for attribute, env_variable in AZURE_CREDENTIAL_ENV_MAPPING.items(): - arg_credentials[attribute] = params.get(attribute, None) - - auth_source = params.get('auth_source', None) - if not auth_source: - auth_source = os.environ.get('ANSIBLE_AZURE_AUTH_SOURCE', 'auto') - - if auth_source == 'msi': - self.log('Retrieving credenitals from MSI') - return self._get_msi_credentials(arg_credentials['subscription_id'], client_id=params.get('client_id', None)) - - if auth_source == 'cli': - if not HAS_AZURE_CLI_CORE: - self.fail(msg=missing_required_lib('azure-cli', reason='for `cli` auth_source'), - exception=HAS_AZURE_CLI_CORE_EXC) - try: - self.log('Retrieving credentials from Azure CLI profile') - cli_credentials = self._get_azure_cli_credentials() - return cli_credentials - except CLIError as err: - self.fail("Azure CLI profile cannot be loaded - {0}".format(err)) - - if auth_source == 'env': - self.log('Retrieving credentials from environment') - env_credentials = self._get_env_credentials() - return env_credentials - - if auth_source == 'credential_file': - self.log("Retrieving credentials from credential file") - profile = params.get('profile') or 'default' - default_credentials = self._get_profile(profile) - return default_credentials - - # auto, precedence: module parameters -> environment variables -> default profile in ~/.azure/credentials - # try module params - if arg_credentials['profile'] is not None: - self.log('Retrieving credentials with profile parameter.') - credentials = self._get_profile(arg_credentials['profile']) - return credentials - - if arg_credentials['subscription_id']: - self.log('Received credentials from parameters.') - return arg_credentials - - # try environment - env_credentials = self._get_env_credentials() - if env_credentials: - self.log('Received credentials from env.') - return env_credentials - - # try default profile from ~./azure/credentials - default_credentials = self._get_profile() - if default_credentials: - self.log('Retrieved default profile credentials from ~/.azure/credentials.') - return default_credentials - - try: - if HAS_AZURE_CLI_CORE: - self.log('Retrieving credentials from AzureCLI profile') - cli_credentials = self._get_azure_cli_credentials() - return cli_credentials - except CLIError as ce: - self.log('Error getting AzureCLI profile credentials - {0}'.format(ce)) - - return None - - def acquire_token_with_username_password(self, authority, resource, username, password, client_id, tenant): - authority_uri = authority - - if tenant is not None: - authority_uri = authority + '/' + tenant - - context = AuthenticationContext(authority_uri) - token_response = context.acquire_token_with_username_password(resource, username, password, client_id) - - return AADTokenCredentials(token_response) - - def log(self, msg, pretty_print=False): - pass - # Use only during module development - # if self.debug: - # log_file = open('azure_rm.log', 'a') - # if pretty_print: - # log_file.write(json.dumps(msg, indent=4, sort_keys=True)) - # else: - # log_file.write(msg + u'\n') diff --git a/test/support/integration/plugins/module_utils/azure_rm_common_rest.py b/test/support/integration/plugins/module_utils/azure_rm_common_rest.py deleted file mode 100644 index 4fd7eaa3b454d8..00000000000000 --- a/test/support/integration/plugins/module_utils/azure_rm_common_rest.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) 2018 Zim Kalinowski, -# -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from ansible.module_utils.ansible_release import __version__ as ANSIBLE_VERSION - -try: - from msrestazure.azure_exceptions import CloudError - from msrestazure.azure_configuration import AzureConfiguration - from msrest.service_client import ServiceClient - from msrest.pipeline import ClientRawResponse - from msrest.polling import LROPoller - from msrestazure.polling.arm_polling import ARMPolling - import uuid - import json -except ImportError: - # This is handled in azure_rm_common - AzureConfiguration = object - -ANSIBLE_USER_AGENT = 'Ansible/{0}'.format(ANSIBLE_VERSION) - - -class GenericRestClientConfiguration(AzureConfiguration): - - def __init__(self, credentials, subscription_id, base_url=None): - - if credentials is None: - raise ValueError("Parameter 'credentials' must not be None.") - if subscription_id is None: - raise ValueError("Parameter 'subscription_id' must not be None.") - if not base_url: - base_url = 'https://management.azure.com' - - super(GenericRestClientConfiguration, self).__init__(base_url) - - self.add_user_agent(ANSIBLE_USER_AGENT) - - self.credentials = credentials - self.subscription_id = subscription_id - - -class GenericRestClient(object): - - def __init__(self, credentials, subscription_id, base_url=None): - self.config = GenericRestClientConfiguration(credentials, subscription_id, base_url) - self._client = ServiceClient(self.config.credentials, self.config) - self.models = None - - def query(self, url, method, query_parameters, header_parameters, body, expected_status_codes, polling_timeout, polling_interval): - # Construct and send request - operation_config = {} - - request = None - - if header_parameters is None: - header_parameters = {} - - header_parameters['x-ms-client-request-id'] = str(uuid.uuid1()) - - if method == 'GET': - request = self._client.get(url, query_parameters) - elif method == 'PUT': - request = self._client.put(url, query_parameters) - elif method == 'POST': - request = self._client.post(url, query_parameters) - elif method == 'HEAD': - request = self._client.head(url, query_parameters) - elif method == 'PATCH': - request = self._client.patch(url, query_parameters) - elif method == 'DELETE': - request = self._client.delete(url, query_parameters) - elif method == 'MERGE': - request = self._client.merge(url, query_parameters) - - response = self._client.send(request, header_parameters, body, **operation_config) - - if response.status_code not in expected_status_codes: - exp = CloudError(response) - exp.request_id = response.headers.get('x-ms-request-id') - raise exp - elif response.status_code == 202 and polling_timeout > 0: - def get_long_running_output(response): - return response - poller = LROPoller(self._client, - ClientRawResponse(None, response), - get_long_running_output, - ARMPolling(polling_interval, **operation_config)) - response = self.get_poller_result(poller, polling_timeout) - - return response - - def get_poller_result(self, poller, timeout): - try: - poller.wait(timeout=timeout) - return poller.result() - except Exception as exc: - raise diff --git a/test/support/integration/plugins/modules/_azure_rm_mariadbconfiguration_facts.py b/test/support/integration/plugins/modules/_azure_rm_mariadbconfiguration_facts.py deleted file mode 120000 index f9993bfba72647..00000000000000 --- a/test/support/integration/plugins/modules/_azure_rm_mariadbconfiguration_facts.py +++ /dev/null @@ -1 +0,0 @@ -azure_rm_mariadbconfiguration_info.py \ No newline at end of file diff --git a/test/support/integration/plugins/modules/_azure_rm_mariadbdatabase_facts.py b/test/support/integration/plugins/modules/_azure_rm_mariadbdatabase_facts.py deleted file mode 120000 index b8293e64dfc7d8..00000000000000 --- a/test/support/integration/plugins/modules/_azure_rm_mariadbdatabase_facts.py +++ /dev/null @@ -1 +0,0 @@ -azure_rm_mariadbdatabase_info.py \ No newline at end of file diff --git a/test/support/integration/plugins/modules/_azure_rm_mariadbfirewallrule_facts.py b/test/support/integration/plugins/modules/_azure_rm_mariadbfirewallrule_facts.py deleted file mode 120000 index 4311a0c1cc2a36..00000000000000 --- a/test/support/integration/plugins/modules/_azure_rm_mariadbfirewallrule_facts.py +++ /dev/null @@ -1 +0,0 @@ -azure_rm_mariadbfirewallrule_info.py \ No newline at end of file diff --git a/test/support/integration/plugins/modules/_azure_rm_mariadbserver_facts.py b/test/support/integration/plugins/modules/_azure_rm_mariadbserver_facts.py deleted file mode 120000 index 5f76e0e932ce6c..00000000000000 --- a/test/support/integration/plugins/modules/_azure_rm_mariadbserver_facts.py +++ /dev/null @@ -1 +0,0 @@ -azure_rm_mariadbserver_info.py \ No newline at end of file diff --git a/test/support/integration/plugins/modules/_azure_rm_resource_facts.py b/test/support/integration/plugins/modules/_azure_rm_resource_facts.py deleted file mode 120000 index 710fda10744f3a..00000000000000 --- a/test/support/integration/plugins/modules/_azure_rm_resource_facts.py +++ /dev/null @@ -1 +0,0 @@ -azure_rm_resource_info.py \ No newline at end of file diff --git a/test/support/integration/plugins/modules/_azure_rm_webapp_facts.py b/test/support/integration/plugins/modules/_azure_rm_webapp_facts.py deleted file mode 120000 index ead87c850b0a4a..00000000000000 --- a/test/support/integration/plugins/modules/_azure_rm_webapp_facts.py +++ /dev/null @@ -1 +0,0 @@ -azure_rm_webapp_info.py \ No newline at end of file diff --git a/test/support/integration/plugins/modules/azure_rm_appserviceplan.py b/test/support/integration/plugins/modules/azure_rm_appserviceplan.py deleted file mode 100644 index ee871c352b6d42..00000000000000 --- a/test/support/integration/plugins/modules/azure_rm_appserviceplan.py +++ /dev/null @@ -1,379 +0,0 @@ -#!/usr/bin/python -# -# Copyright (c) 2018 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_appserviceplan -version_added: "2.7" -short_description: Manage App Service Plan -description: - - Create, update and delete instance of App Service Plan. - -options: - resource_group: - description: - - Name of the resource group to which the resource belongs. - required: True - - name: - description: - - Unique name of the app service plan to create or update. - required: True - - location: - description: - - Resource location. If not set, location from the resource group will be used as default. - - sku: - description: - - The pricing tiers, e.g., C(F1), C(D1), C(B1), C(B2), C(B3), C(S1), C(P1), C(P1V2) etc. - - Please see U(https://azure.microsoft.com/en-us/pricing/details/app-service/plans/) for more detail. - - For Linux app service plan, please see U(https://azure.microsoft.com/en-us/pricing/details/app-service/linux/) for more detail. - is_linux: - description: - - Describe whether to host webapp on Linux worker. - type: bool - default: false - - number_of_workers: - description: - - Describe number of workers to be allocated. - - state: - description: - - Assert the state of the app service plan. - - Use C(present) to create or update an app service plan and C(absent) to delete it. - default: present - choices: - - absent - - present - -extends_documentation_fragment: - - azure - - azure_tags - -author: - - Yunge Zhu (@yungezz) - -''' - -EXAMPLES = ''' - - name: Create a windows app service plan - azure_rm_appserviceplan: - resource_group: myResourceGroup - name: myAppPlan - location: eastus - sku: S1 - - - name: Create a linux app service plan - azure_rm_appserviceplan: - resource_group: myResourceGroup - name: myAppPlan - location: eastus - sku: S1 - is_linux: true - number_of_workers: 1 - - - name: update sku of existing windows app service plan - azure_rm_appserviceplan: - resource_group: myResourceGroup - name: myAppPlan - location: eastus - sku: S2 -''' - -RETURN = ''' -azure_appserviceplan: - description: Facts about the current state of the app service plan. - returned: always - type: dict - sample: { - "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/Microsoft.Web/serverfarms/myAppPlan" - } -''' - -import time -from ansible.module_utils.azure_rm_common import AzureRMModuleBase - -try: - from msrestazure.azure_exceptions import CloudError - from msrest.polling import LROPoller - from msrestazure.azure_operation import AzureOperationPoller - from msrest.serialization import Model - from azure.mgmt.web.models import ( - app_service_plan, AppServicePlan, SkuDescription - ) -except ImportError: - # This is handled in azure_rm_common - pass - - -def _normalize_sku(sku): - if sku is None: - return sku - - sku = sku.upper() - if sku == 'FREE': - return 'F1' - elif sku == 'SHARED': - return 'D1' - return sku - - -def get_sku_name(tier): - tier = tier.upper() - if tier == 'F1' or tier == "FREE": - return 'FREE' - elif tier == 'D1' or tier == "SHARED": - return 'SHARED' - elif tier in ['B1', 'B2', 'B3', 'BASIC']: - return 'BASIC' - elif tier in ['S1', 'S2', 'S3']: - return 'STANDARD' - elif tier in ['P1', 'P2', 'P3']: - return 'PREMIUM' - elif tier in ['P1V2', 'P2V2', 'P3V2']: - return 'PREMIUMV2' - else: - return None - - -def appserviceplan_to_dict(plan): - return dict( - id=plan.id, - name=plan.name, - kind=plan.kind, - location=plan.location, - reserved=plan.reserved, - is_linux=plan.reserved, - provisioning_state=plan.provisioning_state, - status=plan.status, - target_worker_count=plan.target_worker_count, - sku=dict( - name=plan.sku.name, - size=plan.sku.size, - tier=plan.sku.tier, - family=plan.sku.family, - capacity=plan.sku.capacity - ), - resource_group=plan.resource_group, - number_of_sites=plan.number_of_sites, - tags=plan.tags if plan.tags else None - ) - - -class AzureRMAppServicePlans(AzureRMModuleBase): - """Configuration class for an Azure RM App Service Plan resource""" - - def __init__(self): - self.module_arg_spec = dict( - resource_group=dict( - type='str', - required=True - ), - name=dict( - type='str', - required=True - ), - location=dict( - type='str' - ), - sku=dict( - type='str' - ), - is_linux=dict( - type='bool', - default=False - ), - number_of_workers=dict( - type='str' - ), - state=dict( - type='str', - default='present', - choices=['present', 'absent'] - ) - ) - - self.resource_group = None - self.name = None - self.location = None - - self.sku = None - self.is_linux = None - self.number_of_workers = 1 - - self.tags = None - - self.results = dict( - changed=False, - ansible_facts=dict(azure_appserviceplan=None) - ) - self.state = None - - super(AzureRMAppServicePlans, self).__init__(derived_arg_spec=self.module_arg_spec, - supports_check_mode=True, - supports_tags=True) - - def exec_module(self, **kwargs): - """Main module execution method""" - - for key in list(self.module_arg_spec.keys()) + ['tags']: - if kwargs[key]: - setattr(self, key, kwargs[key]) - - old_response = None - response = None - to_be_updated = False - - # set location - resource_group = self.get_resource_group(self.resource_group) - if not self.location: - self.location = resource_group.location - - # get app service plan - old_response = self.get_plan() - - # if not existing - if not old_response: - self.log("App Service plan doesn't exist") - - if self.state == "present": - to_be_updated = True - - if not self.sku: - self.fail('Please specify sku in plan when creation') - - else: - # existing app service plan, do update - self.log("App Service Plan already exists") - - if self.state == 'present': - self.log('Result: {0}'.format(old_response)) - - update_tags, newtags = self.update_tags(old_response.get('tags', dict())) - - if update_tags: - to_be_updated = True - self.tags = newtags - - # check if sku changed - if self.sku and _normalize_sku(self.sku) != old_response['sku']['size']: - to_be_updated = True - - # check if number_of_workers changed - if self.number_of_workers and int(self.number_of_workers) != old_response['sku']['capacity']: - to_be_updated = True - - if self.is_linux and self.is_linux != old_response['reserved']: - self.fail("Operation not allowed: cannot update reserved of app service plan.") - - if old_response: - self.results['id'] = old_response['id'] - - if to_be_updated: - self.log('Need to Create/Update app service plan') - self.results['changed'] = True - - if self.check_mode: - return self.results - - response = self.create_or_update_plan() - self.results['id'] = response['id'] - - if self.state == 'absent' and old_response: - self.log("Delete app service plan") - self.results['changed'] = True - - if self.check_mode: - return self.results - - self.delete_plan() - - self.log('App service plan instance deleted') - - return self.results - - def get_plan(self): - ''' - Gets app service plan - :return: deserialized app service plan dictionary - ''' - self.log("Get App Service Plan {0}".format(self.name)) - - try: - response = self.web_client.app_service_plans.get(self.resource_group, self.name) - if response: - self.log("Response : {0}".format(response)) - self.log("App Service Plan : {0} found".format(response.name)) - - return appserviceplan_to_dict(response) - except CloudError as ex: - self.log("Didn't find app service plan {0} in resource group {1}".format(self.name, self.resource_group)) - - return False - - def create_or_update_plan(self): - ''' - Creates app service plan - :return: deserialized app service plan dictionary - ''' - self.log("Create App Service Plan {0}".format(self.name)) - - try: - # normalize sku - sku = _normalize_sku(self.sku) - - sku_def = SkuDescription(tier=get_sku_name( - sku), name=sku, capacity=self.number_of_workers) - plan_def = AppServicePlan( - location=self.location, app_service_plan_name=self.name, sku=sku_def, reserved=self.is_linux, tags=self.tags if self.tags else None) - - response = self.web_client.app_service_plans.create_or_update(self.resource_group, self.name, plan_def) - - if isinstance(response, LROPoller) or isinstance(response, AzureOperationPoller): - response = self.get_poller_result(response) - - self.log("Response : {0}".format(response)) - - return appserviceplan_to_dict(response) - except CloudError as ex: - self.fail("Failed to create app service plan {0} in resource group {1}: {2}".format(self.name, self.resource_group, str(ex))) - - def delete_plan(self): - ''' - Deletes specified App service plan in the specified subscription and resource group. - - :return: True - ''' - self.log("Deleting the App service plan {0}".format(self.name)) - try: - response = self.web_client.app_service_plans.delete(resource_group_name=self.resource_group, - name=self.name) - except CloudError as e: - self.log('Error attempting to delete App service plan.') - self.fail( - "Error deleting the App service plan : {0}".format(str(e))) - - return True - - -def main(): - """Main execution""" - AzureRMAppServicePlans() - - -if __name__ == '__main__': - main() diff --git a/test/support/integration/plugins/modules/azure_rm_functionapp.py b/test/support/integration/plugins/modules/azure_rm_functionapp.py deleted file mode 100644 index 0c372a88dec528..00000000000000 --- a/test/support/integration/plugins/modules/azure_rm_functionapp.py +++ /dev/null @@ -1,421 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2016, Thomas Stringer -# 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_functionapp -version_added: "2.4" -short_description: Manage Azure Function Apps -description: - - Create, update or delete an Azure Function App. -options: - resource_group: - description: - - Name of resource group. - required: true - aliases: - - resource_group_name - name: - description: - - Name of the Azure Function App. - required: true - location: - description: - - Valid Azure location. Defaults to location of the resource group. - plan: - description: - - App service plan. - - It can be name of existing app service plan in same resource group as function app. - - It can be resource id of existing app service plan. - - Resource id. For example /subscriptions//resourceGroups//providers/Microsoft.Web/serverFarms/. - - It can be a dict which contains C(name), C(resource_group). - - C(name). Name of app service plan. - - C(resource_group). Resource group name of app service plan. - version_added: "2.8" - container_settings: - description: Web app container settings. - suboptions: - name: - description: - - Name of container. For example "imagename:tag". - registry_server_url: - description: - - Container registry server url. For example C(mydockerregistry.io). - registry_server_user: - description: - - The container registry server user name. - registry_server_password: - description: - - The container registry server password. - version_added: "2.8" - storage_account: - description: - - Name of the storage account to use. - required: true - aliases: - - storage - - storage_account_name - app_settings: - description: - - Dictionary containing application settings. - state: - description: - - Assert the state of the Function App. Use C(present) to create or update a Function App and C(absent) to delete. - default: present - choices: - - absent - - present - -extends_documentation_fragment: - - azure - - azure_tags - -author: - - Thomas Stringer (@trstringer) -''' - -EXAMPLES = ''' -- name: Create a function app - azure_rm_functionapp: - resource_group: myResourceGroup - name: myFunctionApp - storage_account: myStorageAccount - -- name: Create a function app with app settings - azure_rm_functionapp: - resource_group: myResourceGroup - name: myFunctionApp - storage_account: myStorageAccount - app_settings: - setting1: value1 - setting2: value2 - -- name: Create container based function app - azure_rm_functionapp: - resource_group: myResourceGroup - name: myFunctionApp - storage_account: myStorageAccount - plan: - resource_group: myResourceGroup - name: myAppPlan - container_settings: - name: httpd - registry_server_url: index.docker.io - -- name: Delete a function app - azure_rm_functionapp: - resource_group: myResourceGroup - name: myFunctionApp - state: absent -''' - -RETURN = ''' -state: - description: - - Current state of the Azure Function App. - returned: success - type: dict - example: - id: /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/Microsoft.Web/sites/myFunctionApp - name: myfunctionapp - kind: functionapp - location: East US - type: Microsoft.Web/sites - state: Running - host_names: - - myfunctionapp.azurewebsites.net - repository_site_name: myfunctionapp - usage_state: Normal - enabled: true - enabled_host_names: - - myfunctionapp.azurewebsites.net - - myfunctionapp.scm.azurewebsites.net - availability_state: Normal - host_name_ssl_states: - - name: myfunctionapp.azurewebsites.net - ssl_state: Disabled - host_type: Standard - - name: myfunctionapp.scm.azurewebsites.net - ssl_state: Disabled - host_type: Repository - server_farm_id: /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/Microsoft.Web/serverfarms/EastUSPlan - reserved: false - last_modified_time_utc: 2017-08-22T18:54:01.190Z - scm_site_also_stopped: false - client_affinity_enabled: true - client_cert_enabled: false - host_names_disabled: false - outbound_ip_addresses: ............ - container_size: 1536 - daily_memory_time_quota: 0 - resource_group: myResourceGroup - default_host_name: myfunctionapp.azurewebsites.net -''' # NOQA - -from ansible.module_utils.azure_rm_common import AzureRMModuleBase - -try: - from msrestazure.azure_exceptions import CloudError - from azure.mgmt.web.models import ( - site_config, app_service_plan, Site, SiteConfig, NameValuePair, SiteSourceControl, - AppServicePlan, SkuDescription - ) - from azure.mgmt.resource.resources import ResourceManagementClient - from msrest.polling import LROPoller -except ImportError: - # This is handled in azure_rm_common - pass - -container_settings_spec = dict( - name=dict(type='str', required=True), - registry_server_url=dict(type='str'), - registry_server_user=dict(type='str'), - registry_server_password=dict(type='str', no_log=True) -) - - -class AzureRMFunctionApp(AzureRMModuleBase): - - def __init__(self): - - self.module_arg_spec = dict( - resource_group=dict(type='str', required=True, aliases=['resource_group_name']), - name=dict(type='str', required=True), - state=dict(type='str', default='present', choices=['present', 'absent']), - location=dict(type='str'), - storage_account=dict( - type='str', - aliases=['storage', 'storage_account_name'] - ), - app_settings=dict(type='dict'), - plan=dict( - type='raw' - ), - container_settings=dict( - type='dict', - options=container_settings_spec - ) - ) - - self.results = dict( - changed=False, - state=dict() - ) - - self.resource_group = None - self.name = None - self.state = None - self.location = None - self.storage_account = None - self.app_settings = None - self.plan = None - self.container_settings = None - - required_if = [('state', 'present', ['storage_account'])] - - super(AzureRMFunctionApp, self).__init__( - self.module_arg_spec, - supports_check_mode=True, - required_if=required_if - ) - - def exec_module(self, **kwargs): - - for key in self.module_arg_spec: - setattr(self, key, kwargs[key]) - if self.app_settings is None: - self.app_settings = dict() - - try: - resource_group = self.rm_client.resource_groups.get(self.resource_group) - except CloudError: - self.fail('Unable to retrieve resource group') - - self.location = self.location or resource_group.location - - try: - function_app = self.web_client.web_apps.get( - resource_group_name=self.resource_group, - name=self.name - ) - # Newer SDK versions (0.40.0+) seem to return None if it doesn't exist instead of raising CloudError - exists = function_app is not None - except CloudError as exc: - exists = False - - if self.state == 'absent': - if exists: - if self.check_mode: - self.results['changed'] = True - return self.results - try: - self.web_client.web_apps.delete( - resource_group_name=self.resource_group, - name=self.name - ) - self.results['changed'] = True - except CloudError as exc: - self.fail('Failure while deleting web app: {0}'.format(exc)) - else: - self.results['changed'] = False - else: - kind = 'functionapp' - linux_fx_version = None - if self.container_settings and self.container_settings.get('name'): - kind = 'functionapp,linux,container' - linux_fx_version = 'DOCKER|' - if self.container_settings.get('registry_server_url'): - self.app_settings['DOCKER_REGISTRY_SERVER_URL'] = 'https://' + self.container_settings['registry_server_url'] - linux_fx_version += self.container_settings['registry_server_url'] + '/' - linux_fx_version += self.container_settings['name'] - if self.container_settings.get('registry_server_user'): - self.app_settings['DOCKER_REGISTRY_SERVER_USERNAME'] = self.container_settings.get('registry_server_user') - - if self.container_settings.get('registry_server_password'): - self.app_settings['DOCKER_REGISTRY_SERVER_PASSWORD'] = self.container_settings.get('registry_server_password') - - if not self.plan and function_app: - self.plan = function_app.server_farm_id - - if not exists: - function_app = Site( - location=self.location, - kind=kind, - site_config=SiteConfig( - app_settings=self.aggregated_app_settings(), - scm_type='LocalGit' - ) - ) - self.results['changed'] = True - else: - self.results['changed'], function_app = self.update(function_app) - - # get app service plan - if self.plan: - if isinstance(self.plan, dict): - self.plan = "/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Web/serverfarms/{2}".format( - self.subscription_id, - self.plan.get('resource_group', self.resource_group), - self.plan.get('name') - ) - function_app.server_farm_id = self.plan - - # set linux fx version - if linux_fx_version: - function_app.site_config.linux_fx_version = linux_fx_version - - if self.check_mode: - self.results['state'] = function_app.as_dict() - elif self.results['changed']: - try: - new_function_app = self.web_client.web_apps.create_or_update( - resource_group_name=self.resource_group, - name=self.name, - site_envelope=function_app - ).result() - self.results['state'] = new_function_app.as_dict() - except CloudError as exc: - self.fail('Error creating or updating web app: {0}'.format(exc)) - - return self.results - - def update(self, source_function_app): - """Update the Site object if there are any changes""" - - source_app_settings = self.web_client.web_apps.list_application_settings( - resource_group_name=self.resource_group, - name=self.name - ) - - changed, target_app_settings = self.update_app_settings(source_app_settings.properties) - - source_function_app.site_config = SiteConfig( - app_settings=target_app_settings, - scm_type='LocalGit' - ) - - return changed, source_function_app - - def update_app_settings(self, source_app_settings): - """Update app settings""" - - target_app_settings = self.aggregated_app_settings() - target_app_settings_dict = dict([(i.name, i.value) for i in target_app_settings]) - return target_app_settings_dict != source_app_settings, target_app_settings - - def necessary_functionapp_settings(self): - """Construct the necessary app settings required for an Azure Function App""" - - function_app_settings = [] - - if self.container_settings is None: - for key in ['AzureWebJobsStorage', 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING', 'AzureWebJobsDashboard']: - function_app_settings.append(NameValuePair(name=key, value=self.storage_connection_string)) - function_app_settings.append(NameValuePair(name='FUNCTIONS_EXTENSION_VERSION', value='~1')) - function_app_settings.append(NameValuePair(name='WEBSITE_NODE_DEFAULT_VERSION', value='6.5.0')) - function_app_settings.append(NameValuePair(name='WEBSITE_CONTENTSHARE', value=self.name)) - else: - function_app_settings.append(NameValuePair(name='FUNCTIONS_EXTENSION_VERSION', value='~2')) - function_app_settings.append(NameValuePair(name='WEBSITES_ENABLE_APP_SERVICE_STORAGE', value=False)) - function_app_settings.append(NameValuePair(name='AzureWebJobsStorage', value=self.storage_connection_string)) - - return function_app_settings - - def aggregated_app_settings(self): - """Combine both system and user app settings""" - - function_app_settings = self.necessary_functionapp_settings() - for app_setting_key in self.app_settings: - found_setting = None - for s in function_app_settings: - if s.name == app_setting_key: - found_setting = s - break - if found_setting: - found_setting.value = self.app_settings[app_setting_key] - else: - function_app_settings.append(NameValuePair( - name=app_setting_key, - value=self.app_settings[app_setting_key] - )) - return function_app_settings - - @property - def storage_connection_string(self): - """Construct the storage account connection string""" - - return 'DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1}'.format( - self.storage_account, - self.storage_key - ) - - @property - def storage_key(self): - """Retrieve the storage account key""" - - return self.storage_client.storage_accounts.list_keys( - resource_group_name=self.resource_group, - account_name=self.storage_account - ).keys[0].value - - -def main(): - """Main function execution""" - - AzureRMFunctionApp() - - -if __name__ == '__main__': - main() diff --git a/test/support/integration/plugins/modules/azure_rm_functionapp_info.py b/test/support/integration/plugins/modules/azure_rm_functionapp_info.py deleted file mode 100644 index 40672f952d4af3..00000000000000 --- a/test/support/integration/plugins/modules/azure_rm_functionapp_info.py +++ /dev/null @@ -1,207 +0,0 @@ -#!/usr/bin/python -# -# Copyright (c) 2016 Thomas Stringer, - -# 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_functionapp_info -version_added: "2.9" -short_description: Get Azure Function App facts -description: - - Get facts for one Azure Function App or all Function Apps within a resource group. -options: - name: - description: - - Only show results for a specific Function App. - resource_group: - description: - - Limit results to a resource group. Required when filtering by name. - aliases: - - resource_group_name - tags: - description: - - Limit results by providing a list of tags. Format tags as 'key' or 'key:value'. - -extends_documentation_fragment: - - azure - -author: - - Thomas Stringer (@trstringer) -''' - -EXAMPLES = ''' - - name: Get facts for one Function App - azure_rm_functionapp_info: - resource_group: myResourceGroup - name: myfunctionapp - - - name: Get facts for all Function Apps in a resource group - azure_rm_functionapp_info: - resource_group: myResourceGroup - - - name: Get facts for all Function Apps by tags - azure_rm_functionapp_info: - tags: - - testing -''' - -RETURN = ''' -azure_functionapps: - description: - - List of Azure Function Apps dicts. - returned: always - type: list - example: - id: /subscriptions/.../resourceGroups/ansible-rg/providers/Microsoft.Web/sites/myfunctionapp - name: myfunctionapp - kind: functionapp - location: East US - type: Microsoft.Web/sites - state: Running - host_names: - - myfunctionapp.azurewebsites.net - repository_site_name: myfunctionapp - usage_state: Normal - enabled: true - enabled_host_names: - - myfunctionapp.azurewebsites.net - - myfunctionapp.scm.azurewebsites.net - availability_state: Normal - host_name_ssl_states: - - name: myfunctionapp.azurewebsites.net - ssl_state: Disabled - host_type: Standard - - name: myfunctionapp.scm.azurewebsites.net - ssl_state: Disabled - host_type: Repository - server_farm_id: /subscriptions/.../resourceGroups/ansible-rg/providers/Microsoft.Web/serverfarms/EastUSPlan - reserved: false - last_modified_time_utc: 2017-08-22T18:54:01.190Z - scm_site_also_stopped: false - client_affinity_enabled: true - client_cert_enabled: false - host_names_disabled: false - outbound_ip_addresses: ............ - container_size: 1536 - daily_memory_time_quota: 0 - resource_group: myResourceGroup - default_host_name: myfunctionapp.azurewebsites.net -''' - -try: - from msrestazure.azure_exceptions import CloudError -except Exception: - # This is handled in azure_rm_common - pass - -from ansible.module_utils.azure_rm_common import AzureRMModuleBase - - -class AzureRMFunctionAppInfo(AzureRMModuleBase): - def __init__(self): - - self.module_arg_spec = dict( - name=dict(type='str'), - resource_group=dict(type='str', aliases=['resource_group_name']), - tags=dict(type='list'), - ) - - self.results = dict( - changed=False, - ansible_info=dict(azure_functionapps=[]) - ) - - self.name = None - self.resource_group = None - self.tags = None - - super(AzureRMFunctionAppInfo, self).__init__( - self.module_arg_spec, - supports_tags=False, - facts_module=True - ) - - def exec_module(self, **kwargs): - - is_old_facts = self.module._name == 'azure_rm_functionapp_facts' - if is_old_facts: - self.module.deprecate("The 'azure_rm_functionapp_facts' module has been renamed to 'azure_rm_functionapp_info'", - version='2.13', collection_name='ansible.builtin') - - for key in self.module_arg_spec: - 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['ansible_info']['azure_functionapps'] = self.get_functionapp() - elif self.resource_group: - self.results['ansible_info']['azure_functionapps'] = self.list_resource_group() - else: - self.results['ansible_info']['azure_functionapps'] = self.list_all() - - return self.results - - def get_functionapp(self): - self.log('Get properties for Function App {0}'.format(self.name)) - function_app = None - result = [] - - try: - function_app = self.web_client.web_apps.get( - self.resource_group, - self.name - ) - except CloudError: - pass - - if function_app and self.has_tags(function_app.tags, self.tags): - result = function_app.as_dict() - - return [result] - - def list_resource_group(self): - self.log('List items') - try: - response = self.web_client.web_apps.list_by_resource_group(self.resource_group) - except Exception as exc: - self.fail("Error listing for resource group {0} - {1}".format(self.resource_group, str(exc))) - - results = [] - for item in response: - if self.has_tags(item.tags, self.tags): - results.append(item.as_dict()) - return results - - def list_all(self): - self.log('List all items') - try: - response = self.web_client.web_apps.list_by_resource_group(self.resource_group) - 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(item.as_dict()) - return results - - -def main(): - AzureRMFunctionAppInfo() - - -if __name__ == '__main__': - main() diff --git a/test/support/integration/plugins/modules/azure_rm_mariadbconfiguration.py b/test/support/integration/plugins/modules/azure_rm_mariadbconfiguration.py deleted file mode 100644 index 212cf7959d1514..00000000000000 --- a/test/support/integration/plugins/modules/azure_rm_mariadbconfiguration.py +++ /dev/null @@ -1,241 +0,0 @@ -#!/usr/bin/python -# -# Copyright (c) 2019 Zim Kalinowski, (@zikalino) -# Copyright (c) 2019 Matti Ranta, (@techknowlogick) -# -# 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_mariadbconfiguration -version_added: "2.8" -short_description: Manage Configuration instance -description: - - Create, update and delete instance of Configuration. - -options: - resource_group: - description: - - The name of the resource group that contains the resource. - required: True - server_name: - description: - - The name of the server. - required: True - name: - description: - - The name of the server configuration. - required: True - value: - description: - - Value of the configuration. - state: - description: - - Assert the state of the MariaDB configuration. Use C(present) to update setting, or C(absent) to reset to default value. - default: present - choices: - - absent - - present - -extends_documentation_fragment: - - azure - -author: - - Zim Kalinowski (@zikalino) - - Matti Ranta (@techknowlogick) -''' - -EXAMPLES = ''' - - name: Update SQL Server setting - azure_rm_mariadbconfiguration: - resource_group: myResourceGroup - server_name: myServer - name: event_scheduler - value: "ON" -''' - -RETURN = ''' -id: - description: - - Resource ID. - returned: always - type: str - sample: "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/Microsoft.DBforMariaDB/servers/myServer/confi - gurations/event_scheduler" -''' - -import time -from ansible.module_utils.azure_rm_common import AzureRMModuleBase - -try: - from msrestazure.azure_exceptions import CloudError - from msrest.polling import LROPoller - from azure.mgmt.rdbms.mysql import MariaDBManagementClient - from msrest.serialization import Model -except ImportError: - # This is handled in azure_rm_common - pass - - -class Actions: - NoAction, Create, Update, Delete = range(4) - - -class AzureRMMariaDbConfiguration(AzureRMModuleBase): - - def __init__(self): - self.module_arg_spec = dict( - resource_group=dict( - type='str', - required=True - ), - server_name=dict( - type='str', - required=True - ), - name=dict( - type='str', - required=True - ), - value=dict( - type='str' - ), - state=dict( - type='str', - default='present', - choices=['present', 'absent'] - ) - ) - - self.resource_group = None - self.server_name = None - self.name = None - self.value = None - - self.results = dict(changed=False) - self.state = None - self.to_do = Actions.NoAction - - super(AzureRMMariaDbConfiguration, 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()): - if hasattr(self, key): - setattr(self, key, kwargs[key]) - - old_response = None - response = None - - old_response = self.get_configuration() - - if not old_response: - self.log("Configuration instance doesn't exist") - if self.state == 'absent': - self.log("Old instance didn't exist") - else: - self.to_do = Actions.Create - else: - self.log("Configuration instance already exists") - if self.state == 'absent' and old_response['source'] == 'user-override': - self.to_do = Actions.Delete - elif self.state == 'present': - self.log("Need to check if Configuration instance has to be deleted or may be updated") - if self.value != old_response.get('value'): - self.to_do = Actions.Update - - if (self.to_do == Actions.Create) or (self.to_do == Actions.Update): - self.log("Need to Create / Update the Configuration instance") - - if self.check_mode: - self.results['changed'] = True - return self.results - - response = self.create_update_configuration() - - self.results['changed'] = True - self.log("Creation / Update done") - elif self.to_do == Actions.Delete: - self.log("Configuration instance deleted") - self.results['changed'] = True - - if self.check_mode: - return self.results - - self.delete_configuration() - else: - self.log("Configuration instance unchanged") - self.results['changed'] = False - response = old_response - - if response: - self.results["id"] = response["id"] - - return self.results - - def create_update_configuration(self): - self.log("Creating / Updating the Configuration instance {0}".format(self.name)) - - try: - response = self.mariadb_client.configurations.create_or_update(resource_group_name=self.resource_group, - server_name=self.server_name, - configuration_name=self.name, - value=self.value, - source='user-override') - if isinstance(response, LROPoller): - response = self.get_poller_result(response) - - except CloudError as exc: - self.log('Error attempting to create the Configuration instance.') - self.fail("Error creating the Configuration instance: {0}".format(str(exc))) - return response.as_dict() - - def delete_configuration(self): - self.log("Deleting the Configuration instance {0}".format(self.name)) - try: - response = self.mariadb_client.configurations.create_or_update(resource_group_name=self.resource_group, - server_name=self.server_name, - configuration_name=self.name, - source='system-default') - except CloudError as e: - self.log('Error attempting to delete the Configuration instance.') - self.fail("Error deleting the Configuration instance: {0}".format(str(e))) - - return True - - def get_configuration(self): - self.log("Checking if the Configuration instance {0} is present".format(self.name)) - found = False - try: - response = self.mariadb_client.configurations.get(resource_group_name=self.resource_group, - server_name=self.server_name, - configuration_name=self.name) - found = True - self.log("Response : {0}".format(response)) - self.log("Configuration instance : {0} found".format(response.name)) - except CloudError as e: - self.log('Did not find the Configuration instance.') - if found is True: - return response.as_dict() - - return False - - -def main(): - """Main execution""" - AzureRMMariaDbConfiguration() - - -if __name__ == '__main__': - main() diff --git a/test/support/integration/plugins/modules/azure_rm_mariadbconfiguration_info.py b/test/support/integration/plugins/modules/azure_rm_mariadbconfiguration_info.py deleted file mode 100644 index 3faac5eb5adef5..00000000000000 --- a/test/support/integration/plugins/modules/azure_rm_mariadbconfiguration_info.py +++ /dev/null @@ -1,217 +0,0 @@ -#!/usr/bin/python -# -# Copyright (c) 2019 Zim Kalinowski, (@zikalino) -# Copyright (c) 2019 Matti Ranta, (@techknowlogick) -# -# 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_mariadbconfiguration_info -version_added: "2.9" -short_description: Get Azure MariaDB Configuration facts -description: - - Get facts of Azure MariaDB Configuration. - -options: - resource_group: - description: - - The name of the resource group that contains the resource. You can obtain this value from the Azure Resource Manager API or the portal. - required: True - type: str - server_name: - description: - - The name of the server. - required: True - type: str - name: - description: - - Setting name. - type: str - -extends_documentation_fragment: - - azure - -author: - - Zim Kalinowski (@zikalino) - - Matti Ranta (@techknowlogick) - -''' - -EXAMPLES = ''' - - name: Get specific setting of MariaDB Server - azure_rm_mariadbconfiguration_info: - resource_group: myResourceGroup - server_name: testserver - name: deadlock_timeout - - - name: Get all settings of MariaDB Server - azure_rm_mariadbconfiguration_info: - resource_group: myResourceGroup - server_name: server_name -''' - -RETURN = ''' -settings: - description: - - A list of dictionaries containing MariaDB Server settings. - returned: always - type: complex - contains: - id: - description: - - Setting resource ID. - returned: always - type: str - sample: "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/Microsoft.DBforMariaDB/servers/testserver - /configurations/deadlock_timeout" - name: - description: - - Setting name. - returned: always - type: str - sample: deadlock_timeout - value: - description: - - Setting value. - returned: always - type: raw - sample: 1000 - description: - description: - - Description of the configuration. - returned: always - type: str - sample: Deadlock timeout. - source: - description: - - Source of the configuration. - returned: always - type: str - sample: system-default -''' - -from ansible.module_utils.azure_rm_common import AzureRMModuleBase - -try: - from msrestazure.azure_exceptions import CloudError - from msrestazure.azure_operation import AzureOperationPoller - from azure.mgmt.rdbms.mariadb import MariaDBManagementClient - from msrest.serialization import Model -except ImportError: - # This is handled in azure_rm_common - pass - - -class AzureRMMariaDbConfigurationInfo(AzureRMModuleBase): - def __init__(self): - # define user inputs into argument - self.module_arg_spec = dict( - resource_group=dict( - type='str', - required=True - ), - server_name=dict( - type='str', - required=True - ), - name=dict( - type='str' - ) - ) - # store the results of the module operation - self.results = dict(changed=False) - self.mgmt_client = None - self.resource_group = None - self.server_name = None - self.name = None - super(AzureRMMariaDbConfigurationInfo, self).__init__(self.module_arg_spec, supports_tags=False) - - def exec_module(self, **kwargs): - is_old_facts = self.module._name == 'azure_rm_mariadbconfiguration_facts' - if is_old_facts: - self.module.deprecate("The 'azure_rm_mariadbconfiguration_facts' module has been renamed to 'azure_rm_mariadbconfiguration_info'", - version='2.13', collection_name='ansible.builtin') - - for key in self.module_arg_spec: - setattr(self, key, kwargs[key]) - self.mgmt_client = self.get_mgmt_svc_client(MariaDBManagementClient, - base_url=self._cloud_environment.endpoints.resource_manager) - - if self.name is not None: - self.results['settings'] = self.get() - else: - self.results['settings'] = self.list_by_server() - return self.results - - def get(self): - ''' - Gets facts of the specified MariaDB Configuration. - - :return: deserialized MariaDB Configurationinstance state dictionary - ''' - response = None - results = [] - try: - response = self.mgmt_client.configurations.get(resource_group_name=self.resource_group, - server_name=self.server_name, - configuration_name=self.name) - self.log("Response : {0}".format(response)) - except CloudError as e: - self.log('Could not get facts for Configurations.') - - if response is not None: - results.append(self.format_item(response)) - - return results - - def list_by_server(self): - ''' - Gets facts of the specified MariaDB Configuration. - - :return: deserialized MariaDB Configurationinstance state dictionary - ''' - response = None - results = [] - try: - response = self.mgmt_client.configurations.list_by_server(resource_group_name=self.resource_group, - server_name=self.server_name) - self.log("Response : {0}".format(response)) - except CloudError as e: - self.log('Could not get facts for Configurations.') - - if response is not None: - for item in response: - results.append(self.format_item(item)) - - return results - - def format_item(self, item): - d = item.as_dict() - d = { - 'resource_group': self.resource_group, - 'server_name': self.server_name, - 'id': d['id'], - 'name': d['name'], - 'value': d['value'], - 'description': d['description'], - 'source': d['source'] - } - return d - - -def main(): - AzureRMMariaDbConfigurationInfo() - - -if __name__ == '__main__': - main() diff --git a/test/support/integration/plugins/modules/azure_rm_mariadbdatabase.py b/test/support/integration/plugins/modules/azure_rm_mariadbdatabase.py deleted file mode 100644 index 8492b9685465b3..00000000000000 --- a/test/support/integration/plugins/modules/azure_rm_mariadbdatabase.py +++ /dev/null @@ -1,304 +0,0 @@ -#!/usr/bin/python -# -# Copyright (c) 2017 Zim Kalinowski, -# Copyright (c) 2019 Matti Ranta, (@techknowlogick) -# -# 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_mariadbdatabase -version_added: "2.8" -short_description: Manage MariaDB Database instance -description: - - Create, update and delete instance of MariaDB Database. - -options: - resource_group: - description: - - The name of the resource group that contains the resource. You can obtain this value from the Azure Resource Manager API or the portal. - required: True - server_name: - description: - - The name of the server. - required: True - name: - description: - - The name of the database. - required: True - charset: - description: - - The charset of the database. Check MariaDB documentation for possible values. - - This is only set on creation, use I(force_update) to recreate a database if the values don't match. - collation: - description: - - The collation of the database. Check MariaDB documentation for possible values. - - This is only set on creation, use I(force_update) to recreate a database if the values don't match. - force_update: - description: - - When set to C(true), will delete and recreate the existing MariaDB database if any of the properties don't match what is set. - - When set to C(false), no change will occur to the database even if any of the properties do not match. - type: bool - default: 'no' - state: - description: - - Assert the state of the MariaDB Database. Use C(present) to create or update a database and C(absent) to delete it. - default: present - choices: - - absent - - present - -extends_documentation_fragment: - - azure - -author: - - Zim Kalinowski (@zikalino) - - Matti Ranta (@techknowlogick) - -''' - -EXAMPLES = ''' - - name: Create (or update) MariaDB Database - azure_rm_mariadbdatabase: - resource_group: myResourceGroup - server_name: testserver - name: db1 -''' - -RETURN = ''' -id: - description: - - Resource ID. - returned: always - type: str - sample: /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/Microsoft.DBforMariaDB/servers/testserver/databases/db1 -name: - description: - - Resource name. - returned: always - type: str - sample: db1 -''' - -import time -from ansible.module_utils.azure_rm_common import AzureRMModuleBase - -try: - from azure.mgmt.rdbms.mariadb import MariaDBManagementClient - from msrestazure.azure_exceptions import CloudError - from msrest.polling import LROPoller - from msrest.serialization import Model -except ImportError: - # This is handled in azure_rm_common - pass - - -class Actions: - NoAction, Create, Update, Delete = range(4) - - -class AzureRMMariaDbDatabase(AzureRMModuleBase): - """Configuration class for an Azure RM MariaDB Database resource""" - - def __init__(self): - self.module_arg_spec = dict( - resource_group=dict( - type='str', - required=True - ), - server_name=dict( - type='str', - required=True - ), - name=dict( - type='str', - required=True - ), - charset=dict( - type='str' - ), - collation=dict( - type='str' - ), - force_update=dict( - type='bool', - default=False - ), - state=dict( - type='str', - default='present', - choices=['present', 'absent'] - ) - ) - - self.resource_group = None - self.server_name = None - self.name = None - self.force_update = None - self.parameters = dict() - - self.results = dict(changed=False) - self.mgmt_client = None - self.state = None - self.to_do = Actions.NoAction - - super(AzureRMMariaDbDatabase, self).__init__(derived_arg_spec=self.module_arg_spec, - supports_check_mode=True, - supports_tags=False) - - def exec_module(self, **kwargs): - """Main module execution method""" - - for key in list(self.module_arg_spec.keys()): - if hasattr(self, key): - setattr(self, key, kwargs[key]) - elif kwargs[key] is not None: - if key == "charset": - self.parameters["charset"] = kwargs[key] - elif key == "collation": - self.parameters["collation"] = kwargs[key] - - old_response = None - response = None - - self.mgmt_client = self.get_mgmt_svc_client(MariaDBManagementClient, - base_url=self._cloud_environment.endpoints.resource_manager) - - resource_group = self.get_resource_group(self.resource_group) - - old_response = self.get_mariadbdatabase() - - if not old_response: - self.log("MariaDB Database instance doesn't exist") - if self.state == 'absent': - self.log("Old instance didn't exist") - else: - self.to_do = Actions.Create - else: - self.log("MariaDB Database instance already exists") - if self.state == 'absent': - self.to_do = Actions.Delete - elif self.state == 'present': - self.log("Need to check if MariaDB Database instance has to be deleted or may be updated") - if ('collation' in self.parameters) and (self.parameters['collation'] != old_response['collation']): - self.to_do = Actions.Update - if ('charset' in self.parameters) and (self.parameters['charset'] != old_response['charset']): - self.to_do = Actions.Update - if self.to_do == Actions.Update: - if self.force_update: - if not self.check_mode: - self.delete_mariadbdatabase() - else: - self.fail("Database properties cannot be updated without setting 'force_update' option") - self.to_do = Actions.NoAction - - if (self.to_do == Actions.Create) or (self.to_do == Actions.Update): - self.log("Need to Create / Update the MariaDB Database instance") - - if self.check_mode: - self.results['changed'] = True - return self.results - - response = self.create_update_mariadbdatabase() - self.results['changed'] = True - self.log("Creation / Update done") - elif self.to_do == Actions.Delete: - self.log("MariaDB Database instance deleted") - self.results['changed'] = True - - if self.check_mode: - return self.results - - self.delete_mariadbdatabase() - # make sure instance is actually deleted, for some Azure resources, instance is hanging around - # for some time after deletion -- this should be really fixed in Azure - while self.get_mariadbdatabase(): - time.sleep(20) - else: - self.log("MariaDB Database instance unchanged") - self.results['changed'] = False - response = old_response - - if response: - self.results["id"] = response["id"] - self.results["name"] = response["name"] - - return self.results - - def create_update_mariadbdatabase(self): - ''' - Creates or updates MariaDB Database with the specified configuration. - - :return: deserialized MariaDB Database instance state dictionary - ''' - self.log("Creating / Updating the MariaDB Database instance {0}".format(self.name)) - - try: - response = self.mgmt_client.databases.create_or_update(resource_group_name=self.resource_group, - server_name=self.server_name, - database_name=self.name, - parameters=self.parameters) - if isinstance(response, LROPoller): - response = self.get_poller_result(response) - - except CloudError as exc: - self.log('Error attempting to create the MariaDB Database instance.') - self.fail("Error creating the MariaDB Database instance: {0}".format(str(exc))) - return response.as_dict() - - def delete_mariadbdatabase(self): - ''' - Deletes specified MariaDB Database instance in the specified subscription and resource group. - - :return: True - ''' - self.log("Deleting the MariaDB Database instance {0}".format(self.name)) - try: - response = self.mgmt_client.databases.delete(resource_group_name=self.resource_group, - server_name=self.server_name, - database_name=self.name) - except CloudError as e: - self.log('Error attempting to delete the MariaDB Database instance.') - self.fail("Error deleting the MariaDB Database instance: {0}".format(str(e))) - - return True - - def get_mariadbdatabase(self): - ''' - Gets the properties of the specified MariaDB Database. - - :return: deserialized MariaDB Database instance state dictionary - ''' - self.log("Checking if the MariaDB Database instance {0} is present".format(self.name)) - found = False - try: - response = self.mgmt_client.databases.get(resource_group_name=self.resource_group, - server_name=self.server_name, - database_name=self.name) - found = True - self.log("Response : {0}".format(response)) - self.log("MariaDB Database instance : {0} found".format(response.name)) - except CloudError as e: - self.log('Did not find the MariaDB Database instance.') - if found is True: - return response.as_dict() - - return False - - -def main(): - """Main execution""" - AzureRMMariaDbDatabase() - - -if __name__ == '__main__': - main() diff --git a/test/support/integration/plugins/modules/azure_rm_mariadbdatabase_info.py b/test/support/integration/plugins/modules/azure_rm_mariadbdatabase_info.py deleted file mode 100644 index e9c99c1483c16e..00000000000000 --- a/test/support/integration/plugins/modules/azure_rm_mariadbdatabase_info.py +++ /dev/null @@ -1,212 +0,0 @@ -#!/usr/bin/python -# -# Copyright (c) 2017 Zim Kalinowski, -# Copyright (c) 2019 Matti Ranta, (@techknowlogick) -# -# 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_mariadbdatabase_info -version_added: "2.9" -short_description: Get Azure MariaDB Database facts -description: - - Get facts of MariaDB Database. - -options: - resource_group: - description: - - The name of the resource group that contains the resource. You can obtain this value from the Azure Resource Manager API or the portal. - required: True - type: str - server_name: - description: - - The name of the server. - required: True - type: str - name: - description: - - The name of the database. - type: str - -extends_documentation_fragment: - - azure - -author: - - Zim Kalinowski (@zikalino) - - Matti Ranta (@techknowlogick) - -''' - -EXAMPLES = ''' - - name: Get instance of MariaDB Database - azure_rm_mariadbdatabase_info: - resource_group: myResourceGroup - server_name: server_name - name: database_name - - - name: List instances of MariaDB Database - azure_rm_mariadbdatabase_info: - resource_group: myResourceGroup - server_name: server_name -''' - -RETURN = ''' -databases: - description: - - A list of dictionaries containing facts for MariaDB Databases. - returned: always - type: complex - contains: - id: - description: - - Resource ID. - returned: always - type: str - sample: "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/Microsoft.DBforMariaDB/servers/testser - ver/databases/db1" - resource_group: - description: - - Resource group name. - returned: always - type: str - sample: testrg - server_name: - description: - - Server name. - returned: always - type: str - sample: testserver - name: - description: - - Resource name. - returned: always - type: str - sample: db1 - charset: - description: - - The charset of the database. - returned: always - type: str - sample: UTF8 - collation: - description: - - The collation of the database. - returned: always - type: str - sample: English_United States.1252 -''' - -from ansible.module_utils.azure_rm_common import AzureRMModuleBase - -try: - from msrestazure.azure_exceptions import CloudError - from azure.mgmt.rdbms.mariadb import MariaDBManagementClient - from msrest.serialization import Model -except ImportError: - # This is handled in azure_rm_common - pass - - -class AzureRMMariaDbDatabaseInfo(AzureRMModuleBase): - def __init__(self): - # define user inputs into argument - self.module_arg_spec = dict( - resource_group=dict( - type='str', - required=True - ), - server_name=dict( - type='str', - required=True - ), - name=dict( - type='str' - ) - ) - # store the results of the module operation - self.results = dict( - changed=False - ) - self.resource_group = None - self.server_name = None - self.name = None - super(AzureRMMariaDbDatabaseInfo, self).__init__(self.module_arg_spec, supports_tags=False) - - def exec_module(self, **kwargs): - is_old_facts = self.module._name == 'azure_rm_mariadbdatabase_facts' - if is_old_facts: - self.module.deprecate("The 'azure_rm_mariadbdatabase_facts' module has been renamed to 'azure_rm_mariadbdatabase_info'", - version='2.13', collection_name='ansible.builtin') - - for key in self.module_arg_spec: - setattr(self, key, kwargs[key]) - - if (self.resource_group is not None and - self.server_name is not None and - self.name is not None): - self.results['databases'] = self.get() - elif (self.resource_group is not None and - self.server_name is not None): - self.results['databases'] = self.list_by_server() - return self.results - - def get(self): - response = None - results = [] - try: - response = self.mariadb_client.databases.get(resource_group_name=self.resource_group, - server_name=self.server_name, - database_name=self.name) - self.log("Response : {0}".format(response)) - except CloudError as e: - self.log('Could not get facts for Databases.') - - if response is not None: - results.append(self.format_item(response)) - - return results - - def list_by_server(self): - response = None - results = [] - try: - response = self.mariadb_client.databases.list_by_server(resource_group_name=self.resource_group, - server_name=self.server_name) - self.log("Response : {0}".format(response)) - except CloudError as e: - self.fail("Error listing for server {0} - {1}".format(self.server_name, str(e))) - - if response is not None: - for item in response: - results.append(self.format_item(item)) - - return results - - def format_item(self, item): - d = item.as_dict() - d = { - 'resource_group': self.resource_group, - 'server_name': self.server_name, - 'name': d['name'], - 'charset': d['charset'], - 'collation': d['collation'] - } - return d - - -def main(): - AzureRMMariaDbDatabaseInfo() - - -if __name__ == '__main__': - main() diff --git a/test/support/integration/plugins/modules/azure_rm_mariadbfirewallrule.py b/test/support/integration/plugins/modules/azure_rm_mariadbfirewallrule.py deleted file mode 100644 index 1fc8c5e79e859c..00000000000000 --- a/test/support/integration/plugins/modules/azure_rm_mariadbfirewallrule.py +++ /dev/null @@ -1,277 +0,0 @@ -#!/usr/bin/python -# -# Copyright (c) 2018 Zim Kalinowski, -# Copyright (c) 2019 Matti Ranta, (@techknowlogick) -# -# 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_mariadbfirewallrule -version_added: "2.8" -short_description: Manage MariaDB firewall rule instance -description: - - Create, update and delete instance of MariaDB firewall rule. - -options: - resource_group: - description: - - The name of the resource group that contains the resource. You can obtain this value from the Azure Resource Manager API or the portal. - required: True - server_name: - description: - - The name of the server. - required: True - name: - description: - - The name of the MariaDB firewall rule. - required: True - start_ip_address: - description: - - The start IP address of the MariaDB firewall rule. Must be IPv4 format. - end_ip_address: - description: - - The end IP address of the MariaDB firewall rule. Must be IPv4 format. - state: - description: - - Assert the state of the MariaDB firewall rule. Use C(present) to create or update a rule and C(absent) to ensure it is not present. - default: present - choices: - - absent - - present - -extends_documentation_fragment: - - azure - -author: - - Zim Kalinowski (@zikalino) - - Matti Ranta (@techknowlogick) - -''' - -EXAMPLES = ''' - - name: Create (or update) MariaDB firewall rule - azure_rm_mariadbfirewallrule: - resource_group: myResourceGroup - server_name: testserver - name: rule1 - start_ip_address: 10.0.0.17 - end_ip_address: 10.0.0.20 -''' - -RETURN = ''' -id: - description: - - Resource ID. - returned: always - type: str - sample: "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/Microsoft.DBforMariaDB/servers/testserver/fire - wallRules/rule1" -''' - -import time -from ansible.module_utils.azure_rm_common import AzureRMModuleBase - -try: - from msrestazure.azure_exceptions import CloudError - from msrest.polling import LROPoller - from azure.mgmt.rdbms.mariadb import MariaDBManagementClient - from msrest.serialization import Model -except ImportError: - # This is handled in azure_rm_common - pass - - -class Actions: - NoAction, Create, Update, Delete = range(4) - - -class AzureRMMariaDbFirewallRule(AzureRMModuleBase): - """Configuration class for an Azure RM MariaDB firewall rule resource""" - - def __init__(self): - self.module_arg_spec = dict( - resource_group=dict( - type='str', - required=True - ), - server_name=dict( - type='str', - required=True - ), - name=dict( - type='str', - required=True - ), - start_ip_address=dict( - type='str' - ), - end_ip_address=dict( - type='str' - ), - state=dict( - type='str', - default='present', - choices=['present', 'absent'] - ) - ) - - self.resource_group = None - self.server_name = None - self.name = None - self.start_ip_address = None - self.end_ip_address = None - - self.results = dict(changed=False) - self.state = None - self.to_do = Actions.NoAction - - super(AzureRMMariaDbFirewallRule, self).__init__(derived_arg_spec=self.module_arg_spec, - supports_check_mode=True, - supports_tags=False) - - def exec_module(self, **kwargs): - """Main module execution method""" - - for key in list(self.module_arg_spec.keys()): - if hasattr(self, key): - setattr(self, key, kwargs[key]) - - old_response = None - response = None - - resource_group = self.get_resource_group(self.resource_group) - - old_response = self.get_firewallrule() - - if not old_response: - self.log("MariaDB firewall rule instance doesn't exist") - if self.state == 'absent': - self.log("Old instance didn't exist") - else: - self.to_do = Actions.Create - else: - self.log("MariaDB firewall rule instance already exists") - if self.state == 'absent': - self.to_do = Actions.Delete - elif self.state == 'present': - self.log("Need to check if MariaDB firewall rule instance has to be deleted or may be updated") - if (self.start_ip_address is not None) and (self.start_ip_address != old_response['start_ip_address']): - self.to_do = Actions.Update - if (self.end_ip_address is not None) and (self.end_ip_address != old_response['end_ip_address']): - self.to_do = Actions.Update - - if (self.to_do == Actions.Create) or (self.to_do == Actions.Update): - self.log("Need to Create / Update the MariaDB firewall rule instance") - - if self.check_mode: - self.results['changed'] = True - return self.results - - response = self.create_update_firewallrule() - - if not old_response: - self.results['changed'] = True - else: - self.results['changed'] = old_response.__ne__(response) - self.log("Creation / Update done") - elif self.to_do == Actions.Delete: - self.log("MariaDB firewall rule instance deleted") - self.results['changed'] = True - - if self.check_mode: - return self.results - - self.delete_firewallrule() - # make sure instance is actually deleted, for some Azure resources, instance is hanging around - # for some time after deletion -- this should be really fixed in Azure - while self.get_firewallrule(): - time.sleep(20) - else: - self.log("MariaDB firewall rule instance unchanged") - self.results['changed'] = False - response = old_response - - if response: - self.results["id"] = response["id"] - - return self.results - - def create_update_firewallrule(self): - ''' - Creates or updates MariaDB firewall rule with the specified configuration. - - :return: deserialized MariaDB firewall rule instance state dictionary - ''' - self.log("Creating / Updating the MariaDB firewall rule instance {0}".format(self.name)) - - try: - response = self.mariadb_client.firewall_rules.create_or_update(resource_group_name=self.resource_group, - server_name=self.server_name, - firewall_rule_name=self.name, - start_ip_address=self.start_ip_address, - end_ip_address=self.end_ip_address) - if isinstance(response, LROPoller): - response = self.get_poller_result(response) - - except CloudError as exc: - self.log('Error attempting to create the MariaDB firewall rule instance.') - self.fail("Error creating the MariaDB firewall rule instance: {0}".format(str(exc))) - return response.as_dict() - - def delete_firewallrule(self): - ''' - Deletes specified MariaDB firewall rule instance in the specified subscription and resource group. - - :return: True - ''' - self.log("Deleting the MariaDB firewall rule instance {0}".format(self.name)) - try: - response = self.mariadb_client.firewall_rules.delete(resource_group_name=self.resource_group, - server_name=self.server_name, - firewall_rule_name=self.name) - except CloudError as e: - self.log('Error attempting to delete the MariaDB firewall rule instance.') - self.fail("Error deleting the MariaDB firewall rule instance: {0}".format(str(e))) - - return True - - def get_firewallrule(self): - ''' - Gets the properties of the specified MariaDB firewall rule. - - :return: deserialized MariaDB firewall rule instance state dictionary - ''' - self.log("Checking if the MariaDB firewall rule instance {0} is present".format(self.name)) - found = False - try: - response = self.mariadb_client.firewall_rules.get(resource_group_name=self.resource_group, - server_name=self.server_name, - firewall_rule_name=self.name) - found = True - self.log("Response : {0}".format(response)) - self.log("MariaDB firewall rule instance : {0} found".format(response.name)) - except CloudError as e: - self.log('Did not find the MariaDB firewall rule instance.') - if found is True: - return response.as_dict() - - return False - - -def main(): - """Main execution""" - AzureRMMariaDbFirewallRule() - - -if __name__ == '__main__': - main() diff --git a/test/support/integration/plugins/modules/azure_rm_mariadbfirewallrule_info.py b/test/support/integration/plugins/modules/azure_rm_mariadbfirewallrule_info.py deleted file mode 100644 index ef71be8dce5eea..00000000000000 --- a/test/support/integration/plugins/modules/azure_rm_mariadbfirewallrule_info.py +++ /dev/null @@ -1,208 +0,0 @@ -#!/usr/bin/python -# -# Copyright (c) 2018 Zim Kalinowski, -# Copyright (c) 2019 Matti Ranta, (@techknowlogick) -# -# 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_mariadbfirewallrule_info -version_added: "2.9" -short_description: Get Azure MariaDB Firewall Rule facts -description: - - Get facts of Azure MariaDB Firewall Rule. - -options: - resource_group: - description: - - The name of the resource group. - required: True - type: str - server_name: - description: - - The name of the server. - required: True - type: str - name: - description: - - The name of the server firewall rule. - type: str - -extends_documentation_fragment: - - azure - -author: - - Zim Kalinowski (@zikalino) - - Matti Ranta (@techknowlogick) - -''' - -EXAMPLES = ''' - - name: Get instance of MariaDB Firewall Rule - azure_rm_mariadbfirewallrule_info: - resource_group: myResourceGroup - server_name: server_name - name: firewall_rule_name - - - name: List instances of MariaDB Firewall Rule - azure_rm_mariadbfirewallrule_info: - resource_group: myResourceGroup - server_name: server_name -''' - -RETURN = ''' -rules: - description: - - A list of dictionaries containing facts for MariaDB Firewall Rule. - returned: always - type: complex - contains: - id: - description: - - Resource ID. - returned: always - type: str - sample: "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/TestGroup/providers/Microsoft.DBforMariaDB/servers/testserver/fire - wallRules/rule1" - server_name: - description: - - The name of the server. - returned: always - type: str - sample: testserver - name: - description: - - Resource name. - returned: always - type: str - sample: rule1 - start_ip_address: - description: - - The start IP address of the MariaDB firewall rule. - returned: always - type: str - sample: 10.0.0.16 - end_ip_address: - description: - - The end IP address of the MariaDB firewall rule. - returned: always - type: str - sample: 10.0.0.18 -''' - -from ansible.module_utils.azure_rm_common import AzureRMModuleBase - -try: - from msrestazure.azure_exceptions import CloudError - from msrestazure.azure_operation import AzureOperationPoller - from azure.mgmt.rdbms.mariadb import MariaDBManagementClient - from msrest.serialization import Model -except ImportError: - # This is handled in azure_rm_common - pass - - -class AzureRMMariaDbFirewallRuleInfo(AzureRMModuleBase): - def __init__(self): - # define user inputs into argument - self.module_arg_spec = dict( - resource_group=dict( - type='str', - required=True - ), - server_name=dict( - type='str', - required=True - ), - name=dict( - type='str' - ) - ) - # store the results of the module operation - self.results = dict( - changed=False - ) - self.mgmt_client = None - self.resource_group = None - self.server_name = None - self.name = None - super(AzureRMMariaDbFirewallRuleInfo, self).__init__(self.module_arg_spec, supports_tags=False) - - def exec_module(self, **kwargs): - is_old_facts = self.module._name == 'azure_rm_mariadbfirewallrule_facts' - if is_old_facts: - self.module.deprecate("The 'azure_rm_mariadbfirewallrule_facts' module has been renamed to 'azure_rm_mariadbfirewallrule_info'", - version='2.13', collection_name='ansible.builtin') - - for key in self.module_arg_spec: - setattr(self, key, kwargs[key]) - self.mgmt_client = self.get_mgmt_svc_client(MariaDBManagementClient, - base_url=self._cloud_environment.endpoints.resource_manager) - - if (self.name is not None): - self.results['rules'] = self.get() - else: - self.results['rules'] = self.list_by_server() - return self.results - - def get(self): - response = None - results = [] - try: - response = self.mgmt_client.firewall_rules.get(resource_group_name=self.resource_group, - server_name=self.server_name, - firewall_rule_name=self.name) - self.log("Response : {0}".format(response)) - except CloudError as e: - self.log('Could not get facts for FirewallRules.') - - if response is not None: - results.append(self.format_item(response)) - - return results - - def list_by_server(self): - response = None - results = [] - try: - response = self.mgmt_client.firewall_rules.list_by_server(resource_group_name=self.resource_group, - server_name=self.server_name) - self.log("Response : {0}".format(response)) - except CloudError as e: - self.log('Could not get facts for FirewallRules.') - - if response is not None: - for item in response: - results.append(self.format_item(item)) - - return results - - def format_item(self, item): - d = item.as_dict() - d = { - 'resource_group': self.resource_group, - 'id': d['id'], - 'server_name': self.server_name, - 'name': d['name'], - 'start_ip_address': d['start_ip_address'], - 'end_ip_address': d['end_ip_address'] - } - return d - - -def main(): - AzureRMMariaDbFirewallRuleInfo() - - -if __name__ == '__main__': - main() diff --git a/test/support/integration/plugins/modules/azure_rm_mariadbserver_info.py b/test/support/integration/plugins/modules/azure_rm_mariadbserver_info.py deleted file mode 100644 index 464aa4d8a5e2ea..00000000000000 --- a/test/support/integration/plugins/modules/azure_rm_mariadbserver_info.py +++ /dev/null @@ -1,265 +0,0 @@ -#!/usr/bin/python -# -# Copyright (c) 2017 Zim Kalinowski, -# Copyright (c) 2019 Matti Ranta, (@techknowlogick) -# -# 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_mariadbserver_info -version_added: "2.9" -short_description: Get Azure MariaDB Server facts -description: - - Get facts of MariaDB Server. - -options: - resource_group: - description: - - The name of the resource group that contains the resource. You can obtain this value from the Azure Resource Manager API or the portal. - required: True - type: str - name: - description: - - The name of the server. - type: str - tags: - description: - - Limit results by providing a list of tags. Format tags as 'key' or 'key:value'. - type: list - -extends_documentation_fragment: - - azure - -author: - - Zim Kalinowski (@zikalino) - - Matti Ranta (@techknowlogick) - -''' - -EXAMPLES = ''' - - name: Get instance of MariaDB Server - azure_rm_mariadbserver_info: - resource_group: myResourceGroup - name: server_name - - - name: List instances of MariaDB Server - azure_rm_mariadbserver_info: - resource_group: myResourceGroup -''' - -RETURN = ''' -servers: - description: - - A list of dictionaries containing facts for MariaDB servers. - returned: always - type: complex - contains: - id: - description: - - Resource ID. - returned: always - type: str - sample: /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/Microsoft.DBforMariaDB/servers/myabdud1223 - resource_group: - description: - - Resource group name. - returned: always - type: str - sample: myResourceGroup - name: - description: - - Resource name. - returned: always - type: str - sample: myabdud1223 - location: - description: - - The location the resource resides in. - returned: always - type: str - sample: eastus - sku: - description: - - The SKU of the server. - returned: always - type: complex - contains: - name: - description: - - The name of the SKU. - returned: always - type: str - sample: GP_Gen4_2 - tier: - description: - - The tier of the particular SKU. - returned: always - type: str - sample: GeneralPurpose - capacity: - description: - - The scale capacity. - returned: always - type: int - sample: 2 - storage_mb: - description: - - The maximum storage allowed for a server. - returned: always - type: int - sample: 128000 - enforce_ssl: - description: - - Enable SSL enforcement. - returned: always - type: bool - sample: False - admin_username: - description: - - The administrator's login name of a server. - returned: always - type: str - sample: serveradmin - version: - description: - - Server version. - returned: always - type: str - sample: "9.6" - user_visible_state: - description: - - A state of a server that is visible to user. - returned: always - type: str - sample: Ready - fully_qualified_domain_name: - description: - - The fully qualified domain name of a server. - returned: always - type: str - sample: myabdud1223.mys.database.azure.com - tags: - description: - - Tags assigned to the resource. Dictionary of string:string pairs. - type: dict - sample: { tag1: abc } -''' - -from ansible.module_utils.azure_rm_common import AzureRMModuleBase - -try: - from msrestazure.azure_exceptions import CloudError - from azure.mgmt.rdbms.mariadb import MariaDBManagementClient - from msrest.serialization import Model -except ImportError: - # This is handled in azure_rm_common - pass - - -class AzureRMMariaDbServerInfo(AzureRMModuleBase): - def __init__(self): - # define user inputs into argument - self.module_arg_spec = dict( - resource_group=dict( - type='str', - required=True - ), - name=dict( - type='str' - ), - tags=dict( - type='list' - ) - ) - # store the results of the module operation - self.results = dict( - changed=False - ) - self.resource_group = None - self.name = None - self.tags = None - super(AzureRMMariaDbServerInfo, self).__init__(self.module_arg_spec, supports_tags=False) - - def exec_module(self, **kwargs): - is_old_facts = self.module._name == 'azure_rm_mariadbserver_facts' - if is_old_facts: - self.module.deprecate("The 'azure_rm_mariadbserver_facts' module has been renamed to 'azure_rm_mariadbserver_info'", - version='2.13', collection_name='ansible.builtin') - - for key in self.module_arg_spec: - setattr(self, key, kwargs[key]) - - if (self.resource_group is not None and - self.name is not None): - self.results['servers'] = self.get() - elif (self.resource_group is not None): - self.results['servers'] = self.list_by_resource_group() - return self.results - - def get(self): - response = None - results = [] - try: - response = self.mariadb_client.servers.get(resource_group_name=self.resource_group, - server_name=self.name) - self.log("Response : {0}".format(response)) - except CloudError as e: - self.log('Could not get facts for MariaDB Server.') - - if response and self.has_tags(response.tags, self.tags): - results.append(self.format_item(response)) - - return results - - def list_by_resource_group(self): - response = None - results = [] - try: - response = self.mariadb_client.servers.list_by_resource_group(resource_group_name=self.resource_group) - self.log("Response : {0}".format(response)) - except CloudError as e: - self.log('Could not get facts for MariaDB Servers.') - - if response is not None: - for item in response: - if self.has_tags(item.tags, self.tags): - results.append(self.format_item(item)) - - return results - - def format_item(self, item): - d = item.as_dict() - d = { - 'id': d['id'], - 'resource_group': self.resource_group, - 'name': d['name'], - 'sku': d['sku'], - 'location': d['location'], - 'storage_mb': d['storage_profile']['storage_mb'], - 'version': d['version'], - 'enforce_ssl': (d['ssl_enforcement'] == 'Enabled'), - 'admin_username': d['administrator_login'], - 'user_visible_state': d['user_visible_state'], - 'fully_qualified_domain_name': d['fully_qualified_domain_name'], - 'tags': d.get('tags') - } - - return d - - -def main(): - AzureRMMariaDbServerInfo() - - -if __name__ == '__main__': - main() diff --git a/test/support/integration/plugins/modules/azure_rm_resource.py b/test/support/integration/plugins/modules/azure_rm_resource.py deleted file mode 100644 index 6ea3e3bb9b3c1f..00000000000000 --- a/test/support/integration/plugins/modules/azure_rm_resource.py +++ /dev/null @@ -1,427 +0,0 @@ -#!/usr/bin/python -# -# Copyright (c) 2018 Zim Kalinowski, -# -# 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_resource -version_added: "2.6" -short_description: Create any Azure resource -description: - - Create, update or delete any Azure resource using Azure REST API. - - This module gives access to resources that are not supported via Ansible modules. - - Refer to U(https://docs.microsoft.com/en-us/rest/api/) regarding details related to specific resource REST API. - -options: - url: - description: - - Azure RM Resource URL. - api_version: - description: - - Specific API version to be used. - provider: - description: - - Provider type. - - Required if URL is not specified. - resource_group: - description: - - Resource group to be used. - - Required if URL is not specified. - resource_type: - description: - - Resource type. - - Required if URL is not specified. - resource_name: - description: - - Resource name. - - Required if URL Is not specified. - subresource: - description: - - List of subresources. - suboptions: - namespace: - description: - - Subresource namespace. - type: - description: - - Subresource type. - name: - description: - - Subresource name. - body: - description: - - The body of the HTTP request/response to the web service. - method: - description: - - The HTTP method of the request or response. It must be uppercase. - choices: - - GET - - PUT - - POST - - HEAD - - PATCH - - DELETE - - MERGE - default: "PUT" - status_code: - description: - - A valid, numeric, HTTP status code that signifies success of the request. Can also be comma separated list of status codes. - type: list - default: [ 200, 201, 202 ] - idempotency: - description: - - If enabled, idempotency check will be done by using I(method=GET) first and then comparing with I(body). - default: no - type: bool - polling_timeout: - description: - - If enabled, idempotency check will be done by using I(method=GET) first and then comparing with I(body). - default: 0 - type: int - version_added: "2.8" - polling_interval: - description: - - If enabled, idempotency check will be done by using I(method=GET) first and then comparing with I(body). - default: 60 - type: int - version_added: "2.8" - state: - description: - - Assert the state of the resource. Use C(present) to create or update resource or C(absent) to delete resource. - default: present - choices: - - absent - - present - -extends_documentation_fragment: - - azure - -author: - - Zim Kalinowski (@zikalino) - -''' - -EXAMPLES = ''' - - name: Update scaleset info using azure_rm_resource - azure_rm_resource: - resource_group: myResourceGroup - provider: compute - resource_type: virtualmachinescalesets - resource_name: myVmss - api_version: "2017-12-01" - body: { body } -''' - -RETURN = ''' -response: - description: - - Response specific to resource type. - returned: always - type: complex - contains: - id: - description: - - Resource ID. - type: str - returned: always - sample: "/subscriptions/xxxx...xxxx/resourceGroups/v-xisuRG/providers/Microsoft.Storage/storageAccounts/staccb57dc95183" - kind: - description: - - The kind of storage. - type: str - returned: always - sample: Storage - location: - description: - - The resource location, defaults to location of the resource group. - type: str - returned: always - sample: eastus - name: - description: - The storage account name. - type: str - returned: always - sample: staccb57dc95183 - properties: - description: - - The storage account's related properties. - type: dict - returned: always - sample: { - "creationTime": "2019-06-13T06:34:33.0996676Z", - "encryption": { - "keySource": "Microsoft.Storage", - "services": { - "blob": { - "enabled": true, - "lastEnabledTime": "2019-06-13T06:34:33.1934074Z" - }, - "file": { - "enabled": true, - "lastEnabledTime": "2019-06-13T06:34:33.1934074Z" - } - } - }, - "networkAcls": { - "bypass": "AzureServices", - "defaultAction": "Allow", - "ipRules": [], - "virtualNetworkRules": [] - }, - "primaryEndpoints": { - "blob": "https://staccb57dc95183.blob.core.windows.net/", - "file": "https://staccb57dc95183.file.core.windows.net/", - "queue": "https://staccb57dc95183.queue.core.windows.net/", - "table": "https://staccb57dc95183.table.core.windows.net/" - }, - "primaryLocation": "eastus", - "provisioningState": "Succeeded", - "secondaryLocation": "westus", - "statusOfPrimary": "available", - "statusOfSecondary": "available", - "supportsHttpsTrafficOnly": false - } - sku: - description: - - The storage account SKU. - type: dict - returned: always - sample: { - "name": "Standard_GRS", - "tier": "Standard" - } - tags: - description: - - Resource tags. - type: dict - returned: always - sample: { 'key1': 'value1' } - type: - description: - - The resource type. - type: str - returned: always - sample: "Microsoft.Storage/storageAccounts" - -''' - -from ansible.module_utils.azure_rm_common import AzureRMModuleBase -from ansible.module_utils.azure_rm_common_rest import GenericRestClient -from ansible.module_utils.common.dict_transformations import dict_merge - -try: - from msrestazure.azure_exceptions import CloudError - from msrest.service_client import ServiceClient - from msrestazure.tools import resource_id, is_valid_resource_id - import json - -except ImportError: - # This is handled in azure_rm_common - pass - - -class AzureRMResource(AzureRMModuleBase): - def __init__(self): - # define user inputs into argument - self.module_arg_spec = dict( - url=dict( - type='str' - ), - provider=dict( - type='str', - ), - resource_group=dict( - type='str', - ), - resource_type=dict( - type='str', - ), - resource_name=dict( - type='str', - ), - subresource=dict( - type='list', - default=[] - ), - api_version=dict( - type='str' - ), - method=dict( - type='str', - default='PUT', - choices=["GET", "PUT", "POST", "HEAD", "PATCH", "DELETE", "MERGE"] - ), - body=dict( - type='raw' - ), - status_code=dict( - type='list', - default=[200, 201, 202] - ), - idempotency=dict( - type='bool', - default=False - ), - polling_timeout=dict( - type='int', - default=0 - ), - polling_interval=dict( - type='int', - default=60 - ), - state=dict( - type='str', - default='present', - choices=['present', 'absent'] - ) - ) - # store the results of the module operation - self.results = dict( - changed=False, - response=None - ) - self.mgmt_client = None - self.url = None - self.api_version = None - self.provider = None - self.resource_group = None - self.resource_type = None - self.resource_name = None - self.subresource_type = None - self.subresource_name = None - self.subresource = [] - self.method = None - self.status_code = [] - self.idempotency = False - self.polling_timeout = None - self.polling_interval = None - self.state = None - self.body = None - super(AzureRMResource, self).__init__(self.module_arg_spec, supports_tags=False) - - def exec_module(self, **kwargs): - for key in self.module_arg_spec: - setattr(self, key, kwargs[key]) - self.mgmt_client = self.get_mgmt_svc_client(GenericRestClient, - base_url=self._cloud_environment.endpoints.resource_manager) - - if self.state == 'absent': - self.method = 'DELETE' - self.status_code.append(204) - - if self.url is None: - orphan = None - rargs = dict() - rargs['subscription'] = self.subscription_id - rargs['resource_group'] = self.resource_group - if not (self.provider is None or self.provider.lower().startswith('.microsoft')): - rargs['namespace'] = "Microsoft." + self.provider - else: - rargs['namespace'] = self.provider - - if self.resource_type is not None and self.resource_name is not None: - rargs['type'] = self.resource_type - rargs['name'] = self.resource_name - for i in range(len(self.subresource)): - resource_ns = self.subresource[i].get('namespace', None) - resource_type = self.subresource[i].get('type', None) - resource_name = self.subresource[i].get('name', None) - if resource_type is not None and resource_name is not None: - rargs['child_namespace_' + str(i + 1)] = resource_ns - rargs['child_type_' + str(i + 1)] = resource_type - rargs['child_name_' + str(i + 1)] = resource_name - else: - orphan = resource_type - else: - orphan = self.resource_type - - self.url = resource_id(**rargs) - - if orphan is not None: - self.url += '/' + orphan - - # if api_version was not specified, get latest one - if not self.api_version: - try: - # extract provider and resource type - if "/providers/" in self.url: - provider = self.url.split("/providers/")[1].split("/")[0] - resourceType = self.url.split(provider + "/")[1].split("/")[0] - url = "/subscriptions/" + self.subscription_id + "/providers/" + provider - api_versions = json.loads(self.mgmt_client.query(url, "GET", {'api-version': '2015-01-01'}, None, None, [200], 0, 0).text) - for rt in api_versions['resourceTypes']: - if rt['resourceType'].lower() == resourceType.lower(): - self.api_version = rt['apiVersions'][0] - break - else: - # if there's no provider in API version, assume Microsoft.Resources - self.api_version = '2018-05-01' - if not self.api_version: - self.fail("Couldn't find api version for {0}/{1}".format(provider, resourceType)) - except Exception as exc: - self.fail("Failed to obtain API version: {0}".format(str(exc))) - - query_parameters = {} - query_parameters['api-version'] = self.api_version - - header_parameters = {} - header_parameters['Content-Type'] = 'application/json; charset=utf-8' - - needs_update = True - response = None - - if self.idempotency: - original = self.mgmt_client.query(self.url, "GET", query_parameters, None, None, [200, 404], 0, 0) - - if original.status_code == 404: - if self.state == 'absent': - needs_update = False - else: - try: - response = json.loads(original.text) - needs_update = (dict_merge(response, self.body) != response) - except Exception: - pass - - if needs_update: - response = self.mgmt_client.query(self.url, - self.method, - query_parameters, - header_parameters, - self.body, - self.status_code, - self.polling_timeout, - self.polling_interval) - if self.state == 'present': - try: - response = json.loads(response.text) - except Exception: - response = response.text - else: - response = None - - self.results['response'] = response - self.results['changed'] = needs_update - - return self.results - - -def main(): - AzureRMResource() - - -if __name__ == '__main__': - main() diff --git a/test/support/integration/plugins/modules/azure_rm_resource_info.py b/test/support/integration/plugins/modules/azure_rm_resource_info.py deleted file mode 100644 index f797f66219cc31..00000000000000 --- a/test/support/integration/plugins/modules/azure_rm_resource_info.py +++ /dev/null @@ -1,432 +0,0 @@ -#!/usr/bin/python -# -# Copyright (c) 2018 Zim Kalinowski, -# -# 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_resource_info -version_added: "2.9" -short_description: Generic facts of Azure resources -description: - - Obtain facts of any resource using Azure REST API. - - This module gives access to resources that are not supported via Ansible modules. - - Refer to U(https://docs.microsoft.com/en-us/rest/api/) regarding details related to specific resource REST API. - -options: - url: - description: - - Azure RM Resource URL. - api_version: - description: - - Specific API version to be used. - provider: - description: - - Provider type, should be specified in no URL is given. - resource_group: - description: - - Resource group to be used. - - Required if URL is not specified. - resource_type: - description: - - Resource type. - resource_name: - description: - - Resource name. - subresource: - description: - - List of subresources. - suboptions: - namespace: - description: - - Subresource namespace. - type: - description: - - Subresource type. - name: - description: - - Subresource name. - -extends_documentation_fragment: - - azure - -author: - - Zim Kalinowski (@zikalino) - -''' - -EXAMPLES = ''' - - name: Get scaleset info - azure_rm_resource_info: - resource_group: myResourceGroup - provider: compute - resource_type: virtualmachinescalesets - resource_name: myVmss - api_version: "2017-12-01" - - - name: Query all the resources in the resource group - azure_rm_resource_info: - resource_group: "{{ resource_group }}" - resource_type: resources -''' - -RETURN = ''' -response: - description: - - Response specific to resource type. - returned: always - type: complex - contains: - id: - description: - - Id of the Azure resource. - type: str - returned: always - sample: "/subscriptions/xxxx...xxxx/resourceGroups/v-xisuRG/providers/Microsoft.Compute/virtualMachines/myVM" - location: - description: - - Resource location. - type: str - returned: always - sample: eastus - name: - description: - - Resource name. - type: str - returned: always - sample: myVM - properties: - description: - - Specifies the virtual machine's property. - type: complex - returned: always - contains: - diagnosticsProfile: - description: - - Specifies the boot diagnostic settings state. - type: complex - returned: always - contains: - bootDiagnostics: - description: - - A debugging feature, which to view Console Output and Screenshot to diagnose VM status. - type: dict - returned: always - sample: { - "enabled": true, - "storageUri": "https://vxisurgdiag.blob.core.windows.net/" - } - hardwareProfile: - description: - - Specifies the hardware settings for the virtual machine. - type: dict - returned: always - sample: { - "vmSize": "Standard_D2s_v3" - } - networkProfile: - description: - - Specifies the network interfaces of the virtual machine. - type: complex - returned: always - contains: - networkInterfaces: - description: - - Describes a network interface reference. - type: list - returned: always - sample: - - { - "id": "/subscriptions/xxxx...xxxx/resourceGroups/v-xisuRG/providers/Microsoft.Network/networkInterfaces/myvm441" - } - osProfile: - description: - - Specifies the operating system settings for the virtual machine. - type: complex - returned: always - contains: - adminUsername: - description: - - Specifies the name of the administrator account. - type: str - returned: always - sample: azureuser - allowExtensionOperations: - description: - - Specifies whether extension operations should be allowed on the virtual machine. - - This may only be set to False when no extensions are present on the virtual machine. - type: bool - returned: always - sample: true - computerName: - description: - - Specifies the host OS name of the virtual machine. - type: str - returned: always - sample: myVM - requireGuestProvisionSignale: - description: - - Specifies the host require guest provision signal or not. - type: bool - returned: always - sample: true - secrets: - description: - - Specifies set of certificates that should be installed onto the virtual machine. - type: list - returned: always - sample: [] - linuxConfiguration: - description: - - Specifies the Linux operating system settings on the virtual machine. - type: dict - returned: when OS type is Linux - sample: { - "disablePasswordAuthentication": false, - "provisionVMAgent": true - } - provisioningState: - description: - - The provisioning state. - type: str - returned: always - sample: Succeeded - vmID: - description: - - Specifies the VM unique ID which is a 128-bits identifier that is encoded and stored in all Azure laaS VMs SMBIOS. - - It can be read using platform BIOS commands. - type: str - returned: always - sample: "eb86d9bb-6725-4787-a487-2e497d5b340c" - storageProfile: - description: - - Specifies the storage account type for the managed disk. - type: complex - returned: always - contains: - dataDisks: - description: - - Specifies the parameters that are used to add a data disk to virtual machine. - type: list - returned: always - sample: - - { - "caching": "None", - "createOption": "Attach", - "diskSizeGB": 1023, - "lun": 2, - "managedDisk": { - "id": "/subscriptions/xxxx....xxxx/resourceGroups/V-XISURG/providers/Microsoft.Compute/disks/testdisk2", - "storageAccountType": "StandardSSD_LRS" - }, - "name": "testdisk2" - } - - { - "caching": "None", - "createOption": "Attach", - "diskSizeGB": 1023, - "lun": 1, - "managedDisk": { - "id": "/subscriptions/xxxx...xxxx/resourceGroups/V-XISURG/providers/Microsoft.Compute/disks/testdisk3", - "storageAccountType": "StandardSSD_LRS" - }, - "name": "testdisk3" - } - - imageReference: - description: - - Specifies information about the image to use. - type: dict - returned: always - sample: { - "offer": "UbuntuServer", - "publisher": "Canonical", - "sku": "18.04-LTS", - "version": "latest" - } - osDisk: - description: - - Specifies information about the operating system disk used by the virtual machine. - type: dict - returned: always - sample: { - "caching": "ReadWrite", - "createOption": "FromImage", - "diskSizeGB": 30, - "managedDisk": { - "id": "/subscriptions/xxx...xxxx/resourceGroups/v-xisuRG/providers/Microsoft.Compute/disks/myVM_disk1_xxx", - "storageAccountType": "Premium_LRS" - }, - "name": "myVM_disk1_xxx", - "osType": "Linux" - } - type: - description: - - The type of identity used for the virtual machine. - type: str - returned: always - sample: "Microsoft.Compute/virtualMachines" -''' - -from ansible.module_utils.azure_rm_common import AzureRMModuleBase -from ansible.module_utils.azure_rm_common_rest import GenericRestClient - -try: - from msrestazure.azure_exceptions import CloudError - from msrest.service_client import ServiceClient - from msrestazure.tools import resource_id, is_valid_resource_id - import json - -except ImportError: - # This is handled in azure_rm_common - pass - - -class AzureRMResourceInfo(AzureRMModuleBase): - def __init__(self): - # define user inputs into argument - self.module_arg_spec = dict( - url=dict( - type='str' - ), - provider=dict( - type='str' - ), - resource_group=dict( - type='str' - ), - resource_type=dict( - type='str' - ), - resource_name=dict( - type='str' - ), - subresource=dict( - type='list', - default=[] - ), - api_version=dict( - type='str' - ) - ) - # store the results of the module operation - self.results = dict( - response=[] - ) - self.mgmt_client = None - self.url = None - self.api_version = None - self.provider = None - self.resource_group = None - self.resource_type = None - self.resource_name = None - self.subresource = [] - super(AzureRMResourceInfo, self).__init__(self.module_arg_spec, supports_tags=False) - - def exec_module(self, **kwargs): - is_old_facts = self.module._name == 'azure_rm_resource_facts' - if is_old_facts: - self.module.deprecate("The 'azure_rm_resource_facts' module has been renamed to 'azure_rm_resource_info'", - version='2.13', collection_name='ansible.builtin') - - for key in self.module_arg_spec: - setattr(self, key, kwargs[key]) - self.mgmt_client = self.get_mgmt_svc_client(GenericRestClient, - base_url=self._cloud_environment.endpoints.resource_manager) - - if self.url is None: - orphan = None - rargs = dict() - rargs['subscription'] = self.subscription_id - rargs['resource_group'] = self.resource_group - if not (self.provider is None or self.provider.lower().startswith('.microsoft')): - rargs['namespace'] = "Microsoft." + self.provider - else: - rargs['namespace'] = self.provider - - if self.resource_type is not None and self.resource_name is not None: - rargs['type'] = self.resource_type - rargs['name'] = self.resource_name - for i in range(len(self.subresource)): - resource_ns = self.subresource[i].get('namespace', None) - resource_type = self.subresource[i].get('type', None) - resource_name = self.subresource[i].get('name', None) - if resource_type is not None and resource_name is not None: - rargs['child_namespace_' + str(i + 1)] = resource_ns - rargs['child_type_' + str(i + 1)] = resource_type - rargs['child_name_' + str(i + 1)] = resource_name - else: - orphan = resource_type - else: - orphan = self.resource_type - - self.url = resource_id(**rargs) - - if orphan is not None: - self.url += '/' + orphan - - # if api_version was not specified, get latest one - if not self.api_version: - try: - # extract provider and resource type - if "/providers/" in self.url: - provider = self.url.split("/providers/")[1].split("/")[0] - resourceType = self.url.split(provider + "/")[1].split("/")[0] - url = "/subscriptions/" + self.subscription_id + "/providers/" + provider - api_versions = json.loads(self.mgmt_client.query(url, "GET", {'api-version': '2015-01-01'}, None, None, [200], 0, 0).text) - for rt in api_versions['resourceTypes']: - if rt['resourceType'].lower() == resourceType.lower(): - self.api_version = rt['apiVersions'][0] - break - else: - # if there's no provider in API version, assume Microsoft.Resources - self.api_version = '2018-05-01' - if not self.api_version: - self.fail("Couldn't find api version for {0}/{1}".format(provider, resourceType)) - except Exception as exc: - self.fail("Failed to obtain API version: {0}".format(str(exc))) - - self.results['url'] = self.url - - query_parameters = {} - query_parameters['api-version'] = self.api_version - - header_parameters = {} - header_parameters['Content-Type'] = 'application/json; charset=utf-8' - skiptoken = None - - while True: - if skiptoken: - query_parameters['skiptoken'] = skiptoken - response = self.mgmt_client.query(self.url, "GET", query_parameters, header_parameters, None, [200, 404], 0, 0) - try: - response = json.loads(response.text) - if isinstance(response, dict): - if response.get('value'): - self.results['response'] = self.results['response'] + response['value'] - skiptoken = response.get('nextLink') - else: - self.results['response'] = self.results['response'] + [response] - except Exception as e: - self.fail('Failed to parse response: ' + str(e)) - if not skiptoken: - break - return self.results - - -def main(): - AzureRMResourceInfo() - - -if __name__ == '__main__': - main() diff --git a/test/support/integration/plugins/modules/azure_rm_storageaccount.py b/test/support/integration/plugins/modules/azure_rm_storageaccount.py deleted file mode 100644 index d4158bbda87b4d..00000000000000 --- a/test/support/integration/plugins/modules/azure_rm_storageaccount.py +++ /dev/null @@ -1,684 +0,0 @@ -#!/usr/bin/python -# -# Copyright (c) 2016 Matt Davis, -# Chris Houseknecht, -# -# 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_storageaccount -version_added: "2.1" -short_description: Manage Azure storage accounts -description: - - Create, update or delete a storage account. -options: - resource_group: - description: - - Name of the resource group to use. - required: true - aliases: - - resource_group_name - name: - description: - - Name of the storage account to update or create. - state: - description: - - State of the storage account. Use C(present) to create or update a storage account and use C(absent) to delete an account. - default: present - choices: - - absent - - present - location: - description: - - Valid Azure location. Defaults to location of the resource group. - account_type: - description: - - Type of storage account. Required when creating a storage account. - - C(Standard_ZRS) and C(Premium_LRS) accounts cannot be changed to other account types. - - Other account types cannot be changed to C(Standard_ZRS) or C(Premium_LRS). - choices: - - Premium_LRS - - Standard_GRS - - Standard_LRS - - StandardSSD_LRS - - Standard_RAGRS - - Standard_ZRS - - Premium_ZRS - aliases: - - type - custom_domain: - description: - - User domain assigned to the storage account. - - Must be a dictionary with I(name) and I(use_sub_domain) keys where I(name) is the CNAME source. - - Only one custom domain is supported per storage account at this time. - - To clear the existing custom domain, use an empty string for the custom domain name property. - - Can be added to an existing storage account. Will be ignored during storage account creation. - aliases: - - custom_dns_domain_suffix - kind: - description: - - The kind of storage. - default: 'Storage' - choices: - - Storage - - StorageV2 - - BlobStorage - version_added: "2.2" - access_tier: - description: - - The access tier for this storage account. Required when I(kind=BlobStorage). - choices: - - Hot - - Cool - version_added: "2.4" - force_delete_nonempty: - description: - - Attempt deletion if resource already exists and cannot be updated. - type: bool - aliases: - - force - https_only: - description: - - Allows https traffic only to storage service when set to C(true). - type: bool - version_added: "2.8" - blob_cors: - description: - - Specifies CORS rules for the Blob service. - - You can include up to five CorsRule elements in the request. - - If no blob_cors elements are included in the argument list, nothing about CORS will be changed. - - If you want to delete all CORS rules and disable CORS for the Blob service, explicitly set I(blob_cors=[]). - type: list - version_added: "2.8" - suboptions: - allowed_origins: - description: - - A list of origin domains that will be allowed via CORS, or "*" to allow all domains. - type: list - required: true - allowed_methods: - description: - - A list of HTTP methods that are allowed to be executed by the origin. - type: list - required: true - max_age_in_seconds: - description: - - The number of seconds that the client/browser should cache a preflight response. - type: int - required: true - exposed_headers: - description: - - A list of response headers to expose to CORS clients. - type: list - required: true - allowed_headers: - description: - - A list of headers allowed to be part of the cross-origin request. - type: list - required: true - -extends_documentation_fragment: - - azure - - azure_tags - -author: - - Chris Houseknecht (@chouseknecht) - - Matt Davis (@nitzmahone) -''' - -EXAMPLES = ''' - - name: remove account, if it exists - azure_rm_storageaccount: - resource_group: myResourceGroup - name: clh0002 - state: absent - - - name: create an account - azure_rm_storageaccount: - resource_group: myResourceGroup - name: clh0002 - type: Standard_RAGRS - tags: - testing: testing - delete: on-exit - - - name: create an account with blob CORS - azure_rm_storageaccount: - resource_group: myResourceGroup - name: clh002 - type: Standard_RAGRS - blob_cors: - - allowed_origins: - - http://www.example.com/ - allowed_methods: - - GET - - POST - allowed_headers: - - x-ms-meta-data* - - x-ms-meta-target* - - x-ms-meta-abc - exposed_headers: - - x-ms-meta-* - max_age_in_seconds: 200 -''' - - -RETURN = ''' -state: - description: - - Current state of the storage account. - returned: always - type: complex - contains: - account_type: - description: - - Type of storage account. - returned: always - type: str - sample: Standard_RAGRS - custom_domain: - description: - - User domain assigned to the storage account. - returned: always - type: complex - contains: - name: - description: - - CNAME source. - returned: always - type: str - sample: testaccount - use_sub_domain: - description: - - Whether to use sub domain. - returned: always - type: bool - sample: true - id: - description: - - Resource ID. - returned: always - type: str - sample: "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/Microsoft.Storage/storageAccounts/clh0003" - location: - description: - - Valid Azure location. Defaults to location of the resource group. - returned: always - type: str - sample: eastus2 - name: - description: - - Name of the storage account to update or create. - returned: always - type: str - sample: clh0003 - primary_endpoints: - description: - - The URLs to retrieve the public I(blob), I(queue), or I(table) object from the primary location. - returned: always - type: dict - sample: { - "blob": "https://clh0003.blob.core.windows.net/", - "queue": "https://clh0003.queue.core.windows.net/", - "table": "https://clh0003.table.core.windows.net/" - } - primary_location: - description: - - The location of the primary data center for the storage account. - returned: always - type: str - sample: eastus2 - provisioning_state: - description: - - The status of the storage account. - - Possible values include C(Creating), C(ResolvingDNS), C(Succeeded). - returned: always - type: str - sample: Succeeded - resource_group: - description: - - The resource group's name. - returned: always - type: str - sample: Testing - secondary_endpoints: - description: - - The URLs to retrieve the public I(blob), I(queue), or I(table) object from the secondary location. - returned: always - type: dict - sample: { - "blob": "https://clh0003-secondary.blob.core.windows.net/", - "queue": "https://clh0003-secondary.queue.core.windows.net/", - "table": "https://clh0003-secondary.table.core.windows.net/" - } - secondary_location: - description: - - The location of the geo-replicated secondary for the storage account. - returned: always - type: str - sample: centralus - status_of_primary: - description: - - The status of the primary location of the storage account; either C(available) or C(unavailable). - returned: always - type: str - sample: available - status_of_secondary: - description: - - The status of the secondary location of the storage account; either C(available) or C(unavailable). - returned: always - type: str - sample: available - tags: - description: - - Resource tags. - returned: always - type: dict - sample: { 'tags1': 'value1' } - type: - description: - - The storage account type. - returned: always - type: str - sample: "Microsoft.Storage/storageAccounts" -''' - -try: - from msrestazure.azure_exceptions import CloudError - from azure.storage.cloudstorageaccount import CloudStorageAccount - from azure.common import AzureMissingResourceHttpError -except ImportError: - # This is handled in azure_rm_common - pass - -import copy -from ansible.module_utils.azure_rm_common import AZURE_SUCCESS_STATE, AzureRMModuleBase -from ansible.module_utils._text import to_native - -cors_rule_spec = dict( - allowed_origins=dict(type='list', elements='str', required=True), - allowed_methods=dict(type='list', elements='str', required=True), - max_age_in_seconds=dict(type='int', required=True), - exposed_headers=dict(type='list', elements='str', required=True), - allowed_headers=dict(type='list', elements='str', required=True), -) - - -def compare_cors(cors1, cors2): - if len(cors1) != len(cors2): - return False - copy2 = copy.copy(cors2) - for rule1 in cors1: - matched = False - for rule2 in copy2: - if (rule1['max_age_in_seconds'] == rule2['max_age_in_seconds'] - and set(rule1['allowed_methods']) == set(rule2['allowed_methods']) - and set(rule1['allowed_origins']) == set(rule2['allowed_origins']) - and set(rule1['allowed_headers']) == set(rule2['allowed_headers']) - and set(rule1['exposed_headers']) == set(rule2['exposed_headers'])): - matched = True - copy2.remove(rule2) - if not matched: - return False - return True - - -class AzureRMStorageAccount(AzureRMModuleBase): - - def __init__(self): - - self.module_arg_spec = dict( - account_type=dict(type='str', - choices=['Premium_LRS', 'Standard_GRS', 'Standard_LRS', 'StandardSSD_LRS', 'Standard_RAGRS', 'Standard_ZRS', 'Premium_ZRS'], - aliases=['type']), - custom_domain=dict(type='dict', aliases=['custom_dns_domain_suffix']), - location=dict(type='str'), - name=dict(type='str', required=True), - resource_group=dict(required=True, type='str', aliases=['resource_group_name']), - state=dict(default='present', choices=['present', 'absent']), - force_delete_nonempty=dict(type='bool', default=False, aliases=['force']), - tags=dict(type='dict'), - kind=dict(type='str', default='Storage', choices=['Storage', 'StorageV2', 'BlobStorage']), - access_tier=dict(type='str', choices=['Hot', 'Cool']), - https_only=dict(type='bool', default=False), - blob_cors=dict(type='list', options=cors_rule_spec, elements='dict') - ) - - self.results = dict( - changed=False, - state=dict() - ) - - self.account_dict = None - self.resource_group = None - self.name = None - self.state = None - self.location = None - self.account_type = None - self.custom_domain = None - self.tags = None - self.force_delete_nonempty = None - self.kind = None - self.access_tier = None - self.https_only = None - self.blob_cors = None - - super(AzureRMStorageAccount, self).__init__(self.module_arg_spec, - supports_check_mode=True) - - def exec_module(self, **kwargs): - - for key in list(self.module_arg_spec.keys()) + ['tags']: - setattr(self, key, kwargs[key]) - - resource_group = self.get_resource_group(self.resource_group) - if not self.location: - # Set default location - self.location = resource_group.location - - if len(self.name) < 3 or len(self.name) > 24: - self.fail("Parameter error: name length must be between 3 and 24 characters.") - - if self.custom_domain: - if self.custom_domain.get('name', None) is None: - self.fail("Parameter error: expecting custom_domain to have a name attribute of type string.") - if self.custom_domain.get('use_sub_domain', None) is None: - self.fail("Parameter error: expecting custom_domain to have a use_sub_domain " - "attribute of type boolean.") - - self.account_dict = self.get_account() - - if self.state == 'present' and self.account_dict and \ - self.account_dict['provisioning_state'] != AZURE_SUCCESS_STATE: - self.fail("Error: storage account {0} has not completed provisioning. State is {1}. Expecting state " - "to be {2}.".format(self.name, self.account_dict['provisioning_state'], AZURE_SUCCESS_STATE)) - - if self.account_dict is not None: - self.results['state'] = self.account_dict - else: - self.results['state'] = dict() - - if self.state == 'present': - if not self.account_dict: - self.results['state'] = self.create_account() - else: - self.update_account() - elif self.state == 'absent' and self.account_dict: - self.delete_account() - self.results['state'] = dict(Status='Deleted') - - return self.results - - def check_name_availability(self): - self.log('Checking name availability for {0}'.format(self.name)) - try: - response = self.storage_client.storage_accounts.check_name_availability(self.name) - except CloudError as e: - self.log('Error attempting to validate name.') - self.fail("Error checking name availability: {0}".format(str(e))) - if not response.name_available: - self.log('Error name not available.') - self.fail("{0} - {1}".format(response.message, response.reason)) - - def get_account(self): - self.log('Get properties for account {0}'.format(self.name)) - account_obj = None - blob_service_props = None - account_dict = None - - try: - account_obj = self.storage_client.storage_accounts.get_properties(self.resource_group, self.name) - blob_service_props = self.storage_client.blob_services.get_service_properties(self.resource_group, self.name) - except CloudError: - pass - - if account_obj: - account_dict = self.account_obj_to_dict(account_obj, blob_service_props) - - return account_dict - - def account_obj_to_dict(self, account_obj, blob_service_props=None): - account_dict = dict( - id=account_obj.id, - name=account_obj.name, - location=account_obj.location, - resource_group=self.resource_group, - type=account_obj.type, - access_tier=(account_obj.access_tier.value - if account_obj.access_tier is not None else None), - sku_tier=account_obj.sku.tier.value, - sku_name=account_obj.sku.name.value, - provisioning_state=account_obj.provisioning_state.value, - secondary_location=account_obj.secondary_location, - status_of_primary=(account_obj.status_of_primary.value - if account_obj.status_of_primary is not None else None), - status_of_secondary=(account_obj.status_of_secondary.value - if account_obj.status_of_secondary is not None else None), - primary_location=account_obj.primary_location, - https_only=account_obj.enable_https_traffic_only - ) - account_dict['custom_domain'] = None - if account_obj.custom_domain: - account_dict['custom_domain'] = dict( - name=account_obj.custom_domain.name, - use_sub_domain=account_obj.custom_domain.use_sub_domain - ) - - account_dict['primary_endpoints'] = None - if account_obj.primary_endpoints: - account_dict['primary_endpoints'] = dict( - blob=account_obj.primary_endpoints.blob, - queue=account_obj.primary_endpoints.queue, - table=account_obj.primary_endpoints.table - ) - account_dict['secondary_endpoints'] = None - if account_obj.secondary_endpoints: - account_dict['secondary_endpoints'] = dict( - blob=account_obj.secondary_endpoints.blob, - queue=account_obj.secondary_endpoints.queue, - table=account_obj.secondary_endpoints.table - ) - account_dict['tags'] = None - if account_obj.tags: - account_dict['tags'] = account_obj.tags - if blob_service_props and blob_service_props.cors and blob_service_props.cors.cors_rules: - account_dict['blob_cors'] = [dict( - allowed_origins=[to_native(y) for y in x.allowed_origins], - allowed_methods=[to_native(y) for y in x.allowed_methods], - max_age_in_seconds=x.max_age_in_seconds, - exposed_headers=[to_native(y) for y in x.exposed_headers], - allowed_headers=[to_native(y) for y in x.allowed_headers] - ) for x in blob_service_props.cors.cors_rules] - return account_dict - - def update_account(self): - self.log('Update storage account {0}'.format(self.name)) - if bool(self.https_only) != bool(self.account_dict.get('https_only')): - self.results['changed'] = True - self.account_dict['https_only'] = self.https_only - if not self.check_mode: - try: - parameters = self.storage_models.StorageAccountUpdateParameters(enable_https_traffic_only=self.https_only) - self.storage_client.storage_accounts.update(self.resource_group, - self.name, - parameters) - except Exception as exc: - self.fail("Failed to update account type: {0}".format(str(exc))) - - if self.account_type: - if self.account_type != self.account_dict['sku_name']: - # change the account type - SkuName = self.storage_models.SkuName - if self.account_dict['sku_name'] in [SkuName.premium_lrs, SkuName.standard_zrs]: - self.fail("Storage accounts of type {0} and {1} cannot be changed.".format( - SkuName.premium_lrs, SkuName.standard_zrs)) - if self.account_type in [SkuName.premium_lrs, SkuName.standard_zrs]: - self.fail("Storage account of type {0} cannot be changed to a type of {1} or {2}.".format( - self.account_dict['sku_name'], SkuName.premium_lrs, SkuName.standard_zrs)) - - self.results['changed'] = True - self.account_dict['sku_name'] = self.account_type - - if self.results['changed'] and not self.check_mode: - # Perform the update. The API only allows changing one attribute per call. - try: - self.log("sku_name: %s" % self.account_dict['sku_name']) - self.log("sku_tier: %s" % self.account_dict['sku_tier']) - sku = self.storage_models.Sku(name=SkuName(self.account_dict['sku_name'])) - sku.tier = self.storage_models.SkuTier(self.account_dict['sku_tier']) - parameters = self.storage_models.StorageAccountUpdateParameters(sku=sku) - self.storage_client.storage_accounts.update(self.resource_group, - self.name, - parameters) - except Exception as exc: - self.fail("Failed to update account type: {0}".format(str(exc))) - - if self.custom_domain: - if not self.account_dict['custom_domain'] or self.account_dict['custom_domain'] != self.custom_domain: - self.results['changed'] = True - self.account_dict['custom_domain'] = self.custom_domain - - if self.results['changed'] and not self.check_mode: - new_domain = self.storage_models.CustomDomain(name=self.custom_domain['name'], - use_sub_domain=self.custom_domain['use_sub_domain']) - parameters = self.storage_models.StorageAccountUpdateParameters(custom_domain=new_domain) - try: - self.storage_client.storage_accounts.update(self.resource_group, self.name, parameters) - except Exception as exc: - self.fail("Failed to update custom domain: {0}".format(str(exc))) - - if self.access_tier: - if not self.account_dict['access_tier'] or self.account_dict['access_tier'] != self.access_tier: - self.results['changed'] = True - self.account_dict['access_tier'] = self.access_tier - - if self.results['changed'] and not self.check_mode: - parameters = self.storage_models.StorageAccountUpdateParameters(access_tier=self.access_tier) - try: - self.storage_client.storage_accounts.update(self.resource_group, self.name, parameters) - except Exception as exc: - self.fail("Failed to update access tier: {0}".format(str(exc))) - - update_tags, self.account_dict['tags'] = self.update_tags(self.account_dict['tags']) - if update_tags: - self.results['changed'] = True - if not self.check_mode: - parameters = self.storage_models.StorageAccountUpdateParameters(tags=self.account_dict['tags']) - try: - self.storage_client.storage_accounts.update(self.resource_group, self.name, parameters) - except Exception as exc: - self.fail("Failed to update tags: {0}".format(str(exc))) - - if self.blob_cors and not compare_cors(self.account_dict.get('blob_cors', []), self.blob_cors): - self.results['changed'] = True - if not self.check_mode: - self.set_blob_cors() - - def create_account(self): - self.log("Creating account {0}".format(self.name)) - - if not self.location: - self.fail('Parameter error: location required when creating a storage account.') - - if not self.account_type: - self.fail('Parameter error: account_type required when creating a storage account.') - - if not self.access_tier and self.kind == 'BlobStorage': - self.fail('Parameter error: access_tier required when creating a storage account of type BlobStorage.') - - self.check_name_availability() - self.results['changed'] = True - - if self.check_mode: - account_dict = dict( - location=self.location, - account_type=self.account_type, - name=self.name, - resource_group=self.resource_group, - enable_https_traffic_only=self.https_only, - tags=dict() - ) - if self.tags: - account_dict['tags'] = self.tags - if self.blob_cors: - account_dict['blob_cors'] = self.blob_cors - return account_dict - sku = self.storage_models.Sku(name=self.storage_models.SkuName(self.account_type)) - sku.tier = self.storage_models.SkuTier.standard if 'Standard' in self.account_type else \ - self.storage_models.SkuTier.premium - parameters = self.storage_models.StorageAccountCreateParameters(sku=sku, - kind=self.kind, - location=self.location, - tags=self.tags, - access_tier=self.access_tier) - self.log(str(parameters)) - try: - poller = self.storage_client.storage_accounts.create(self.resource_group, self.name, parameters) - self.get_poller_result(poller) - except CloudError as e: - self.log('Error creating storage account.') - self.fail("Failed to create account: {0}".format(str(e))) - if self.blob_cors: - self.set_blob_cors() - # the poller doesn't actually return anything - return self.get_account() - - def delete_account(self): - if self.account_dict['provisioning_state'] == self.storage_models.ProvisioningState.succeeded.value and \ - not self.force_delete_nonempty and self.account_has_blob_containers(): - self.fail("Account contains blob containers. Is it in use? Use the force_delete_nonempty option to attempt deletion.") - - self.log('Delete storage account {0}'.format(self.name)) - self.results['changed'] = True - if not self.check_mode: - try: - status = self.storage_client.storage_accounts.delete(self.resource_group, self.name) - self.log("delete status: ") - self.log(str(status)) - except CloudError as e: - self.fail("Failed to delete the account: {0}".format(str(e))) - return True - - def account_has_blob_containers(self): - ''' - If there are blob containers, then there are likely VMs depending on this account and it should - not be deleted. - ''' - self.log('Checking for existing blob containers') - blob_service = self.get_blob_client(self.resource_group, self.name) - try: - response = blob_service.list_containers() - except AzureMissingResourceHttpError: - # No blob storage available? - return False - - if len(response.items) > 0: - return True - return False - - def set_blob_cors(self): - try: - cors_rules = self.storage_models.CorsRules(cors_rules=[self.storage_models.CorsRule(**x) for x in self.blob_cors]) - self.storage_client.blob_services.set_service_properties(self.resource_group, - self.name, - self.storage_models.BlobServiceProperties(cors=cors_rules)) - except Exception as exc: - self.fail("Failed to set CORS rules: {0}".format(str(exc))) - - -def main(): - AzureRMStorageAccount() - - -if __name__ == '__main__': - main() diff --git a/test/support/integration/plugins/modules/azure_rm_webapp.py b/test/support/integration/plugins/modules/azure_rm_webapp.py deleted file mode 100644 index 4f185f458056d7..00000000000000 --- a/test/support/integration/plugins/modules/azure_rm_webapp.py +++ /dev/null @@ -1,1070 +0,0 @@ -#!/usr/bin/python -# -# Copyright (c) 2018 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_webapp -version_added: "2.7" -short_description: Manage Web App instances -description: - - Create, update and delete instance of Web App. - -options: - resource_group: - description: - - Name of the resource group to which the resource belongs. - required: True - name: - description: - - Unique name of the app to create or update. To create or update a deployment slot, use the {slot} parameter. - required: True - - location: - description: - - Resource location. If not set, location from the resource group will be used as default. - - plan: - description: - - App service plan. Required for creation. - - Can be name of existing app service plan in same resource group as web app. - - Can be the resource ID of an existing app service plan. For example - /subscriptions//resourceGroups//providers/Microsoft.Web/serverFarms/. - - Can be a dict containing five parameters, defined below. - - C(name), name of app service plan. - - C(resource_group), resource group of the app service plan. - - C(sku), SKU of app service plan, allowed values listed on U(https://azure.microsoft.com/en-us/pricing/details/app-service/linux/). - - C(is_linux), whether or not the app service plan is Linux. defaults to C(False). - - C(number_of_workers), number of workers for app service plan. - - frameworks: - description: - - Set of run time framework settings. Each setting is a dictionary. - - See U(https://docs.microsoft.com/en-us/azure/app-service/app-service-web-overview) for more info. - suboptions: - name: - description: - - Name of the framework. - - Supported framework list for Windows web app and Linux web app is different. - - Windows web apps support C(java), C(net_framework), C(php), C(python), and C(node) from June 2018. - - Windows web apps support multiple framework at the same time. - - Linux web apps support C(java), C(ruby), C(php), C(dotnetcore), and C(node) from June 2018. - - Linux web apps support only one framework. - - Java framework is mutually exclusive with others. - choices: - - java - - net_framework - - php - - python - - ruby - - dotnetcore - - node - version: - description: - - Version of the framework. For Linux web app supported value, see U(https://aka.ms/linux-stacks) for more info. - - C(net_framework) supported value sample, C(v4.0) for .NET 4.6 and C(v3.0) for .NET 3.5. - - C(php) supported value sample, C(5.5), C(5.6), C(7.0). - - C(python) supported value sample, C(5.5), C(5.6), C(7.0). - - C(node) supported value sample, C(6.6), C(6.9). - - C(dotnetcore) supported value sample, C(1.0), C(1.1), C(1.2). - - C(ruby) supported value sample, C(2.3). - - C(java) supported value sample, C(1.9) for Windows web app. C(1.8) for Linux web app. - settings: - description: - - List of settings of the framework. - suboptions: - java_container: - description: - - Name of Java container. - - Supported only when I(frameworks=java). Sample values C(Tomcat), C(Jetty). - java_container_version: - description: - - Version of Java container. - - Supported only when I(frameworks=java). - - Sample values for C(Tomcat), C(8.0), C(8.5), C(9.0). For C(Jetty,), C(9.1), C(9.3). - - container_settings: - description: - - Web app container settings. - suboptions: - name: - description: - - Name of container, for example C(imagename:tag). - registry_server_url: - description: - - Container registry server URL, for example C(mydockerregistry.io). - registry_server_user: - description: - - The container registry server user name. - registry_server_password: - description: - - The container registry server password. - - scm_type: - description: - - Repository type of deployment source, for example C(LocalGit), C(GitHub). - - List of supported values maintained at U(https://docs.microsoft.com/en-us/rest/api/appservice/webapps/createorupdate#scmtype). - - deployment_source: - description: - - Deployment source for git. - suboptions: - url: - description: - - Repository url of deployment source. - - branch: - description: - - The branch name of the repository. - startup_file: - description: - - The web's startup file. - - Used only for Linux web apps. - - client_affinity_enabled: - description: - - Whether or not to send session affinity cookies, which route client requests in the same session to the same instance. - type: bool - default: True - - https_only: - description: - - Configures web site to accept only https requests. - type: bool - - dns_registration: - description: - - Whether or not the web app hostname is registered with DNS on creation. Set to C(false) to register. - type: bool - - skip_custom_domain_verification: - description: - - Whether or not to skip verification of custom (non *.azurewebsites.net) domains associated with web app. Set to C(true) to skip. - type: bool - - ttl_in_seconds: - description: - - Time to live in seconds for web app default domain name. - - app_settings: - description: - - Configure web app application settings. Suboptions are in key value pair format. - - purge_app_settings: - description: - - Purge any existing application settings. Replace web app application settings with app_settings. - type: bool - - app_state: - description: - - Start/Stop/Restart the web app. - type: str - choices: - - started - - stopped - - restarted - default: started - - state: - description: - - State of the Web App. - - Use C(present) to create or update a Web App and C(absent) to delete it. - default: present - choices: - - absent - - present - -extends_documentation_fragment: - - azure - - azure_tags - -author: - - Yunge Zhu (@yungezz) - -''' - -EXAMPLES = ''' - - name: Create a windows web app with non-exist app service plan - azure_rm_webapp: - resource_group: myResourceGroup - name: myWinWebapp - plan: - resource_group: myAppServicePlan_rg - name: myAppServicePlan - is_linux: false - sku: S1 - - - name: Create a docker web app with some app settings, with docker image - azure_rm_webapp: - resource_group: myResourceGroup - name: myDockerWebapp - plan: - resource_group: myAppServicePlan_rg - name: myAppServicePlan - is_linux: true - sku: S1 - number_of_workers: 2 - app_settings: - testkey: testvalue - testkey2: testvalue2 - container_settings: - name: ansible/ansible:ubuntu1404 - - - name: Create a docker web app with private acr registry - azure_rm_webapp: - resource_group: myResourceGroup - name: myDockerWebapp - plan: myAppServicePlan - app_settings: - testkey: testvalue - container_settings: - name: ansible/ubuntu1404 - registry_server_url: myregistry.io - registry_server_user: user - registry_server_password: pass - - - name: Create a linux web app with Node 6.6 framework - azure_rm_webapp: - resource_group: myResourceGroup - name: myLinuxWebapp - plan: - resource_group: myAppServicePlan_rg - name: myAppServicePlan - app_settings: - testkey: testvalue - frameworks: - - name: "node" - version: "6.6" - - - name: Create a windows web app with node, php - azure_rm_webapp: - resource_group: myResourceGroup - name: myWinWebapp - plan: - resource_group: myAppServicePlan_rg - name: myAppServicePlan - app_settings: - testkey: testvalue - frameworks: - - name: "node" - version: 6.6 - - name: "php" - version: "7.0" - - - name: Create a stage deployment slot for an existing web app - azure_rm_webapp: - resource_group: myResourceGroup - name: myWebapp/slots/stage - plan: - resource_group: myAppServicePlan_rg - name: myAppServicePlan - app_settings: - testkey:testvalue - - - name: Create a linux web app with java framework - azure_rm_webapp: - resource_group: myResourceGroup - name: myLinuxWebapp - plan: - resource_group: myAppServicePlan_rg - name: myAppServicePlan - app_settings: - testkey: testvalue - frameworks: - - name: "java" - version: "8" - settings: - java_container: "Tomcat" - java_container_version: "8.5" -''' - -RETURN = ''' -azure_webapp: - description: - - ID of current web app. - returned: always - type: str - sample: "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/Microsoft.Web/sites/myWebApp" -''' - -import time -from ansible.module_utils.azure_rm_common import AzureRMModuleBase - -try: - from msrestazure.azure_exceptions import CloudError - from msrest.polling import LROPoller - from msrest.serialization import Model - from azure.mgmt.web.models import ( - site_config, app_service_plan, Site, - AppServicePlan, SkuDescription, NameValuePair - ) -except ImportError: - # This is handled in azure_rm_common - pass - -container_settings_spec = dict( - name=dict(type='str', required=True), - registry_server_url=dict(type='str'), - registry_server_user=dict(type='str'), - registry_server_password=dict(type='str', no_log=True) -) - -deployment_source_spec = dict( - url=dict(type='str'), - branch=dict(type='str') -) - - -framework_settings_spec = dict( - java_container=dict(type='str', required=True), - java_container_version=dict(type='str', required=True) -) - - -framework_spec = dict( - name=dict( - type='str', - required=True, - choices=['net_framework', 'java', 'php', 'node', 'python', 'dotnetcore', 'ruby']), - version=dict(type='str', required=True), - settings=dict(type='dict', options=framework_settings_spec) -) - - -def _normalize_sku(sku): - if sku is None: - return sku - - sku = sku.upper() - if sku == 'FREE': - return 'F1' - elif sku == 'SHARED': - return 'D1' - return sku - - -def get_sku_name(tier): - tier = tier.upper() - if tier == 'F1' or tier == "FREE": - return 'FREE' - elif tier == 'D1' or tier == "SHARED": - return 'SHARED' - elif tier in ['B1', 'B2', 'B3', 'BASIC']: - return 'BASIC' - elif tier in ['S1', 'S2', 'S3']: - return 'STANDARD' - elif tier in ['P1', 'P2', 'P3']: - return 'PREMIUM' - elif tier in ['P1V2', 'P2V2', 'P3V2']: - return 'PREMIUMV2' - else: - return None - - -def appserviceplan_to_dict(plan): - return dict( - id=plan.id, - name=plan.name, - kind=plan.kind, - location=plan.location, - reserved=plan.reserved, - is_linux=plan.reserved, - provisioning_state=plan.provisioning_state, - tags=plan.tags if plan.tags else None - ) - - -def webapp_to_dict(webapp): - return dict( - id=webapp.id, - name=webapp.name, - location=webapp.location, - client_cert_enabled=webapp.client_cert_enabled, - enabled=webapp.enabled, - reserved=webapp.reserved, - client_affinity_enabled=webapp.client_affinity_enabled, - server_farm_id=webapp.server_farm_id, - host_names_disabled=webapp.host_names_disabled, - https_only=webapp.https_only if hasattr(webapp, 'https_only') else None, - skip_custom_domain_verification=webapp.skip_custom_domain_verification if hasattr(webapp, 'skip_custom_domain_verification') else None, - ttl_in_seconds=webapp.ttl_in_seconds if hasattr(webapp, 'ttl_in_seconds') else None, - state=webapp.state, - tags=webapp.tags if webapp.tags else None - ) - - -class Actions: - CreateOrUpdate, UpdateAppSettings, Delete = range(3) - - -class AzureRMWebApps(AzureRMModuleBase): - """Configuration class for an Azure RM Web App resource""" - - def __init__(self): - self.module_arg_spec = dict( - resource_group=dict( - type='str', - required=True - ), - name=dict( - type='str', - required=True - ), - location=dict( - type='str' - ), - plan=dict( - type='raw' - ), - frameworks=dict( - type='list', - elements='dict', - options=framework_spec - ), - container_settings=dict( - type='dict', - options=container_settings_spec - ), - scm_type=dict( - type='str', - ), - deployment_source=dict( - type='dict', - options=deployment_source_spec - ), - startup_file=dict( - type='str' - ), - client_affinity_enabled=dict( - type='bool', - default=True - ), - dns_registration=dict( - type='bool' - ), - https_only=dict( - type='bool' - ), - skip_custom_domain_verification=dict( - type='bool' - ), - ttl_in_seconds=dict( - type='int' - ), - app_settings=dict( - type='dict' - ), - purge_app_settings=dict( - type='bool', - default=False - ), - app_state=dict( - type='str', - choices=['started', 'stopped', 'restarted'], - default='started' - ), - state=dict( - type='str', - default='present', - choices=['present', 'absent'] - ) - ) - - mutually_exclusive = [['container_settings', 'frameworks']] - - self.resource_group = None - self.name = None - self.location = None - - # update in create_or_update as parameters - self.client_affinity_enabled = True - self.dns_registration = None - self.skip_custom_domain_verification = None - self.ttl_in_seconds = None - self.https_only = None - - self.tags = None - - # site config, e.g app settings, ssl - self.site_config = dict() - self.app_settings = dict() - self.app_settings_strDic = None - - # app service plan - self.plan = None - - # siteSourceControl - self.deployment_source = dict() - - # site, used at level creation, or update. e.g windows/linux, client_affinity etc first level args - self.site = None - - # property for internal usage, not used for sdk - self.container_settings = None - - self.purge_app_settings = False - self.app_state = 'started' - - self.results = dict( - changed=False, - id=None, - ) - self.state = None - self.to_do = [] - - self.frameworks = None - - # set site_config value from kwargs - self.site_config_updatable_properties = ["net_framework_version", - "java_version", - "php_version", - "python_version", - "scm_type"] - - # updatable_properties - self.updatable_properties = ["client_affinity_enabled", - "force_dns_registration", - "https_only", - "skip_custom_domain_verification", - "ttl_in_seconds"] - - self.supported_linux_frameworks = ['ruby', 'php', 'dotnetcore', 'node', 'java'] - self.supported_windows_frameworks = ['net_framework', 'php', 'python', 'node', 'java'] - - super(AzureRMWebApps, self).__init__(derived_arg_spec=self.module_arg_spec, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True, - supports_tags=True) - - def exec_module(self, **kwargs): - """Main module execution method""" - - for key in list(self.module_arg_spec.keys()) + ['tags']: - if hasattr(self, key): - setattr(self, key, kwargs[key]) - elif kwargs[key] is not None: - if key == "scm_type": - self.site_config[key] = kwargs[key] - - old_response = None - response = None - to_be_updated = False - - # set location - resource_group = self.get_resource_group(self.resource_group) - if not self.location: - self.location = resource_group.location - - # get existing web app - old_response = self.get_webapp() - - if old_response: - self.results['id'] = old_response['id'] - - if self.state == 'present': - if not self.plan and not old_response: - self.fail("Please specify plan for newly created web app.") - - if not self.plan: - self.plan = old_response['server_farm_id'] - - self.plan = self.parse_resource_to_dict(self.plan) - - # get app service plan - is_linux = False - old_plan = self.get_app_service_plan() - if old_plan: - is_linux = old_plan['reserved'] - else: - is_linux = self.plan['is_linux'] if 'is_linux' in self.plan else False - - if self.frameworks: - # java is mutually exclusive with other frameworks - if len(self.frameworks) > 1 and any(f['name'] == 'java' for f in self.frameworks): - self.fail('Java is mutually exclusive with other frameworks.') - - if is_linux: - if len(self.frameworks) != 1: - self.fail('Can specify one framework only for Linux web app.') - - if self.frameworks[0]['name'] not in self.supported_linux_frameworks: - self.fail('Unsupported framework {0} for Linux web app.'.format(self.frameworks[0]['name'])) - - self.site_config['linux_fx_version'] = (self.frameworks[0]['name'] + '|' + self.frameworks[0]['version']).upper() - - if self.frameworks[0]['name'] == 'java': - if self.frameworks[0]['version'] != '8': - self.fail("Linux web app only supports java 8.") - if self.frameworks[0]['settings'] and self.frameworks[0]['settings']['java_container'].lower() != 'tomcat': - self.fail("Linux web app only supports tomcat container.") - - if self.frameworks[0]['settings'] and self.frameworks[0]['settings']['java_container'].lower() == 'tomcat': - self.site_config['linux_fx_version'] = 'TOMCAT|' + self.frameworks[0]['settings']['java_container_version'] + '-jre8' - else: - self.site_config['linux_fx_version'] = 'JAVA|8-jre8' - else: - for fx in self.frameworks: - if fx.get('name') not in self.supported_windows_frameworks: - self.fail('Unsupported framework {0} for Windows web app.'.format(fx.get('name'))) - else: - self.site_config[fx.get('name') + '_version'] = fx.get('version') - - if 'settings' in fx and fx['settings'] is not None: - for key, value in fx['settings'].items(): - self.site_config[key] = value - - if not self.app_settings: - self.app_settings = dict() - - if self.container_settings: - linux_fx_version = 'DOCKER|' - - if self.container_settings.get('registry_server_url'): - self.app_settings['DOCKER_REGISTRY_SERVER_URL'] = 'https://' + self.container_settings['registry_server_url'] - - linux_fx_version += self.container_settings['registry_server_url'] + '/' - - linux_fx_version += self.container_settings['name'] - - self.site_config['linux_fx_version'] = linux_fx_version - - if self.container_settings.get('registry_server_user'): - self.app_settings['DOCKER_REGISTRY_SERVER_USERNAME'] = self.container_settings['registry_server_user'] - - if self.container_settings.get('registry_server_password'): - self.app_settings['DOCKER_REGISTRY_SERVER_PASSWORD'] = self.container_settings['registry_server_password'] - - # init site - self.site = Site(location=self.location, site_config=self.site_config) - - if self.https_only is not None: - self.site.https_only = self.https_only - - if self.client_affinity_enabled: - self.site.client_affinity_enabled = self.client_affinity_enabled - - # check if the web app already present in the resource group - if not old_response: - self.log("Web App instance doesn't exist") - - to_be_updated = True - self.to_do.append(Actions.CreateOrUpdate) - self.site.tags = self.tags - - # service plan is required for creation - if not self.plan: - self.fail("Please specify app service plan in plan parameter.") - - if not old_plan: - # no existing service plan, create one - if (not self.plan.get('name') or not self.plan.get('sku')): - self.fail('Please specify name, is_linux, sku in plan') - - if 'location' not in self.plan: - plan_resource_group = self.get_resource_group(self.plan['resource_group']) - self.plan['location'] = plan_resource_group.location - - old_plan = self.create_app_service_plan() - - self.site.server_farm_id = old_plan['id'] - - # if linux, setup startup_file - if old_plan['is_linux']: - if hasattr(self, 'startup_file'): - self.site_config['app_command_line'] = self.startup_file - - # set app setting - if self.app_settings: - app_settings = [] - for key in self.app_settings.keys(): - app_settings.append(NameValuePair(name=key, value=self.app_settings[key])) - - self.site_config['app_settings'] = app_settings - else: - # existing web app, do update - self.log("Web App instance already exists") - - self.log('Result: {0}'.format(old_response)) - - update_tags, self.site.tags = self.update_tags(old_response.get('tags', None)) - - if update_tags: - to_be_updated = True - - # check if root level property changed - if self.is_updatable_property_changed(old_response): - to_be_updated = True - self.to_do.append(Actions.CreateOrUpdate) - - # check if site_config changed - old_config = self.get_webapp_configuration() - - if self.is_site_config_changed(old_config): - to_be_updated = True - self.to_do.append(Actions.CreateOrUpdate) - - # check if linux_fx_version changed - if old_config.linux_fx_version != self.site_config.get('linux_fx_version', ''): - to_be_updated = True - self.to_do.append(Actions.CreateOrUpdate) - - self.app_settings_strDic = self.list_app_settings() - - # purge existing app_settings: - if self.purge_app_settings: - to_be_updated = True - self.app_settings_strDic = dict() - self.to_do.append(Actions.UpdateAppSettings) - - # check if app settings changed - if self.purge_app_settings or self.is_app_settings_changed(): - to_be_updated = True - self.to_do.append(Actions.UpdateAppSettings) - - if self.app_settings: - for key in self.app_settings.keys(): - self.app_settings_strDic[key] = self.app_settings[key] - - elif self.state == 'absent': - if old_response: - self.log("Delete Web App instance") - self.results['changed'] = True - - if self.check_mode: - return self.results - - self.delete_webapp() - - self.log('Web App instance deleted') - - else: - self.fail("Web app {0} not exists.".format(self.name)) - - if to_be_updated: - self.log('Need to Create/Update web app') - self.results['changed'] = True - - if self.check_mode: - return self.results - - if Actions.CreateOrUpdate in self.to_do: - response = self.create_update_webapp() - - self.results['id'] = response['id'] - - if Actions.UpdateAppSettings in self.to_do: - update_response = self.update_app_settings() - self.results['id'] = update_response.id - - webapp = None - if old_response: - webapp = old_response - if response: - webapp = response - - if webapp: - if (webapp['state'] != 'Stopped' and self.app_state == 'stopped') or \ - (webapp['state'] != 'Running' and self.app_state == 'started') or \ - self.app_state == 'restarted': - - self.results['changed'] = True - if self.check_mode: - return self.results - - self.set_webapp_state(self.app_state) - - return self.results - - # compare existing web app with input, determine weather it's update operation - def is_updatable_property_changed(self, existing_webapp): - for property_name in self.updatable_properties: - if hasattr(self, property_name) and getattr(self, property_name) is not None and \ - getattr(self, property_name) != existing_webapp.get(property_name, None): - return True - - return False - - # compare xxx_version - def is_site_config_changed(self, existing_config): - for fx_version in self.site_config_updatable_properties: - if self.site_config.get(fx_version): - if not getattr(existing_config, fx_version) or \ - getattr(existing_config, fx_version).upper() != self.site_config.get(fx_version).upper(): - return True - - return False - - # comparing existing app setting with input, determine whether it's changed - def is_app_settings_changed(self): - if self.app_settings: - if self.app_settings_strDic: - for key in self.app_settings.keys(): - if self.app_settings[key] != self.app_settings_strDic.get(key, None): - return True - else: - return True - return False - - # comparing deployment source with input, determine wheather it's changed - def is_deployment_source_changed(self, existing_webapp): - if self.deployment_source: - if self.deployment_source.get('url') \ - and self.deployment_source['url'] != existing_webapp.get('site_source_control')['url']: - return True - - if self.deployment_source.get('branch') \ - and self.deployment_source['branch'] != existing_webapp.get('site_source_control')['branch']: - return True - - return False - - def create_update_webapp(self): - ''' - Creates or updates Web App with the specified configuration. - - :return: deserialized Web App instance state dictionary - ''' - self.log( - "Creating / Updating the Web App instance {0}".format(self.name)) - - try: - skip_dns_registration = self.dns_registration - force_dns_registration = None if self.dns_registration is None else not self.dns_registration - - response = self.web_client.web_apps.create_or_update(resource_group_name=self.resource_group, - name=self.name, - site_envelope=self.site, - skip_dns_registration=skip_dns_registration, - skip_custom_domain_verification=self.skip_custom_domain_verification, - force_dns_registration=force_dns_registration, - ttl_in_seconds=self.ttl_in_seconds) - if isinstance(response, LROPoller): - response = self.get_poller_result(response) - - except CloudError as exc: - self.log('Error attempting to create the Web App instance.') - self.fail( - "Error creating the Web App instance: {0}".format(str(exc))) - return webapp_to_dict(response) - - def delete_webapp(self): - ''' - Deletes specified Web App instance in the specified subscription and resource group. - - :return: True - ''' - self.log("Deleting the Web App instance {0}".format(self.name)) - try: - response = self.web_client.web_apps.delete(resource_group_name=self.resource_group, - name=self.name) - except CloudError as e: - self.log('Error attempting to delete the Web App instance.') - self.fail( - "Error deleting the Web App instance: {0}".format(str(e))) - - return True - - def get_webapp(self): - ''' - Gets the properties of the specified Web App. - - :return: deserialized Web App instance state dictionary - ''' - self.log( - "Checking if the Web App instance {0} is present".format(self.name)) - - response = None - - try: - response = self.web_client.web_apps.get(resource_group_name=self.resource_group, - name=self.name) - - # Newer SDK versions (0.40.0+) seem to return None if it doesn't exist instead of raising CloudError - if response is not None: - self.log("Response : {0}".format(response)) - self.log("Web App instance : {0} found".format(response.name)) - return webapp_to_dict(response) - - except CloudError as ex: - pass - - self.log("Didn't find web app {0} in resource group {1}".format( - self.name, self.resource_group)) - - return False - - def get_app_service_plan(self): - ''' - Gets app service plan - :return: deserialized app service plan dictionary - ''' - self.log("Get App Service Plan {0}".format(self.plan['name'])) - - try: - response = self.web_client.app_service_plans.get( - resource_group_name=self.plan['resource_group'], - name=self.plan['name']) - - # Newer SDK versions (0.40.0+) seem to return None if it doesn't exist instead of raising CloudError - if response is not None: - self.log("Response : {0}".format(response)) - self.log("App Service Plan : {0} found".format(response.name)) - - return appserviceplan_to_dict(response) - except CloudError as ex: - pass - - self.log("Didn't find app service plan {0} in resource group {1}".format( - self.plan['name'], self.plan['resource_group'])) - - return False - - def create_app_service_plan(self): - ''' - Creates app service plan - :return: deserialized app service plan dictionary - ''' - self.log("Create App Service Plan {0}".format(self.plan['name'])) - - try: - # normalize sku - sku = _normalize_sku(self.plan['sku']) - - sku_def = SkuDescription(tier=get_sku_name( - sku), name=sku, capacity=(self.plan.get('number_of_workers', None))) - plan_def = AppServicePlan( - location=self.plan['location'], app_service_plan_name=self.plan['name'], sku=sku_def, reserved=(self.plan.get('is_linux', None))) - - poller = self.web_client.app_service_plans.create_or_update( - self.plan['resource_group'], self.plan['name'], plan_def) - - if isinstance(poller, LROPoller): - response = self.get_poller_result(poller) - - self.log("Response : {0}".format(response)) - - return appserviceplan_to_dict(response) - except CloudError as ex: - self.fail("Failed to create app service plan {0} in resource group {1}: {2}".format( - self.plan['name'], self.plan['resource_group'], str(ex))) - - def list_app_settings(self): - ''' - List application settings - :return: deserialized list response - ''' - self.log("List application setting") - - try: - - response = self.web_client.web_apps.list_application_settings( - resource_group_name=self.resource_group, name=self.name) - self.log("Response : {0}".format(response)) - - return response.properties - except CloudError as ex: - self.fail("Failed to list application settings for web app {0} in resource group {1}: {2}".format( - self.name, self.resource_group, str(ex))) - - def update_app_settings(self): - ''' - Update application settings - :return: deserialized updating response - ''' - self.log("Update application setting") - - try: - response = self.web_client.web_apps.update_application_settings( - resource_group_name=self.resource_group, name=self.name, properties=self.app_settings_strDic) - self.log("Response : {0}".format(response)) - - return response - except CloudError as ex: - self.fail("Failed to update application settings for web app {0} in resource group {1}: {2}".format( - self.name, self.resource_group, str(ex))) - - def create_or_update_source_control(self): - ''' - Update site source control - :return: deserialized updating response - ''' - self.log("Update site source control") - - if self.deployment_source is None: - return False - - self.deployment_source['is_manual_integration'] = False - self.deployment_source['is_mercurial'] = False - - try: - response = self.web_client.web_client.create_or_update_source_control( - self.resource_group, self.name, self.deployment_source) - self.log("Response : {0}".format(response)) - - return response.as_dict() - except CloudError as ex: - self.fail("Failed to update site source control for web app {0} in resource group {1}".format( - self.name, self.resource_group)) - - def get_webapp_configuration(self): - ''' - Get web app configuration - :return: deserialized web app configuration response - ''' - self.log("Get web app configuration") - - try: - - response = self.web_client.web_apps.get_configuration( - resource_group_name=self.resource_group, name=self.name) - self.log("Response : {0}".format(response)) - - return response - except CloudError as ex: - self.log("Failed to get configuration for web app {0} in resource group {1}: {2}".format( - self.name, self.resource_group, str(ex))) - - return False - - def set_webapp_state(self, appstate): - ''' - Start/stop/restart web app - :return: deserialized updating response - ''' - try: - if appstate == 'started': - response = self.web_client.web_apps.start(resource_group_name=self.resource_group, name=self.name) - elif appstate == 'stopped': - response = self.web_client.web_apps.stop(resource_group_name=self.resource_group, name=self.name) - elif appstate == 'restarted': - response = self.web_client.web_apps.restart(resource_group_name=self.resource_group, name=self.name) - else: - self.fail("Invalid web app state {0}".format(appstate)) - - self.log("Response : {0}".format(response)) - - return response - except CloudError as ex: - request_id = ex.request_id if ex.request_id else '' - self.log("Failed to {0} web app {1} in resource group {2}, request_id {3} - {4}".format( - appstate, self.name, self.resource_group, request_id, str(ex))) - - -def main(): - """Main execution""" - AzureRMWebApps() - - -if __name__ == '__main__': - main() diff --git a/test/support/integration/plugins/modules/azure_rm_webapp_info.py b/test/support/integration/plugins/modules/azure_rm_webapp_info.py deleted file mode 100644 index 222868030649b5..00000000000000 --- a/test/support/integration/plugins/modules/azure_rm_webapp_info.py +++ /dev/null @@ -1,489 +0,0 @@ -#!/usr/bin/python -# -# Copyright (c) 2018 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_webapp_info - -version_added: "2.9" - -short_description: Get Azure web app facts - -description: - - Get facts for a specific web app or all web app in a resource group, or all web app in current subscription. - -options: - name: - description: - - Only show results for a specific web app. - resource_group: - description: - - Limit results by resource group. - return_publish_profile: - description: - - Indicate whether to return publishing profile of the web app. - default: False - type: bool - tags: - description: - - Limit results by providing a list of tags. Format tags as 'key' or 'key:value'. - -extends_documentation_fragment: - - azure - -author: - - Yunge Zhu (@yungezz) -''' - -EXAMPLES = ''' - - name: Get facts for web app by name - azure_rm_webapp_info: - resource_group: myResourceGroup - name: winwebapp1 - - - name: Get facts for web apps in resource group - azure_rm_webapp_info: - resource_group: myResourceGroup - - - name: Get facts for web apps with tags - azure_rm_webapp_info: - tags: - - testtag - - foo:bar -''' - -RETURN = ''' -webapps: - description: - - List of web apps. - returned: always - type: complex - contains: - id: - description: - - ID of the web app. - returned: always - type: str - sample: /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/Microsoft.Web/sites/myWebApp - name: - description: - - Name of the web app. - returned: always - type: str - sample: winwebapp1 - resource_group: - description: - - Resource group of the web app. - returned: always - type: str - sample: myResourceGroup - location: - description: - - Location of the web app. - returned: always - type: str - sample: eastus - plan: - description: - - ID of app service plan used by the web app. - returned: always - type: str - sample: /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/Microsoft.Web/serverfarms/myAppServicePlan - app_settings: - description: - - App settings of the application. Only returned when web app has app settings. - returned: always - type: dict - sample: { - "testkey": "testvalue", - "testkey2": "testvalue2" - } - frameworks: - description: - - Frameworks of the application. Only returned when web app has frameworks. - returned: always - type: list - sample: [ - { - "name": "net_framework", - "version": "v4.0" - }, - { - "name": "java", - "settings": { - "java_container": "tomcat", - "java_container_version": "8.5" - }, - "version": "1.7" - }, - { - "name": "php", - "version": "5.6" - } - ] - availability_state: - description: - - Availability of this web app. - returned: always - type: str - sample: Normal - default_host_name: - description: - - Host name of the web app. - returned: always - type: str - sample: vxxisurg397winapp4.azurewebsites.net - enabled: - description: - - Indicates the web app enabled or not. - returned: always - type: bool - sample: true - enabled_host_names: - description: - - Enabled host names of the web app. - returned: always - type: list - sample: [ - "vxxisurg397winapp4.azurewebsites.net", - "vxxisurg397winapp4.scm.azurewebsites.net" - ] - host_name_ssl_states: - description: - - SSL state per host names of the web app. - returned: always - type: list - sample: [ - { - "hostType": "Standard", - "name": "vxxisurg397winapp4.azurewebsites.net", - "sslState": "Disabled" - }, - { - "hostType": "Repository", - "name": "vxxisurg397winapp4.scm.azurewebsites.net", - "sslState": "Disabled" - } - ] - host_names: - description: - - Host names of the web app. - returned: always - type: list - sample: [ - "vxxisurg397winapp4.azurewebsites.net" - ] - outbound_ip_addresses: - description: - - Outbound IP address of the web app. - returned: always - type: str - sample: "40.71.11.131,40.85.166.200,168.62.166.67,137.135.126.248,137.135.121.45" - ftp_publish_url: - description: - - Publishing URL of the web app when deployment type is FTP. - returned: always - type: str - sample: ftp://xxxx.ftp.azurewebsites.windows.net - state: - description: - - State of the web app. - returned: always - type: str - sample: running - publishing_username: - description: - - Publishing profile user name. - returned: only when I(return_publish_profile=True). - type: str - sample: "$vxxisuRG397winapp4" - publishing_password: - description: - - Publishing profile password. - returned: only when I(return_publish_profile=True). - type: str - sample: "uvANsPQpGjWJmrFfm4Ssd5rpBSqGhjMk11pMSgW2vCsQtNx9tcgZ0xN26s9A" - tags: - description: - - Tags assigned to the resource. Dictionary of string:string pairs. - returned: always - type: dict - sample: { tag1: abc } -''' -try: - from msrestazure.azure_exceptions import CloudError - from msrest.polling import LROPoller - from azure.common import AzureMissingResourceHttpError, AzureHttpError -except Exception: - # This is handled in azure_rm_common - pass - -from ansible.module_utils.azure_rm_common import AzureRMModuleBase - -AZURE_OBJECT_CLASS = 'WebApp' - - -class AzureRMWebAppInfo(AzureRMModuleBase): - - def __init__(self): - - self.module_arg_spec = dict( - name=dict(type='str'), - resource_group=dict(type='str'), - tags=dict(type='list'), - return_publish_profile=dict(type='bool', default=False), - ) - - self.results = dict( - changed=False, - webapps=[], - ) - - self.name = None - self.resource_group = None - self.tags = None - self.return_publish_profile = False - - self.framework_names = ['net_framework', 'java', 'php', 'node', 'python', 'dotnetcore', 'ruby'] - - super(AzureRMWebAppInfo, self).__init__(self.module_arg_spec, - supports_tags=False, - facts_module=True) - - def exec_module(self, **kwargs): - is_old_facts = self.module._name == 'azure_rm_webapp_facts' - if is_old_facts: - self.module.deprecate("The 'azure_rm_webapp_facts' module has been renamed to 'azure_rm_webapp_info'", - version='2.13', collection_name='ansible.builtin') - - for key in self.module_arg_spec: - setattr(self, key, kwargs[key]) - - if self.name: - self.results['webapps'] = self.list_by_name() - elif self.resource_group: - self.results['webapps'] = self.list_by_resource_group() - else: - self.results['webapps'] = self.list_all() - - return self.results - - def list_by_name(self): - self.log('Get web app {0}'.format(self.name)) - item = None - result = [] - - try: - item = self.web_client.web_apps.get(self.resource_group, self.name) - except CloudError: - pass - - if item and self.has_tags(item.tags, self.tags): - curated_result = self.get_curated_webapp(self.resource_group, self.name, item) - result = [curated_result] - - return result - - def list_by_resource_group(self): - self.log('List web apps in resource groups {0}'.format(self.resource_group)) - try: - response = list(self.web_client.web_apps.list_by_resource_group(self.resource_group)) - except CloudError as exc: - request_id = exc.request_id if exc.request_id else '' - self.fail("Error listing web apps in resource groups {0}, request id: {1} - {2}".format(self.resource_group, request_id, str(exc))) - - results = [] - for item in response: - if self.has_tags(item.tags, self.tags): - curated_output = self.get_curated_webapp(self.resource_group, item.name, item) - results.append(curated_output) - return results - - def list_all(self): - self.log('List web apps in current subscription') - try: - response = list(self.web_client.web_apps.list()) - except CloudError as exc: - request_id = exc.request_id if exc.request_id else '' - self.fail("Error listing web apps, request id {0} - {1}".format(request_id, str(exc))) - - results = [] - for item in response: - if self.has_tags(item.tags, self.tags): - curated_output = self.get_curated_webapp(item.resource_group, item.name, item) - results.append(curated_output) - return results - - def list_webapp_configuration(self, resource_group, name): - self.log('Get web app {0} configuration'.format(name)) - - response = [] - - try: - response = self.web_client.web_apps.get_configuration(resource_group_name=resource_group, name=name) - except CloudError as ex: - request_id = ex.request_id if ex.request_id else '' - self.fail('Error getting web app {0} configuration, request id {1} - {2}'.format(name, request_id, str(ex))) - - return response.as_dict() - - def list_webapp_appsettings(self, resource_group, name): - self.log('Get web app {0} app settings'.format(name)) - - response = [] - - try: - response = self.web_client.web_apps.list_application_settings(resource_group_name=resource_group, name=name) - except CloudError as ex: - request_id = ex.request_id if ex.request_id else '' - self.fail('Error getting web app {0} app settings, request id {1} - {2}'.format(name, request_id, str(ex))) - - return response.as_dict() - - def get_publish_credentials(self, resource_group, name): - self.log('Get web app {0} publish credentials'.format(name)) - try: - poller = self.web_client.web_apps.list_publishing_credentials(resource_group, name) - if isinstance(poller, LROPoller): - response = self.get_poller_result(poller) - except CloudError as ex: - request_id = ex.request_id if ex.request_id else '' - self.fail('Error getting web app {0} publishing credentials - {1}'.format(request_id, str(ex))) - return response - - def get_webapp_ftp_publish_url(self, resource_group, name): - import xmltodict - - self.log('Get web app {0} app publish profile'.format(name)) - - url = None - try: - content = self.web_client.web_apps.list_publishing_profile_xml_with_secrets(resource_group_name=resource_group, name=name) - if not content: - return url - - full_xml = '' - for f in content: - full_xml += f.decode() - profiles = xmltodict.parse(full_xml, xml_attribs=True)['publishData']['publishProfile'] - - if not profiles: - return url - - for profile in profiles: - if profile['@publishMethod'] == 'FTP': - url = profile['@publishUrl'] - - except CloudError as ex: - self.fail('Error getting web app {0} app settings'.format(name)) - - return url - - def get_curated_webapp(self, resource_group, name, webapp): - pip = self.serialize_obj(webapp, AZURE_OBJECT_CLASS) - - try: - site_config = self.list_webapp_configuration(resource_group, name) - app_settings = self.list_webapp_appsettings(resource_group, name) - publish_cred = self.get_publish_credentials(resource_group, name) - ftp_publish_url = self.get_webapp_ftp_publish_url(resource_group, name) - except CloudError as ex: - pass - return self.construct_curated_webapp(webapp=pip, - configuration=site_config, - app_settings=app_settings, - deployment_slot=None, - ftp_publish_url=ftp_publish_url, - publish_credentials=publish_cred) - - def construct_curated_webapp(self, - webapp, - configuration=None, - app_settings=None, - deployment_slot=None, - ftp_publish_url=None, - publish_credentials=None): - curated_output = dict() - curated_output['id'] = webapp['id'] - curated_output['name'] = webapp['name'] - curated_output['resource_group'] = webapp['properties']['resourceGroup'] - curated_output['location'] = webapp['location'] - curated_output['plan'] = webapp['properties']['serverFarmId'] - curated_output['tags'] = webapp.get('tags', None) - - # important properties from output. not match input arguments. - curated_output['app_state'] = webapp['properties']['state'] - curated_output['availability_state'] = webapp['properties']['availabilityState'] - curated_output['default_host_name'] = webapp['properties']['defaultHostName'] - curated_output['host_names'] = webapp['properties']['hostNames'] - curated_output['enabled'] = webapp['properties']['enabled'] - curated_output['enabled_host_names'] = webapp['properties']['enabledHostNames'] - curated_output['host_name_ssl_states'] = webapp['properties']['hostNameSslStates'] - curated_output['outbound_ip_addresses'] = webapp['properties']['outboundIpAddresses'] - - # curated site_config - if configuration: - curated_output['frameworks'] = [] - for fx_name in self.framework_names: - fx_version = configuration.get(fx_name + '_version', None) - if fx_version: - fx = { - 'name': fx_name, - 'version': fx_version - } - # java container setting - if fx_name == 'java': - if configuration['java_container'] and configuration['java_container_version']: - settings = { - 'java_container': configuration['java_container'].lower(), - 'java_container_version': configuration['java_container_version'] - } - fx['settings'] = settings - - curated_output['frameworks'].append(fx) - - # linux_fx_version - if configuration.get('linux_fx_version', None): - tmp = configuration.get('linux_fx_version').split("|") - if len(tmp) == 2: - curated_output['frameworks'].append({'name': tmp[0].lower(), 'version': tmp[1]}) - - # curated app_settings - if app_settings and app_settings.get('properties', None): - curated_output['app_settings'] = dict() - for item in app_settings['properties']: - curated_output['app_settings'][item] = app_settings['properties'][item] - - # curated deploymenet_slot - if deployment_slot: - curated_output['deployment_slot'] = deployment_slot - - # ftp_publish_url - if ftp_publish_url: - curated_output['ftp_publish_url'] = ftp_publish_url - - # curated publish credentials - if publish_credentials and self.return_publish_profile: - curated_output['publishing_username'] = publish_credentials.publishing_user_name - curated_output['publishing_password'] = publish_credentials.publishing_password - return curated_output - - -def main(): - AzureRMWebAppInfo() - - -if __name__ == '__main__': - main() diff --git a/test/support/integration/plugins/modules/azure_rm_webappslot.py b/test/support/integration/plugins/modules/azure_rm_webappslot.py deleted file mode 100644 index ddba710b9d7a2c..00000000000000 --- a/test/support/integration/plugins/modules/azure_rm_webappslot.py +++ /dev/null @@ -1,1058 +0,0 @@ -#!/usr/bin/python -# -# Copyright (c) 2018 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_webappslot -version_added: "2.8" -short_description: Manage Azure Web App slot -description: - - Create, update and delete Azure Web App slot. - -options: - resource_group: - description: - - Name of the resource group to which the resource belongs. - required: True - name: - description: - - Unique name of the deployment slot to create or update. - required: True - webapp_name: - description: - - Web app name which this deployment slot belongs to. - required: True - location: - description: - - Resource location. If not set, location from the resource group will be used as default. - configuration_source: - description: - - Source slot to clone configurations from when creating slot. Use webapp's name to refer to the production slot. - auto_swap_slot_name: - description: - - Used to configure target slot name to auto swap, or disable auto swap. - - Set it target slot name to auto swap. - - Set it to False to disable auto slot swap. - swap: - description: - - Swap deployment slots of a web app. - suboptions: - action: - description: - - Swap types. - - C(preview) is to apply target slot settings on source slot first. - - C(swap) is to complete swapping. - - C(reset) is to reset the swap. - choices: - - preview - - swap - - reset - default: preview - target_slot: - description: - - Name of target slot to swap. If set to None, then swap with production slot. - preserve_vnet: - description: - - C(True) to preserve virtual network to the slot during swap. Otherwise C(False). - type: bool - default: True - frameworks: - description: - - Set of run time framework settings. Each setting is a dictionary. - - See U(https://docs.microsoft.com/en-us/azure/app-service/app-service-web-overview) for more info. - suboptions: - name: - description: - - Name of the framework. - - Supported framework list for Windows web app and Linux web app is different. - - Windows web apps support C(java), C(net_framework), C(php), C(python), and C(node) from June 2018. - - Windows web apps support multiple framework at same time. - - Linux web apps support C(java), C(ruby), C(php), C(dotnetcore), and C(node) from June 2018. - - Linux web apps support only one framework. - - Java framework is mutually exclusive with others. - choices: - - java - - net_framework - - php - - python - - ruby - - dotnetcore - - node - version: - description: - - Version of the framework. For Linux web app supported value, see U(https://aka.ms/linux-stacks) for more info. - - C(net_framework) supported value sample, C(v4.0) for .NET 4.6 and C(v3.0) for .NET 3.5. - - C(php) supported value sample, C(5.5), C(5.6), C(7.0). - - C(python) supported value sample, C(5.5), C(5.6), C(7.0). - - C(node) supported value sample, C(6.6), C(6.9). - - C(dotnetcore) supported value sample, C(1.0), C(1.1), C(1.2). - - C(ruby) supported value sample, 2.3. - - C(java) supported value sample, C(1.9) for Windows web app. C(1.8) for Linux web app. - settings: - description: - - List of settings of the framework. - suboptions: - java_container: - description: - - Name of Java container. This is supported by specific framework C(java) onlys, for example C(Tomcat), C(Jetty). - java_container_version: - description: - - Version of Java container. This is supported by specific framework C(java) only. - - For C(Tomcat), for example C(8.0), C(8.5), C(9.0). For C(Jetty), for example C(9.1), C(9.3). - container_settings: - description: - - Web app slot container settings. - suboptions: - name: - description: - - Name of container, for example C(imagename:tag). - registry_server_url: - description: - - Container registry server URL, for example C(mydockerregistry.io). - registry_server_user: - description: - - The container registry server user name. - registry_server_password: - description: - - The container registry server password. - startup_file: - description: - - The slot startup file. - - This only applies for Linux web app slot. - app_settings: - description: - - Configure web app slot application settings. Suboptions are in key value pair format. - purge_app_settings: - description: - - Purge any existing application settings. Replace slot application settings with app_settings. - type: bool - deployment_source: - description: - - Deployment source for git. - suboptions: - url: - description: - - Repository URL of deployment source. - branch: - description: - - The branch name of the repository. - app_state: - description: - - Start/Stop/Restart the slot. - type: str - choices: - - started - - stopped - - restarted - default: started - state: - description: - - State of the Web App deployment slot. - - Use C(present) to create or update a slot and C(absent) to delete it. - default: present - choices: - - absent - - present - -extends_documentation_fragment: - - azure - - azure_tags - -author: - - Yunge Zhu(@yungezz) - -''' - -EXAMPLES = ''' - - name: Create a webapp slot - azure_rm_webappslot: - resource_group: myResourceGroup - webapp_name: myJavaWebApp - name: stage - configuration_source: myJavaWebApp - app_settings: - testkey: testvalue - - - name: swap the slot with production slot - azure_rm_webappslot: - resource_group: myResourceGroup - webapp_name: myJavaWebApp - name: stage - swap: - action: swap - - - name: stop the slot - azure_rm_webappslot: - resource_group: myResourceGroup - webapp_name: myJavaWebApp - name: stage - app_state: stopped - - - name: udpate a webapp slot app settings - azure_rm_webappslot: - resource_group: myResourceGroup - webapp_name: myJavaWebApp - name: stage - app_settings: - testkey: testvalue2 - - - name: udpate a webapp slot frameworks - azure_rm_webappslot: - resource_group: myResourceGroup - webapp_name: myJavaWebApp - name: stage - frameworks: - - name: "node" - version: "10.1" -''' - -RETURN = ''' -id: - description: - - ID of current slot. - returned: always - type: str - sample: /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/Microsoft.Web/sites/testapp/slots/stage1 -''' - -import time -from ansible.module_utils.azure_rm_common import AzureRMModuleBase - -try: - from msrestazure.azure_exceptions import CloudError - from msrest.polling import LROPoller - from msrest.serialization import Model - from azure.mgmt.web.models import ( - site_config, app_service_plan, Site, - AppServicePlan, SkuDescription, NameValuePair - ) -except ImportError: - # This is handled in azure_rm_common - pass - -swap_spec = dict( - action=dict( - type='str', - choices=[ - 'preview', - 'swap', - 'reset' - ], - default='preview' - ), - target_slot=dict( - type='str' - ), - preserve_vnet=dict( - type='bool', - default=True - ) -) - -container_settings_spec = dict( - name=dict(type='str', required=True), - registry_server_url=dict(type='str'), - registry_server_user=dict(type='str'), - registry_server_password=dict(type='str', no_log=True) -) - -deployment_source_spec = dict( - url=dict(type='str'), - branch=dict(type='str') -) - - -framework_settings_spec = dict( - java_container=dict(type='str', required=True), - java_container_version=dict(type='str', required=True) -) - - -framework_spec = dict( - name=dict( - type='str', - required=True, - choices=['net_framework', 'java', 'php', 'node', 'python', 'dotnetcore', 'ruby']), - version=dict(type='str', required=True), - settings=dict(type='dict', options=framework_settings_spec) -) - - -def webapp_to_dict(webapp): - return dict( - id=webapp.id, - name=webapp.name, - location=webapp.location, - client_cert_enabled=webapp.client_cert_enabled, - enabled=webapp.enabled, - reserved=webapp.reserved, - client_affinity_enabled=webapp.client_affinity_enabled, - server_farm_id=webapp.server_farm_id, - host_names_disabled=webapp.host_names_disabled, - https_only=webapp.https_only if hasattr(webapp, 'https_only') else None, - skip_custom_domain_verification=webapp.skip_custom_domain_verification if hasattr(webapp, 'skip_custom_domain_verification') else None, - ttl_in_seconds=webapp.ttl_in_seconds if hasattr(webapp, 'ttl_in_seconds') else None, - state=webapp.state, - tags=webapp.tags if webapp.tags else None - ) - - -def slot_to_dict(slot): - return dict( - id=slot.id, - resource_group=slot.resource_group, - server_farm_id=slot.server_farm_id, - target_swap_slot=slot.target_swap_slot, - enabled_host_names=slot.enabled_host_names, - slot_swap_status=slot.slot_swap_status, - name=slot.name, - location=slot.location, - enabled=slot.enabled, - reserved=slot.reserved, - host_names_disabled=slot.host_names_disabled, - state=slot.state, - repository_site_name=slot.repository_site_name, - default_host_name=slot.default_host_name, - kind=slot.kind, - site_config=slot.site_config, - tags=slot.tags if slot.tags else None - ) - - -class Actions: - NoAction, CreateOrUpdate, UpdateAppSettings, Delete = range(4) - - -class AzureRMWebAppSlots(AzureRMModuleBase): - """Configuration class for an Azure RM Web App slot resource""" - - def __init__(self): - self.module_arg_spec = dict( - resource_group=dict( - type='str', - required=True - ), - name=dict( - type='str', - required=True - ), - webapp_name=dict( - type='str', - required=True - ), - location=dict( - type='str' - ), - configuration_source=dict( - type='str' - ), - auto_swap_slot_name=dict( - type='raw' - ), - swap=dict( - type='dict', - options=swap_spec - ), - frameworks=dict( - type='list', - elements='dict', - options=framework_spec - ), - container_settings=dict( - type='dict', - options=container_settings_spec - ), - deployment_source=dict( - type='dict', - options=deployment_source_spec - ), - startup_file=dict( - type='str' - ), - app_settings=dict( - type='dict' - ), - purge_app_settings=dict( - type='bool', - default=False - ), - app_state=dict( - type='str', - choices=['started', 'stopped', 'restarted'], - default='started' - ), - state=dict( - type='str', - default='present', - choices=['present', 'absent'] - ) - ) - - mutually_exclusive = [['container_settings', 'frameworks']] - - self.resource_group = None - self.name = None - self.webapp_name = None - self.location = None - - self.auto_swap_slot_name = None - self.swap = None - self.tags = None - self.startup_file = None - self.configuration_source = None - self.clone = False - - # site config, e.g app settings, ssl - self.site_config = dict() - self.app_settings = dict() - self.app_settings_strDic = None - - # siteSourceControl - self.deployment_source = dict() - - # site, used at level creation, or update. - self.site = None - - # property for internal usage, not used for sdk - self.container_settings = None - - self.purge_app_settings = False - self.app_state = 'started' - - self.results = dict( - changed=False, - id=None, - ) - self.state = None - self.to_do = Actions.NoAction - - self.frameworks = None - - # set site_config value from kwargs - self.site_config_updatable_frameworks = ["net_framework_version", - "java_version", - "php_version", - "python_version", - "linux_fx_version"] - - self.supported_linux_frameworks = ['ruby', 'php', 'dotnetcore', 'node', 'java'] - self.supported_windows_frameworks = ['net_framework', 'php', 'python', 'node', 'java'] - - super(AzureRMWebAppSlots, self).__init__(derived_arg_spec=self.module_arg_spec, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True, - supports_tags=True) - - def exec_module(self, **kwargs): - """Main module execution method""" - - for key in list(self.module_arg_spec.keys()) + ['tags']: - if hasattr(self, key): - setattr(self, key, kwargs[key]) - elif kwargs[key] is not None: - if key == "scm_type": - self.site_config[key] = kwargs[key] - - old_response = None - response = None - to_be_updated = False - - # set location - resource_group = self.get_resource_group(self.resource_group) - if not self.location: - self.location = resource_group.location - - # get web app - webapp_response = self.get_webapp() - - if not webapp_response: - self.fail("Web app {0} does not exist in resource group {1}.".format(self.webapp_name, self.resource_group)) - - # get slot - old_response = self.get_slot() - - # set is_linux - is_linux = True if webapp_response['reserved'] else False - - if self.state == 'present': - if self.frameworks: - # java is mutually exclusive with other frameworks - if len(self.frameworks) > 1 and any(f['name'] == 'java' for f in self.frameworks): - self.fail('Java is mutually exclusive with other frameworks.') - - if is_linux: - if len(self.frameworks) != 1: - self.fail('Can specify one framework only for Linux web app.') - - if self.frameworks[0]['name'] not in self.supported_linux_frameworks: - self.fail('Unsupported framework {0} for Linux web app.'.format(self.frameworks[0]['name'])) - - self.site_config['linux_fx_version'] = (self.frameworks[0]['name'] + '|' + self.frameworks[0]['version']).upper() - - if self.frameworks[0]['name'] == 'java': - if self.frameworks[0]['version'] != '8': - self.fail("Linux web app only supports java 8.") - - if self.frameworks[0].get('settings', {}) and self.frameworks[0]['settings'].get('java_container', None) and \ - self.frameworks[0]['settings']['java_container'].lower() != 'tomcat': - self.fail("Linux web app only supports tomcat container.") - - if self.frameworks[0].get('settings', {}) and self.frameworks[0]['settings'].get('java_container', None) and \ - self.frameworks[0]['settings']['java_container'].lower() == 'tomcat': - self.site_config['linux_fx_version'] = 'TOMCAT|' + self.frameworks[0]['settings']['java_container_version'] + '-jre8' - else: - self.site_config['linux_fx_version'] = 'JAVA|8-jre8' - else: - for fx in self.frameworks: - if fx.get('name') not in self.supported_windows_frameworks: - self.fail('Unsupported framework {0} for Windows web app.'.format(fx.get('name'))) - else: - self.site_config[fx.get('name') + '_version'] = fx.get('version') - - if 'settings' in fx and fx['settings'] is not None: - for key, value in fx['settings'].items(): - self.site_config[key] = value - - if not self.app_settings: - self.app_settings = dict() - - if self.container_settings: - linux_fx_version = 'DOCKER|' - - if self.container_settings.get('registry_server_url'): - self.app_settings['DOCKER_REGISTRY_SERVER_URL'] = 'https://' + self.container_settings['registry_server_url'] - - linux_fx_version += self.container_settings['registry_server_url'] + '/' - - linux_fx_version += self.container_settings['name'] - - self.site_config['linux_fx_version'] = linux_fx_version - - if self.container_settings.get('registry_server_user'): - self.app_settings['DOCKER_REGISTRY_SERVER_USERNAME'] = self.container_settings['registry_server_user'] - - if self.container_settings.get('registry_server_password'): - self.app_settings['DOCKER_REGISTRY_SERVER_PASSWORD'] = self.container_settings['registry_server_password'] - - # set auto_swap_slot_name - if self.auto_swap_slot_name and isinstance(self.auto_swap_slot_name, str): - self.site_config['auto_swap_slot_name'] = self.auto_swap_slot_name - if self.auto_swap_slot_name is False: - self.site_config['auto_swap_slot_name'] = None - - # init site - self.site = Site(location=self.location, site_config=self.site_config) - - # check if the slot already present in the webapp - if not old_response: - self.log("Web App slot doesn't exist") - - to_be_updated = True - self.to_do = Actions.CreateOrUpdate - self.site.tags = self.tags - - # if linux, setup startup_file - if self.startup_file: - self.site_config['app_command_line'] = self.startup_file - - # set app setting - if self.app_settings: - app_settings = [] - for key in self.app_settings.keys(): - app_settings.append(NameValuePair(name=key, value=self.app_settings[key])) - - self.site_config['app_settings'] = app_settings - - # clone slot - if self.configuration_source: - self.clone = True - - else: - # existing slot, do update - self.log("Web App slot already exists") - - self.log('Result: {0}'.format(old_response)) - - update_tags, self.site.tags = self.update_tags(old_response.get('tags', None)) - - if update_tags: - to_be_updated = True - - # check if site_config changed - old_config = self.get_configuration_slot(self.name) - - if self.is_site_config_changed(old_config): - to_be_updated = True - self.to_do = Actions.CreateOrUpdate - - self.app_settings_strDic = self.list_app_settings_slot(self.name) - - # purge existing app_settings: - if self.purge_app_settings: - to_be_updated = True - self.to_do = Actions.UpdateAppSettings - self.app_settings_strDic = dict() - - # check if app settings changed - if self.purge_app_settings or self.is_app_settings_changed(): - to_be_updated = True - self.to_do = Actions.UpdateAppSettings - - if self.app_settings: - for key in self.app_settings.keys(): - self.app_settings_strDic[key] = self.app_settings[key] - - elif self.state == 'absent': - if old_response: - self.log("Delete Web App slot") - self.results['changed'] = True - - if self.check_mode: - return self.results - - self.delete_slot() - - self.log('Web App slot deleted') - - else: - self.log("Web app slot {0} not exists.".format(self.name)) - - if to_be_updated: - self.log('Need to Create/Update web app') - self.results['changed'] = True - - if self.check_mode: - return self.results - - if self.to_do == Actions.CreateOrUpdate: - response = self.create_update_slot() - - self.results['id'] = response['id'] - - if self.clone: - self.clone_slot() - - if self.to_do == Actions.UpdateAppSettings: - self.update_app_settings_slot() - - slot = None - if response: - slot = response - if old_response: - slot = old_response - - if slot: - if (slot['state'] != 'Stopped' and self.app_state == 'stopped') or \ - (slot['state'] != 'Running' and self.app_state == 'started') or \ - self.app_state == 'restarted': - - self.results['changed'] = True - if self.check_mode: - return self.results - - self.set_state_slot(self.app_state) - - if self.swap: - self.results['changed'] = True - if self.check_mode: - return self.results - - self.swap_slot() - - return self.results - - # compare site config - def is_site_config_changed(self, existing_config): - for fx_version in self.site_config_updatable_frameworks: - if self.site_config.get(fx_version): - if not getattr(existing_config, fx_version) or \ - getattr(existing_config, fx_version).upper() != self.site_config.get(fx_version).upper(): - return True - - if self.auto_swap_slot_name is False and existing_config.auto_swap_slot_name is not None: - return True - elif self.auto_swap_slot_name and self.auto_swap_slot_name != getattr(existing_config, 'auto_swap_slot_name', None): - return True - return False - - # comparing existing app setting with input, determine whether it's changed - def is_app_settings_changed(self): - if self.app_settings: - if len(self.app_settings_strDic) != len(self.app_settings): - return True - - if self.app_settings_strDic != self.app_settings: - return True - return False - - # comparing deployment source with input, determine whether it's changed - def is_deployment_source_changed(self, existing_webapp): - if self.deployment_source: - if self.deployment_source.get('url') \ - and self.deployment_source['url'] != existing_webapp.get('site_source_control')['url']: - return True - - if self.deployment_source.get('branch') \ - and self.deployment_source['branch'] != existing_webapp.get('site_source_control')['branch']: - return True - - return False - - def create_update_slot(self): - ''' - Creates or updates Web App slot with the specified configuration. - - :return: deserialized Web App instance state dictionary - ''' - self.log( - "Creating / Updating the Web App slot {0}".format(self.name)) - - try: - response = self.web_client.web_apps.create_or_update_slot(resource_group_name=self.resource_group, - slot=self.name, - name=self.webapp_name, - site_envelope=self.site) - if isinstance(response, LROPoller): - response = self.get_poller_result(response) - - except CloudError as exc: - self.log('Error attempting to create the Web App slot instance.') - self.fail("Error creating the Web App slot: {0}".format(str(exc))) - return slot_to_dict(response) - - def delete_slot(self): - ''' - Deletes specified Web App slot in the specified subscription and resource group. - - :return: True - ''' - self.log("Deleting the Web App slot {0}".format(self.name)) - try: - response = self.web_client.web_apps.delete_slot(resource_group_name=self.resource_group, - name=self.webapp_name, - slot=self.name) - except CloudError as e: - self.log('Error attempting to delete the Web App slot.') - self.fail( - "Error deleting the Web App slots: {0}".format(str(e))) - - return True - - def get_webapp(self): - ''' - Gets the properties of the specified Web App. - - :return: deserialized Web App instance state dictionary - ''' - self.log( - "Checking if the Web App instance {0} is present".format(self.webapp_name)) - - response = None - - try: - response = self.web_client.web_apps.get(resource_group_name=self.resource_group, - name=self.webapp_name) - - # Newer SDK versions (0.40.0+) seem to return None if it doesn't exist instead of raising CloudError - if response is not None: - self.log("Response : {0}".format(response)) - self.log("Web App instance : {0} found".format(response.name)) - return webapp_to_dict(response) - - except CloudError as ex: - pass - - self.log("Didn't find web app {0} in resource group {1}".format( - self.webapp_name, self.resource_group)) - - return False - - def get_slot(self): - ''' - Gets the properties of the specified Web App slot. - - :return: deserialized Web App slot state dictionary - ''' - self.log( - "Checking if the Web App slot {0} is present".format(self.name)) - - response = None - - try: - response = self.web_client.web_apps.get_slot(resource_group_name=self.resource_group, - name=self.webapp_name, - slot=self.name) - - # Newer SDK versions (0.40.0+) seem to return None if it doesn't exist instead of raising CloudError - if response is not None: - self.log("Response : {0}".format(response)) - self.log("Web App slot: {0} found".format(response.name)) - return slot_to_dict(response) - - except CloudError as ex: - pass - - self.log("Does not find web app slot {0} in resource group {1}".format(self.name, self.resource_group)) - - return False - - def list_app_settings(self): - ''' - List webapp application settings - :return: deserialized list response - ''' - self.log("List webapp application setting") - - try: - - response = self.web_client.web_apps.list_application_settings( - resource_group_name=self.resource_group, name=self.webapp_name) - self.log("Response : {0}".format(response)) - - return response.properties - except CloudError as ex: - self.fail("Failed to list application settings for web app {0} in resource group {1}: {2}".format( - self.name, self.resource_group, str(ex))) - - def list_app_settings_slot(self, slot_name): - ''' - List application settings - :return: deserialized list response - ''' - self.log("List application setting") - - try: - - response = self.web_client.web_apps.list_application_settings_slot( - resource_group_name=self.resource_group, name=self.webapp_name, slot=slot_name) - self.log("Response : {0}".format(response)) - - return response.properties - except CloudError as ex: - self.fail("Failed to list application settings for web app slot {0} in resource group {1}: {2}".format( - self.name, self.resource_group, str(ex))) - - def update_app_settings_slot(self, slot_name=None, app_settings=None): - ''' - Update application settings - :return: deserialized updating response - ''' - self.log("Update application setting") - - if slot_name is None: - slot_name = self.name - if app_settings is None: - app_settings = self.app_settings_strDic - try: - response = self.web_client.web_apps.update_application_settings_slot(resource_group_name=self.resource_group, - name=self.webapp_name, - slot=slot_name, - kind=None, - properties=app_settings) - self.log("Response : {0}".format(response)) - - return response.as_dict() - except CloudError as ex: - self.fail("Failed to update application settings for web app slot {0} in resource group {1}: {2}".format( - self.name, self.resource_group, str(ex))) - - return response - - def create_or_update_source_control_slot(self): - ''' - Update site source control - :return: deserialized updating response - ''' - self.log("Update site source control") - - if self.deployment_source is None: - return False - - self.deployment_source['is_manual_integration'] = False - self.deployment_source['is_mercurial'] = False - - try: - response = self.web_client.web_client.create_or_update_source_control_slot( - resource_group_name=self.resource_group, - name=self.webapp_name, - site_source_control=self.deployment_source, - slot=self.name) - self.log("Response : {0}".format(response)) - - return response.as_dict() - except CloudError as ex: - self.fail("Failed to update site source control for web app slot {0} in resource group {1}: {2}".format( - self.name, self.resource_group, str(ex))) - - def get_configuration(self): - ''' - Get web app configuration - :return: deserialized web app configuration response - ''' - self.log("Get web app configuration") - - try: - - response = self.web_client.web_apps.get_configuration( - resource_group_name=self.resource_group, name=self.webapp_name) - self.log("Response : {0}".format(response)) - - return response - except CloudError as ex: - self.fail("Failed to get configuration for web app {0} in resource group {1}: {2}".format( - self.webapp_name, self.resource_group, str(ex))) - - def get_configuration_slot(self, slot_name): - ''' - Get slot configuration - :return: deserialized slot configuration response - ''' - self.log("Get web app slot configuration") - - try: - - response = self.web_client.web_apps.get_configuration_slot( - resource_group_name=self.resource_group, name=self.webapp_name, slot=slot_name) - self.log("Response : {0}".format(response)) - - return response - except CloudError as ex: - self.fail("Failed to get configuration for web app slot {0} in resource group {1}: {2}".format( - slot_name, self.resource_group, str(ex))) - - def update_configuration_slot(self, slot_name=None, site_config=None): - ''' - Update slot configuration - :return: deserialized slot configuration response - ''' - self.log("Update web app slot configuration") - - if slot_name is None: - slot_name = self.name - if site_config is None: - site_config = self.site_config - try: - - response = self.web_client.web_apps.update_configuration_slot( - resource_group_name=self.resource_group, name=self.webapp_name, slot=slot_name, site_config=site_config) - self.log("Response : {0}".format(response)) - - return response - except CloudError as ex: - self.fail("Failed to update configuration for web app slot {0} in resource group {1}: {2}".format( - slot_name, self.resource_group, str(ex))) - - def set_state_slot(self, appstate): - ''' - Start/stop/restart web app slot - :return: deserialized updating response - ''' - try: - if appstate == 'started': - response = self.web_client.web_apps.start_slot(resource_group_name=self.resource_group, name=self.webapp_name, slot=self.name) - elif appstate == 'stopped': - response = self.web_client.web_apps.stop_slot(resource_group_name=self.resource_group, name=self.webapp_name, slot=self.name) - elif appstate == 'restarted': - response = self.web_client.web_apps.restart_slot(resource_group_name=self.resource_group, name=self.webapp_name, slot=self.name) - else: - self.fail("Invalid web app slot state {0}".format(appstate)) - - self.log("Response : {0}".format(response)) - - return response - except CloudError as ex: - request_id = ex.request_id if ex.request_id else '' - self.fail("Failed to {0} web app slot {1} in resource group {2}, request_id {3} - {4}".format( - appstate, self.name, self.resource_group, request_id, str(ex))) - - def swap_slot(self): - ''' - Swap slot - :return: deserialized response - ''' - self.log("Swap slot") - - try: - if self.swap['action'] == 'swap': - if self.swap['target_slot'] is None: - response = self.web_client.web_apps.swap_slot_with_production(resource_group_name=self.resource_group, - name=self.webapp_name, - target_slot=self.name, - preserve_vnet=self.swap['preserve_vnet']) - else: - response = self.web_client.web_apps.swap_slot_slot(resource_group_name=self.resource_group, - name=self.webapp_name, - slot=self.name, - target_slot=self.swap['target_slot'], - preserve_vnet=self.swap['preserve_vnet']) - elif self.swap['action'] == 'preview': - if self.swap['target_slot'] is None: - response = self.web_client.web_apps.apply_slot_config_to_production(resource_group_name=self.resource_group, - name=self.webapp_name, - target_slot=self.name, - preserve_vnet=self.swap['preserve_vnet']) - else: - response = self.web_client.web_apps.apply_slot_configuration_slot(resource_group_name=self.resource_group, - name=self.webapp_name, - slot=self.name, - target_slot=self.swap['target_slot'], - preserve_vnet=self.swap['preserve_vnet']) - elif self.swap['action'] == 'reset': - if self.swap['target_slot'] is None: - response = self.web_client.web_apps.reset_production_slot_config(resource_group_name=self.resource_group, - name=self.webapp_name) - else: - response = self.web_client.web_apps.reset_slot_configuration_slot(resource_group_name=self.resource_group, - name=self.webapp_name, - slot=self.swap['target_slot']) - response = self.web_client.web_apps.reset_slot_configuration_slot(resource_group_name=self.resource_group, - name=self.webapp_name, - slot=self.name) - - self.log("Response : {0}".format(response)) - - return response - except CloudError as ex: - self.fail("Failed to swap web app slot {0} in resource group {1}: {2}".format(self.name, self.resource_group, str(ex))) - - def clone_slot(self): - if self.configuration_source: - src_slot = None if self.configuration_source.lower() == self.webapp_name.lower() else self.configuration_source - - if src_slot is None: - site_config_clone_from = self.get_configuration() - else: - site_config_clone_from = self.get_configuration_slot(slot_name=src_slot) - - self.update_configuration_slot(site_config=site_config_clone_from) - - if src_slot is None: - app_setting_clone_from = self.list_app_settings() - else: - app_setting_clone_from = self.list_app_settings_slot(src_slot) - - if self.app_settings: - app_setting_clone_from.update(self.app_settings) - - self.update_app_settings_slot(app_settings=app_setting_clone_from) - - -def main(): - """Main execution""" - AzureRMWebAppSlots() - - -if __name__ == '__main__': - main() diff --git a/test/utils/shippable/incidental/azure.sh b/test/utils/shippable/incidental/azure.sh deleted file mode 120000 index 700ad3edcff119..00000000000000 --- a/test/utils/shippable/incidental/azure.sh +++ /dev/null @@ -1 +0,0 @@ -cloud.sh \ No newline at end of file From f5e3c04d58d2de481ac6e2d3f59ef81625e05779 Mon Sep 17 00:00:00 2001 From: Sam Doran Date: Wed, 12 May 2021 16:27:43 -0400 Subject: [PATCH 3/3] Remove incidental Azure tests from matrix --- .azure-pipelines/azure-pipelines.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.azure-pipelines/azure-pipelines.yml b/.azure-pipelines/azure-pipelines.yml index 564d405dd57f9d..25a93aa2347332 100644 --- a/.azure-pipelines/azure-pipelines.yml +++ b/.azure-pipelines/azure-pipelines.yml @@ -232,10 +232,6 @@ stages: test: aws/2.7 - name: AWS Python 3.6 test: aws/3.6 - - name: Azure Python 2.7 - test: azure/2.7 - - name: Azure Python 3.6 - test: azure/3.6 - name: Cloud Python test: cloud/ - stage: Summary