diff --git a/lib/ansible/module_utils/azure_rm_common.py b/lib/ansible/module_utils/azure_rm_common.py index 95d0c7104a..8c234bfdd4 100644 --- a/lib/ansible/module_utils/azure_rm_common.py +++ b/lib/ansible/module_utils/azure_rm_common.py @@ -31,6 +31,10 @@ from os.path import expanduser from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.six.moves import configparser import ansible.module_utils.six.moves.urllib.parse as urlparse +try: + from ansible.release import __version__ as ANSIBLE_VERSION +except ImportError: + ANSIBLE_VERSION = 'unknown' AZURE_COMMON_ARGS = dict( cli_default_profile=dict(type='bool'), @@ -66,7 +70,8 @@ AZURE_COMMON_REQUIRED_IF = [ ('log_mode', 'file', ['log_path']) ] -ANSIBLE_USER_AGENT = 'Ansible-Deploy' +ANSIBLE_USER_AGENT = 'Ansible/{0}'.format(ANSIBLE_VERSION) +CLOUDSHELL_USER_AGENT_KEY = 'AZURE_HTTP_USER_AGENT' CIDR_PATTERN = re.compile("(([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" "[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))") @@ -132,20 +137,44 @@ def azure_id_to_dict(id): return result -AZURE_EXPECTED_VERSIONS = dict( - storage_client_version="1.0.0", - compute_client_version="1.0.0", - network_client_version="1.0.0", - resource_client_version="1.1.0", - dns_client_version="1.0.1", - web_client_version="0.32.0" -) +AZURE_PKG_VERSIONS = { + StorageManagementClient.__name__: { + 'package_name': 'storage', + 'expected_version': '1.0.0', + 'installed_version': storage_client_version + }, + ComputeManagementClient.__name__: { + 'package_name': 'compute', + 'expected_version': '1.0.0', + 'installed_version': compute_client_version + }, + NetworkManagementClient.__name__: { + 'package_name': 'network', + 'expected_version': '1.0.0', + 'installed_version': network_client_version + }, + ResourceManagementClient.__name__: { + 'package_name': 'resource', + 'expected_version': '1.1.0', + 'installed_version': resource_client_version + }, + DnsManagementClient.__name__: { + 'package_name': 'dns', + 'expected_version': '1.0.1', + 'installed_version': dns_client_version + }, + WebSiteManagementClient.__name__: { + 'package_name': 'web', + 'expected_version': '0.32.0', + 'installed_version': web_client_version + }, +} 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, check_invalid_arguments=True, mutually_exclusive=None, required_together=None, required_one_of=None, add_file_common_args=False, supports_check_mode=False, @@ -256,12 +285,17 @@ class AzureRMModuleBase(object): res = self.exec_module(**self.module.params) self.module.exit_json(**res) - def check_client_version(self, client_name, client_version, expected_version): + def check_client_version(self, client_type): # Ensure Azure modules are at least 2.0.0rc5. - if Version(client_version) < Version(expected_version): - self.fail("Installed {0} client version is {1}. The supported version is {2}. Try " - "`pip install azure>={3} --upgrade`".format(client_name, client_version, expected_version, - AZURE_MIN_RELEASE)) + package_version = AZURE_PKG_VERSIONS.get(client_type.__name__, None) + if package_version is not None: + client_name = package_version.get('package_name') + client_version = package_version.get('installed_version') + expected_version = package_version.get('expected_version') + if Version(client_version) < Version(expected_version): + self.fail("Installed {0} client version is {1}. The supported version is {2}. Try " + "`pip install azure>={3} --upgrade`".format(client_name, client_version, expected_version, + AZURE_MIN_RELEASE)) def exec_module(self, **kwargs): self.fail("Error: {0} failed to implement exec_module method.".format(self.__class__.__name__)) @@ -652,107 +686,81 @@ class AzureRMModuleBase(object): return self.get_poller_result(poller) - def _register(self, key): - try: - # We have to perform the one-time registration here. Otherwise, we receive an error the first - # time we attempt to use the requested client. - resource_client = self.rm_client - resource_client.providers.register(key) - except Exception as exc: - self.log("One-time registration of {0} failed - {1}".format(key, str(exc))) - self.log("You might need to register {0} using an admin account".format(key)) - self.log(("To register a provider using the Python CLI: " - "https://docs.microsoft.com/azure/azure-resource-manager/" - "resource-manager-common-deployment-errors#noregisteredproviderfound")) + 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) + if api_version: + client = client_type(self.azure_credentials, + self.subscription_id, + api_version=api_version, + base_url=base_url) + else: + client = client_type(self.azure_credentials, + self.subscription_id, + base_url=base_url) + + # Add user agent for Ansible + client.config.add_user_agent(ANSIBLE_USER_AGENT) + # Add user agent when running from Cloud Shell + if CLOUDSHELL_USER_AGENT_KEY in os.environ: + client.config.add_user_agent(os.environ[CLOUDSHELL_USER_AGENT_KEY]) + + return client @property def storage_client(self): self.log('Getting storage client...') if not self._storage_client: - self.check_client_version('storage', storage_client_version, AZURE_EXPECTED_VERSIONS['storage_client_version']) - self._storage_client = StorageManagementClient( - self.azure_credentials, - self.subscription_id, - base_url=self._cloud_environment.endpoints.resource_manager, - api_version='2017-06-01' - ) - self._register('Microsoft.Storage') + self._storage_client = self.get_mgmt_svc_client(StorageManagementClient, + base_url=self._cloud_environment.endpoints.resource_manager, + api_version='2017-06-01') return self._storage_client @property def network_client(self): self.log('Getting network client') if not self._network_client: - self.check_client_version('network', network_client_version, AZURE_EXPECTED_VERSIONS['network_client_version']) - self._network_client = NetworkManagementClient( - self.azure_credentials, - self.subscription_id, - base_url=self._cloud_environment.endpoints.resource_manager, - api_version='2017-06-01' - ) - self._register('Microsoft.Network') + self._network_client = self.get_mgmt_svc_client(NetworkManagementClient, + base_url=self._cloud_environment.endpoints.resource_manager, + api_version='2017-06-01') return self._network_client @property def rm_client(self): self.log('Getting resource manager client') if not self._resource_client: - self.check_client_version('resource', resource_client_version, AZURE_EXPECTED_VERSIONS['resource_client_version']) - self._resource_client = ResourceManagementClient( - self.azure_credentials, - self.subscription_id, - base_url=self._cloud_environment.endpoints.resource_manager, - api_version='2017-05-10' - ) + 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 compute_client(self): self.log('Getting compute client') if not self._compute_client: - self.check_client_version('compute', compute_client_version, AZURE_EXPECTED_VERSIONS['compute_client_version']) - self._compute_client = ComputeManagementClient( - self.azure_credentials, - self.subscription_id, - base_url=self._cloud_environment.endpoints.resource_manager, - api_version='2017-03-30' - ) - self._register('Microsoft.Compute') + self._compute_client = self.get_mgmt_svc_client(ComputeManagementClient, + base_url=self._cloud_environment.endpoints.resource_manager, + api_version='2017-03-30') return self._compute_client @property def dns_client(self): self.log('Getting dns client') if not self._dns_client: - self.check_client_version('dns', dns_client_version, AZURE_EXPECTED_VERSIONS['dns_client_version']) - self._dns_client = DnsManagementClient( - self.azure_credentials, - self.subscription_id, - base_url=self._cloud_environment.endpoints.resource_manager, - ) - self._register('Microsoft.Dns') + self._dns_client = self.get_mgmt_svc_client(DnsManagementClient, + base_url=self._cloud_environment.endpoints.resource_manager) return self._dns_client @property def web_client(self): self.log('Getting web client') if not self._web_client: - self.check_client_version('web', web_client_version, AZURE_EXPECTED_VERSIONS['web_client_version']) - self._web_client = WebSiteManagementClient( - credentials=self.azure_credentials, - subscription_id=self.subscription_id, - base_url=self.base_url - ) - self._register('Microsoft.Web') + self._web_client = self.get_mgmt_svc_client(WebSiteManagementClient, base_url=self.base_url) return self._web_client @property def containerservice_client(self): self.log('Getting container service client') if not self._containerservice_client: - self._containerservice_client = ContainerServiceClient( - self.azure_credentials, - self.subscription_id - ) - self._register('Microsoft.ContainerService') + self._containerservice_client = self.get_mgmt_svc_client(ContainerServiceClient) return self._containerservice_client