@ -1,25 +1,11 @@
# Copyright (c) 2016 Matt Davis, <mdavis@ansible.com>
# Chris Houseknecht, <house@redhat.com>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
import json
import os
import re
import sy s
import types
import copy
import inspect
import traceback
@ -47,7 +33,8 @@ AZURE_COMMON_ARGS = dict(
ad_user = dict ( type = ' str ' , no_log = True ) ,
password = dict ( type = ' str ' , no_log = True ) ,
cloud_environment = dict ( type = ' str ' ) ,
cert_validation_mode = dict ( type = ' str ' , choices = [ ' validate ' , ' ignore ' ] )
cert_validation_mode = dict ( type = ' str ' , choices = [ ' validate ' , ' ignore ' ] ) ,
api_profile = dict ( type = ' str ' , default = ' latest ' )
# debug=dict(type='bool', default=False),
)
@ -63,6 +50,31 @@ AZURE_CREDENTIAL_ENV_MAPPING = dict(
cert_validation_mode = ' AZURE_CERT_VALIDATION_MODE ' ,
)
# 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 = ' 2017-12-01 ' ,
resource_skus = ' 2017-09-01 ' ,
disks = ' 2017-03-30 ' ,
snapshots = ' 2017-03-30 ' ,
virtual_machine_run_commands = ' 2017-03-30 '
) ,
' NetworkManagementClient ' : ' 2017-11-01 ' ,
' ResourceManagementClient ' : ' 2017-05-10 ' ,
' StorageManagementClient ' : ' 2017-10-01 '
} ,
' 2017-03-09-profile ' : {
' ComputeManagementClient ' : ' 2016-03-30 ' ,
' NetworkManagementClient ' : ' 2015-06-15 ' ,
' ResourceManagementClient ' : ' 2016-02-01 ' ,
' StorageManagementClient ' : ' 2016-01-01 '
}
}
AZURE_TAG_ARGS = dict (
tags = dict ( type = ' dict ' ) ,
append_tags = dict ( type = ' bool ' , default = True ) ,
@ -162,36 +174,36 @@ def format_resource_id(val, subscription_id, namespace, types, resource_group):
type = types ,
subscription = subscription_id ) if not is_valid_resource_id ( val ) else val
# 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 . __name__ : {
' StorageManagementClient ' : {
' package_name ' : ' storage ' ,
' expected_version ' : ' 1.5.0 ' ,
' installed_version ' : storage_client_version
' expected_version ' : ' 1.5.0 '
} ,
ComputeManagementClient . __name__ : {
' ComputeManagementClient ' : {
' package_name ' : ' compute ' ,
' expected_version ' : ' 2.0.0 ' ,
' installed_version ' : compute_client_version
' expected_version ' : ' 2.0.0 '
} ,
' ContainerInstanceManagementClient ' : {
' package_name ' : ' containerinstance ' ,
' expected_version ' : ' 0.3.1 '
} ,
NetworkManagementClient . __name__ : {
' NetworkManagementClient ' : {
' package_name ' : ' network ' ,
' expected_version ' : ' 1.3.0 ' ,
' installed_version ' : network_client_version
' expected_version ' : ' 1.3.0 '
} ,
ResourceManagementClient . __name__ : {
' ResourceManagementClient ' : {
' package_name ' : ' resource ' ,
' expected_version ' : ' 1.1.0 ' ,
' installed_version ' : resource_client_version
' expected_version ' : ' 1.1.0 '
} ,
DnsManagementClient . __name__ : {
' DnsManagementClient ' : {
' package_name ' : ' dns ' ,
' expected_version ' : ' 1.0.1 ' ,
' installed_version ' : dns_client_version
' expected_version ' : ' 1.0.1 '
} ,
WebSiteManagementClient . __name__ : {
' WebSiteManagementClient ' : {
' package_name ' : ' web ' ,
' expected_version ' : ' 0.32.0 ' ,
' installed_version ' : web_client_version
' expected_version ' : ' 0.32.0 '
} ,
} if HAS_AZURE else { }
@ -250,6 +262,7 @@ class AzureRMModuleBase(object):
self . _containerservice_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')
@ -337,10 +350,15 @@ class AzureRMModuleBase(object):
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 ' )
try :
client_module = importlib . import_module ( client_type . __module__ )
client_version = client_module . VERSION
except RuntimeError :
# 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 {0} client version is {1} . The supported version is {2} . Try "
self . fail ( " Installed azure-mgmt- {0} client version is {1} . The supported version is {2} . Try "
" `pip install ansible[azure]` " . format ( client_name , client_version , expected_version ) )
def exec_module ( self , * * kwargs ) :
@ -767,18 +785,65 @@ class AzureRMModuleBase(object):
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 )
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 )
client_argspec = inspect . getargspec ( client_type . __init__ )
client_kwargs = dict ( credentials = self . azure_credentials , subscription_id = self . 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 )
if not base_url :
# most things are resource_manager, don't make everyone specify
base_url = self . _cloud_environment . endpoints . resource_manager
# 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
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 )
# Add user agent for Ansible
client . config . add_user_agent ( ANSIBLE_USER_AGENT )