From a076612b6365cc6d06869764e5af05692b358df0 Mon Sep 17 00:00:00 2001 From: chouseknecht Date: Wed, 29 Jun 2016 03:15:52 -0400 Subject: [PATCH] Update and pin to azure-2.0.0rc5 --- lib/ansible/module_utils/azure_rm_common.py | 134 ++++++++---------- .../utils/module_docs_fragments/azure.py | 4 +- 2 files changed, 62 insertions(+), 76 deletions(-) diff --git a/lib/ansible/module_utils/azure_rm_common.py b/lib/ansible/module_utils/azure_rm_common.py index bd1517a0d80..5f41d6ab1b4 100644 --- a/lib/ansible/module_utils/azure_rm_common.py +++ b/lib/ansible/module_utils/azure_rm_common.py @@ -24,7 +24,10 @@ import os import re import sys import copy +import importlib +import inspect +from distutils.version import LooseVersion from os.path import expanduser from ansible.module_utils.basic import * @@ -73,10 +76,13 @@ try: from enum import Enum from msrest.serialization import Serializer from msrestazure.azure_exceptions import CloudError -# from azure.mgmt.compute import __version__ as azure_compute_version from azure.mgmt.network.models import PublicIPAddress, NetworkSecurityGroup, SecurityRule, NetworkInterface, \ NetworkInterfaceIPConfiguration, Subnet from azure.common.credentials import ServicePrincipalCredentials, UserPassCredentials + 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.network.network_management_client import NetworkManagementClient from azure.mgmt.resource.resources.resource_management_client import ResourceManagementClient from azure.mgmt.storage.storage_management_client import StorageManagementClient @@ -97,6 +103,23 @@ def azure_id_to_dict(id): return result +AZURE_EXPECTED_VERSIONS = dict( + storage_client_version="0.30.0rc5", + compute_client_version="0.30.0rc5", + network_client_version="0.30.0rc5", + resource_client_version="0.30.0rc5" +) + +AZURE_MIN_RELEASE = '2.0.0rc5' + + +def check_client_version(client_name, client_version, expected_version): + # Pinning Azure modules to 2.0.0rc5. + if LooseVersion(client_version) != LooseVersion(expected_version): + self.fail("Installed {0} client version is {1}. The supported version is {2}. Try " + "`pip install azure=={3}`".format(client_name, client_version, expected_version, + AZURE_MIN_RELEASE)) + class AzureRMModuleBase(object): def __init__(self, derived_arg_spec, bypass_checks=False, no_log=False, @@ -128,12 +151,8 @@ class AzureRMModuleBase(object): required_if=merged_required_if) if not HAS_AZURE: - self.fail("The Azure Python SDK is not installed (try 'pip install azure') - {0}".format(HAS_AZURE_EXC)) - - # re-enable after SDK hits release - # if azure_compute_version < AZURE_MIN_VERSION: - # self.fail("Expecting azure.mgmt.compute.__version__ to be >= {0}. Found version {1} " - # "Do you have Azure >= 2.0.0rc2 installed?".format(AZURE_MIN_VERSION, azure_compute_version)) + self.fail("Do you have azure=={1} installed? Try `pip install azure=={1}`" + "- {0}".format(HAS_AZURE_EXC, AZURE_MIN_RELEASE)) self._network_client = None self._storage_client = None @@ -141,7 +160,7 @@ class AzureRMModuleBase(object): self._compute_client = None self.check_mode = self.module.check_mode self.facts_module = facts_module - self.debug = self.module.params.get('debug') + # self.debug = self.module.params.get('debug') # authenticate self.credentials = self._get_credentials(self.module.params) @@ -189,12 +208,12 @@ class AzureRMModuleBase(object): 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') + #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') def validate_tags(self, tags): ''' @@ -210,52 +229,6 @@ class AzureRMModuleBase(object): if not isinstance(value, str): self.fail("Tags values must be strings. Found {0}:{1}".format(str(key), str(value))) - def _tag_purge(self, tags): - ''' - Remove metadata tags not found in user provided tags parameter. Returns tuple - with bool indicating something changed and dict of new tags to be assigned to - the object. - - :param tags: object metadata tags - :return: bool, dict of tags - ''' - if not self.module.params.get('tags'): - # purge all tags - return True, dict() - new_tags = copy.copy(tags) - changed = False - for key in tags: - if not self.module.params['tags'].get(key): - # key not found in user provided parameters - new_tags.pop(key) - changed = True - if changed: - self.log('CHANGED: purged tags') - return changed, new_tags - - def _tag_update(self, tags): - ''' - Update metadata tags with values in user provided tags parameter. Returns - tuple with bool indicating something changed and dict of new tags to be - assigned to the object. - - :param tags: object metadata tags - :return: bool, dict of tags - ''' - if isinstance(tags, dict): - new_tags = copy.copy(tags) - else: - new_tags = dict() - changed = False - if self.module.params.get('tags'): - for key, value in self.module.params['tags'].items(): - if not (new_tags.get(key) and new_tags[key] == value): - changed = True - new_tags[key] = value - if changed: - self.log('CHANGED: updated tags') - return changed, new_tags - def update_tags(self, tags): ''' Call from the module to update metadata tags. Returns tuple @@ -265,15 +238,18 @@ class AzureRMModuleBase(object): :param tags: metadata tags from the object :return: bool, dict ''' + new_tags = copy.copy(tags) if isinstance(tags, dict) else dict() changed = False - updated, new_tags = self._tag_update(tags) - if updated: - changed = True - - if not self.module.params['append_tags']: - purged, new_tags = self._tag_purge(new_tags) - if purged: - changed = True + if isinstance(self.module.params.get('tags'), dict): + for key, value in self.module.params['tags'].iteritems(): + if not new_tags.get(key) or new_tags[key] != value: + changed = True + new_tags[key] = value + if isinstance(tags, dict): + for key, value in tags.iteritems(): + if not self.module.params['tags'].get(key): + new_tags.pop(key) + changed = True return changed, new_tags def has_tags(self, obj_tags, tag_list): @@ -389,15 +365,24 @@ class AzureRMModuleBase(object): return None - def serialize_obj(self, obj, class_name): + def serialize_obj(self, obj, class_name, enum_modules=[]): ''' 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 ''' - serializer = Serializer() + 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) def get_poller_result(self, poller, wait=5): @@ -458,14 +443,12 @@ class AzureRMModuleBase(object): # 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) - keys['key1'] = account_keys.key1 - keys['key2'] = account_keys.key2 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') - return CloudStorageAccount(storage_account_name, keys['key1']).create_block_blob_service() + return CloudStorageAccount(storage_account_name, account_keys.keys[0].value).create_block_blob_service() except Exception as exc: self.fail("Error creating blob service client for storage account {0} - {1}".format(storage_account_name, str(exc))) @@ -591,7 +574,7 @@ class AzureRMModuleBase(object): def storage_client(self): self.log('Getting storage client...') if not self._storage_client: - config = StorageManagementClientConfiguration() + check_client_version('storage', storage_client_version, AZURE_EXPECTED_VERSIONS['storage_client_version']) self._storage_client = StorageManagementClient(self.azure_credentials, self.subscription_id) self._register('Microsoft.Storage') return self._storage_client @@ -600,6 +583,7 @@ class AzureRMModuleBase(object): def network_client(self): self.log('Getting network client') if not self._network_client: + check_client_version('network', network_client_version, AZURE_EXPECTED_VERSIONS['network_client_version']) self._network_client = NetworkManagementClient(self.azure_credentials, self.subscription_id) self._register('Microsoft.Network') return self._network_client @@ -608,6 +592,7 @@ class AzureRMModuleBase(object): def rm_client(self): self.log('Getting resource manager client') if not self._resource_client: + check_client_version('resource', resource_client_version, AZURE_EXPECTED_VERSIONS['resource_client_version']) self._resource_client = ResourceManagementClient(self.azure_credentials, self.subscription_id) return self._resource_client @@ -615,6 +600,7 @@ class AzureRMModuleBase(object): def compute_client(self): self.log('Getting compute client') if not self._compute_client: + check_client_version('compute', compute_client_version, AZURE_EXPECTED_VERSIONS['compute_client_version']) self._compute_client = ComputeManagementClient(self.azure_credentials, self.subscription_id) self._register('Microsoft.Compute') return self._compute_client diff --git a/lib/ansible/utils/module_docs_fragments/azure.py b/lib/ansible/utils/module_docs_fragments/azure.py index 35ae92954a6..8a2e0c5396f 100644 --- a/lib/ansible/utils/module_docs_fragments/azure.py +++ b/lib/ansible/utils/module_docs_fragments/azure.py @@ -66,7 +66,7 @@ options: requirements: - "python >= 2.7" - - "azure >= 2.0.0rc4" + - "azure == 2.0.0rc5" notes: - For authentication with Azure you can pass parameters, set environment variables or use a profile stored @@ -79,4 +79,4 @@ notes: a [default] section and the following keys: subscription_id, client_id, secret and tenant or subscription_id, ad_user and password. It is also possible to add additional profiles. Specify the profile by passing profile or setting AZURE_PROFILE in the environment." - ''' \ No newline at end of file + '''