|
|
|
@ -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,7 +208,7 @@ class AzureRMModuleBase(object):
|
|
|
|
|
def log(self, msg, pretty_print=False):
|
|
|
|
|
pass
|
|
|
|
|
# Use only during module development
|
|
|
|
|
# if self.debug:
|
|
|
|
|
#if self.debug:
|
|
|
|
|
# log_file = open('azure_rm.log', 'a')
|
|
|
|
|
# if pretty_print:
|
|
|
|
|
# log_file.write(json.dumps(msg, indent=4, sort_keys=True))
|
|
|
|
@ -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,14 +238,17 @@ 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:
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
if not self.module.params['append_tags']:
|
|
|
|
|
purged, new_tags = self._tag_purge(new_tags)
|
|
|
|
|
if purged:
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
@ -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
|
|
|
|
|