msrestazure-0.4.13/0000775000372000037200000000000013147616231015031 5ustar travistravis00000000000000msrestazure-0.4.13/MANIFEST.in0000664000372000037200000000001513147616126016566 0ustar travistravis00000000000000include *.rstmsrestazure-0.4.13/README.rst0000664000372000037200000001555713147616126016540 0ustar travistravis00000000000000AutoRest: Python Client Runtime - Azure Module =============================================== .. image:: https://travis-ci.org/Azure/msrestazure-for-python.svg?branch=master :target: https://travis-ci.org/Azure/msrestazure-for-python .. image:: https://codecov.io/gh/azure/msrestazure-for-python/branch/master/graph/badge.svg :target: https://codecov.io/gh/azure/msrestazure-for-python Installation ------------ To install: .. code-block:: bash $ pip install msrestazure Release History --------------- 2017-08-24 Version 0.4.13 +++++++++++++++++++++++++ **Features** - "keyring" is now completely optional 2017-08-23 Version 0.4.12 +++++++++++++++++++++++++ **Features** - add "timeout" to ServicePrincipalCredentials and UserPasswordCredentials - Threads created by AzureOperationPoller have now a name prefixed by "AzureOperationPoller" to help identify them **Bugfixes** - Do not fail if keyring is badly installed - Update Azure Gov login endpoint - Update metadata ARM endpoint parser **Breaking changes** - Remove InteractiveCredentials. This class was deprecated and unusable. Use ADAL device code instead. 2017-06-29 Version 0.4.11 +++++++++++++++++++++++++ **Features** - Add cloud definitions for public Azure, German Azure, China Azure and Azure Gov - Add get_cloud_from_metadata_endpoint to automatically create a Cloud object from an ARM endpoint - Add `cloud_environment` to all Credentials objects (except AdalAuthentication) **Note** - This deprecates "china=True", to be replaced by "cloud_environment=AZURE_CHINA_CLOUD" Example: .. code:: python from msrestazure.azure_cloud import AZURE_CHINA_CLOUD from msrestazure.azure_active_directory import UserPassCredentials credentials = UserPassCredentials( login, password, cloud_environment=AZURE_CHINA_CLOUD ) `base_url` of SDK client can be pointed to "cloud_environment.endpoints.resource_manager" for basic scenario: Example: .. code:: python from msrestazure.azure_cloud import AZURE_CHINA_CLOUD from msrestazure.azure_active_directory import UserPassCredentials from azure.mgmt.resource import ResourceManagementClient credentials = UserPassCredentials( login, password, cloud_environment=AZURE_CHINA_CLOUD ) client = ResourceManagementClient( credentials, subscription_id, base_url=AZURE_CHINA_CLOUD.endpoints.resource_manager ) Azure Stack connection can be done: .. code:: python from msrestazure.azure_cloud import get_cloud_from_metadata_endpoint from msrestazure.azure_active_directory import UserPassCredentials from azure.mgmt.resource import ResourceManagementClient mystack_cloud = get_cloud_from_metadata_endpoint("https://myazurestack-arm-endpoint.com") credentials = UserPassCredentials( login, password, cloud_environment=mystack_cloud ) client = ResourceManagementClient( credentials, subscription_id, base_url=mystack_cloud.endpoints.resource_manager ) 2017-06-27 Version 0.4.10 +++++++++++++++++++++++++ **Bugfixes** - Accept PATCH/201 as LRO valid state - Close token session on exit (ServicePrincipal and UserPassword credentials) 2017-06-19 Version 0.4.9 ++++++++++++++++++++++++ **Features** - Add proxies parameters to ServicePrincipal and UserPassword credentials class #29 - Add automatic Azure provider registration if needed (requires msrest 0.4.10) #28 Thank you to likel for his contribution 2017-05-31 Version 0.4.8 ++++++++++++++++++++++++ **Bugfixes** - Fix LRO if first call never returns 200, but ends on 201 (#26) - FiX LRO AttributeError if timeout is short (#21) **Features** - Expose a "status()" method in AzureOperationPoller (#18) 2017-01-23 Version 0.4.7 ++++++++++++++++++++++++ **Bugfixes** - Adding `accept_language` and `generate_client_request_id ` default values 2016-12-12 Version 0.4.6 ++++++++++++++++++++++++ **Bugfixes** Refactor Long Running Operation algorithm. - There is no breaking changes, however you might need to record again your offline HTTP records if you use unittests with VCRpy. - Fix a couple of latent bugs 2016-11-30 Version 0.4.5 ++++++++++++++++++++++++ **New features** - Add AdalAuthentification class to wrap ADAL library (https://github.com/Azure/msrestazure-for-python/pull/8) 2016-10-17 Version 0.4.4 ++++++++++++++++++++++++ **Bugfixes** - More informative and well-formed CloudError exceptions (https://github.com/Azure/autorest/issues/1460) - Raise CustomException is defined in Swagger (https://github.com/Azure/autorest/issues/1404) 2016-09-14 Version 0.4.3 ++++++++++++++++++++++++ **Bugfixes** - Make AzureOperationPoller thread as daemon (do not block anymore a Ctrl+C) (https://github.com/Azure/autorest/pull/1379) 2016-09-01 Version 0.4.2 ++++++++++++++++++++++++ **Bugfixes** - Better exception message (https://github.com/Azure/autorest/pull/1300) This version needs msrest >= 0.4.3 2016-06-08 Version 0.4.1 ++++++++++++++++++++++++ **Bugfixes** - Fix for LRO PUT operation https://github.com/Azure/autorest/issues/1133 2016-05-25 Version 0.4.0 ++++++++++++++++++++++++ Update msrest dependency to 0.4.0 **Bugfixes** - Fix for several AAD issues https://github.com/Azure/autorest/issues/1055 - Fix for LRO PATCH bug and refactor https://github.com/Azure/autorest/issues/993 **Behaviour changes** - Needs Autorest > 0.17.0 Nightly 20160525 2016-04-26 Version 0.3.0 ++++++++++++++++++++++++ Update msrest dependency to 0.3.0 **Bugfixes** - Read only values are no longer in __init__ or sent to the server (https://github.com/Azure/autorest/pull/959) - Useless kwarg removed **Behaviour changes** - Needs Autorest > 0.16.0 Nightly 20160426 2016-03-31 Version 0.2.1 ++++++++++++++++++++++++ **Bugfixes** - Fix AzurePollerOperation if Swagger defines provisioning status as enum type (https://github.com/Azure/autorest/pull/892) 2016-03-25 Version 0.2.0 ++++++++++++++++++++++++ Update msrest dependency to 0.2.0 **Behaviour change** - async methods called with raw=True don't return anymore AzureOperationPoller but ClientRawResponse - Needs Autorest > 0.16.0 Nightly 20160324 2016-03-21 Version 0.1.2 ++++++++++++++++++++++++ Update msrest dependency to 0.1.3 **Bugfixes** - AzureOperationPoller.wait() failed to raise exception if query error (https://github.com/Azure/autorest/pull/856) 2016-03-04 Version 0.1.1 ++++++++++++++++++++++++ **Bugfixes** - Source package corrupted in Pypi (https://github.com/Azure/autorest/issues/799) 2016-03-04 Version 0.1.0 ++++++++++++++++++++++++ **Behaviour change** - Replaced _required attribute in CloudErrorData class with _validation dict. 2016-02-29 Version 0.0.2 ++++++++++++++++++++++++ **Bugfixes** - Fixed AAD bug to include connection verification in UserPassCredentials. (https://github.com/Azure/autorest/pull/725) - Source package corrupted in Pypi (https://github.com/Azure/autorest/issues/718) 2016-02-19 Version 0.0.1 ++++++++++++++++++++++++ - Initial release. msrestazure-0.4.13/msrestazure.egg-info/0000775000372000037200000000000013147616231021107 5ustar travistravis00000000000000msrestazure-0.4.13/msrestazure.egg-info/requires.txt0000664000372000037200000000005013147616231023502 0ustar travistravis00000000000000msrest~=0.4.10 keyring>=5.6 adal~=0.4.0 msrestazure-0.4.13/msrestazure.egg-info/SOURCES.txt0000664000372000037200000000067613147616231023004 0ustar travistravis00000000000000MANIFEST.in README.rst setup.cfg setup.py msrestazure/__init__.py msrestazure/azure_active_directory.py msrestazure/azure_cloud.py msrestazure/azure_configuration.py msrestazure/azure_exceptions.py msrestazure/azure_operation.py msrestazure/tools.py msrestazure/version.py msrestazure.egg-info/PKG-INFO msrestazure.egg-info/SOURCES.txt msrestazure.egg-info/dependency_links.txt msrestazure.egg-info/requires.txt msrestazure.egg-info/top_level.txtmsrestazure-0.4.13/msrestazure.egg-info/dependency_links.txt0000664000372000037200000000000113147616231025155 0ustar travistravis00000000000000 msrestazure-0.4.13/msrestazure.egg-info/PKG-INFO0000664000372000037200000002363213147616231022212 0ustar travistravis00000000000000Metadata-Version: 1.1 Name: msrestazure Version: 0.4.13 Summary: AutoRest swagger generator Python client runtime. Azure-specific module. Home-page: https://github.com/Azure/msrestazure-for-python Author: Microsoft Corporation Author-email: UNKNOWN License: MIT License Description: AutoRest: Python Client Runtime - Azure Module =============================================== .. image:: https://travis-ci.org/Azure/msrestazure-for-python.svg?branch=master :target: https://travis-ci.org/Azure/msrestazure-for-python .. image:: https://codecov.io/gh/azure/msrestazure-for-python/branch/master/graph/badge.svg :target: https://codecov.io/gh/azure/msrestazure-for-python Installation ------------ To install: .. code-block:: bash $ pip install msrestazure Release History --------------- 2017-08-24 Version 0.4.13 +++++++++++++++++++++++++ **Features** - "keyring" is now completely optional 2017-08-23 Version 0.4.12 +++++++++++++++++++++++++ **Features** - add "timeout" to ServicePrincipalCredentials and UserPasswordCredentials - Threads created by AzureOperationPoller have now a name prefixed by "AzureOperationPoller" to help identify them **Bugfixes** - Do not fail if keyring is badly installed - Update Azure Gov login endpoint - Update metadata ARM endpoint parser **Breaking changes** - Remove InteractiveCredentials. This class was deprecated and unusable. Use ADAL device code instead. 2017-06-29 Version 0.4.11 +++++++++++++++++++++++++ **Features** - Add cloud definitions for public Azure, German Azure, China Azure and Azure Gov - Add get_cloud_from_metadata_endpoint to automatically create a Cloud object from an ARM endpoint - Add `cloud_environment` to all Credentials objects (except AdalAuthentication) **Note** - This deprecates "china=True", to be replaced by "cloud_environment=AZURE_CHINA_CLOUD" Example: .. code:: python from msrestazure.azure_cloud import AZURE_CHINA_CLOUD from msrestazure.azure_active_directory import UserPassCredentials credentials = UserPassCredentials( login, password, cloud_environment=AZURE_CHINA_CLOUD ) `base_url` of SDK client can be pointed to "cloud_environment.endpoints.resource_manager" for basic scenario: Example: .. code:: python from msrestazure.azure_cloud import AZURE_CHINA_CLOUD from msrestazure.azure_active_directory import UserPassCredentials from azure.mgmt.resource import ResourceManagementClient credentials = UserPassCredentials( login, password, cloud_environment=AZURE_CHINA_CLOUD ) client = ResourceManagementClient( credentials, subscription_id, base_url=AZURE_CHINA_CLOUD.endpoints.resource_manager ) Azure Stack connection can be done: .. code:: python from msrestazure.azure_cloud import get_cloud_from_metadata_endpoint from msrestazure.azure_active_directory import UserPassCredentials from azure.mgmt.resource import ResourceManagementClient mystack_cloud = get_cloud_from_metadata_endpoint("https://myazurestack-arm-endpoint.com") credentials = UserPassCredentials( login, password, cloud_environment=mystack_cloud ) client = ResourceManagementClient( credentials, subscription_id, base_url=mystack_cloud.endpoints.resource_manager ) 2017-06-27 Version 0.4.10 +++++++++++++++++++++++++ **Bugfixes** - Accept PATCH/201 as LRO valid state - Close token session on exit (ServicePrincipal and UserPassword credentials) 2017-06-19 Version 0.4.9 ++++++++++++++++++++++++ **Features** - Add proxies parameters to ServicePrincipal and UserPassword credentials class #29 - Add automatic Azure provider registration if needed (requires msrest 0.4.10) #28 Thank you to likel for his contribution 2017-05-31 Version 0.4.8 ++++++++++++++++++++++++ **Bugfixes** - Fix LRO if first call never returns 200, but ends on 201 (#26) - FiX LRO AttributeError if timeout is short (#21) **Features** - Expose a "status()" method in AzureOperationPoller (#18) 2017-01-23 Version 0.4.7 ++++++++++++++++++++++++ **Bugfixes** - Adding `accept_language` and `generate_client_request_id ` default values 2016-12-12 Version 0.4.6 ++++++++++++++++++++++++ **Bugfixes** Refactor Long Running Operation algorithm. - There is no breaking changes, however you might need to record again your offline HTTP records if you use unittests with VCRpy. - Fix a couple of latent bugs 2016-11-30 Version 0.4.5 ++++++++++++++++++++++++ **New features** - Add AdalAuthentification class to wrap ADAL library (https://github.com/Azure/msrestazure-for-python/pull/8) 2016-10-17 Version 0.4.4 ++++++++++++++++++++++++ **Bugfixes** - More informative and well-formed CloudError exceptions (https://github.com/Azure/autorest/issues/1460) - Raise CustomException is defined in Swagger (https://github.com/Azure/autorest/issues/1404) 2016-09-14 Version 0.4.3 ++++++++++++++++++++++++ **Bugfixes** - Make AzureOperationPoller thread as daemon (do not block anymore a Ctrl+C) (https://github.com/Azure/autorest/pull/1379) 2016-09-01 Version 0.4.2 ++++++++++++++++++++++++ **Bugfixes** - Better exception message (https://github.com/Azure/autorest/pull/1300) This version needs msrest >= 0.4.3 2016-06-08 Version 0.4.1 ++++++++++++++++++++++++ **Bugfixes** - Fix for LRO PUT operation https://github.com/Azure/autorest/issues/1133 2016-05-25 Version 0.4.0 ++++++++++++++++++++++++ Update msrest dependency to 0.4.0 **Bugfixes** - Fix for several AAD issues https://github.com/Azure/autorest/issues/1055 - Fix for LRO PATCH bug and refactor https://github.com/Azure/autorest/issues/993 **Behaviour changes** - Needs Autorest > 0.17.0 Nightly 20160525 2016-04-26 Version 0.3.0 ++++++++++++++++++++++++ Update msrest dependency to 0.3.0 **Bugfixes** - Read only values are no longer in __init__ or sent to the server (https://github.com/Azure/autorest/pull/959) - Useless kwarg removed **Behaviour changes** - Needs Autorest > 0.16.0 Nightly 20160426 2016-03-31 Version 0.2.1 ++++++++++++++++++++++++ **Bugfixes** - Fix AzurePollerOperation if Swagger defines provisioning status as enum type (https://github.com/Azure/autorest/pull/892) 2016-03-25 Version 0.2.0 ++++++++++++++++++++++++ Update msrest dependency to 0.2.0 **Behaviour change** - async methods called with raw=True don't return anymore AzureOperationPoller but ClientRawResponse - Needs Autorest > 0.16.0 Nightly 20160324 2016-03-21 Version 0.1.2 ++++++++++++++++++++++++ Update msrest dependency to 0.1.3 **Bugfixes** - AzureOperationPoller.wait() failed to raise exception if query error (https://github.com/Azure/autorest/pull/856) 2016-03-04 Version 0.1.1 ++++++++++++++++++++++++ **Bugfixes** - Source package corrupted in Pypi (https://github.com/Azure/autorest/issues/799) 2016-03-04 Version 0.1.0 ++++++++++++++++++++++++ **Behaviour change** - Replaced _required attribute in CloudErrorData class with _validation dict. 2016-02-29 Version 0.0.2 ++++++++++++++++++++++++ **Bugfixes** - Fixed AAD bug to include connection verification in UserPassCredentials. (https://github.com/Azure/autorest/pull/725) - Source package corrupted in Pypi (https://github.com/Azure/autorest/issues/718) 2016-02-19 Version 0.0.1 ++++++++++++++++++++++++ - Initial release. Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: License :: OSI Approved :: MIT License Classifier: Topic :: Software Development msrestazure-0.4.13/msrestazure.egg-info/top_level.txt0000664000372000037200000000001413147616231023634 0ustar travistravis00000000000000msrestazure msrestazure-0.4.13/setup.cfg0000664000372000037200000000010313147616231016644 0ustar travistravis00000000000000[bdist_wheel] universal = 1 [egg_info] tag_build = tag_date = 0 msrestazure-0.4.13/setup.py0000664000372000037200000000444513147616126016555 0ustar travistravis00000000000000# -------------------------------------------------------------------------- # # Copyright (c) Microsoft Corporation. All rights reserved. # # The MIT License (MIT) # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the ""Software""), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. # # -------------------------------------------------------------------------- from setuptools import setup setup( name='msrestazure', version='0.4.13', author='Microsoft Corporation', packages=['msrestazure'], url='https://github.com/Azure/msrestazure-for-python', license='MIT License', description=('AutoRest swagger generator Python client runtime. ' 'Azure-specific module.'), long_description=open('README.rst').read(), classifiers=[ 'Development Status :: 4 - Beta', 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'License :: OSI Approved :: MIT License', 'Topic :: Software Development'], install_requires=[ "msrest~=0.4.10", "keyring>=5.6", "adal~=0.4.0" ], ) msrestazure-0.4.13/PKG-INFO0000664000372000037200000002363213147616231016134 0ustar travistravis00000000000000Metadata-Version: 1.1 Name: msrestazure Version: 0.4.13 Summary: AutoRest swagger generator Python client runtime. Azure-specific module. Home-page: https://github.com/Azure/msrestazure-for-python Author: Microsoft Corporation Author-email: UNKNOWN License: MIT License Description: AutoRest: Python Client Runtime - Azure Module =============================================== .. image:: https://travis-ci.org/Azure/msrestazure-for-python.svg?branch=master :target: https://travis-ci.org/Azure/msrestazure-for-python .. image:: https://codecov.io/gh/azure/msrestazure-for-python/branch/master/graph/badge.svg :target: https://codecov.io/gh/azure/msrestazure-for-python Installation ------------ To install: .. code-block:: bash $ pip install msrestazure Release History --------------- 2017-08-24 Version 0.4.13 +++++++++++++++++++++++++ **Features** - "keyring" is now completely optional 2017-08-23 Version 0.4.12 +++++++++++++++++++++++++ **Features** - add "timeout" to ServicePrincipalCredentials and UserPasswordCredentials - Threads created by AzureOperationPoller have now a name prefixed by "AzureOperationPoller" to help identify them **Bugfixes** - Do not fail if keyring is badly installed - Update Azure Gov login endpoint - Update metadata ARM endpoint parser **Breaking changes** - Remove InteractiveCredentials. This class was deprecated and unusable. Use ADAL device code instead. 2017-06-29 Version 0.4.11 +++++++++++++++++++++++++ **Features** - Add cloud definitions for public Azure, German Azure, China Azure and Azure Gov - Add get_cloud_from_metadata_endpoint to automatically create a Cloud object from an ARM endpoint - Add `cloud_environment` to all Credentials objects (except AdalAuthentication) **Note** - This deprecates "china=True", to be replaced by "cloud_environment=AZURE_CHINA_CLOUD" Example: .. code:: python from msrestazure.azure_cloud import AZURE_CHINA_CLOUD from msrestazure.azure_active_directory import UserPassCredentials credentials = UserPassCredentials( login, password, cloud_environment=AZURE_CHINA_CLOUD ) `base_url` of SDK client can be pointed to "cloud_environment.endpoints.resource_manager" for basic scenario: Example: .. code:: python from msrestazure.azure_cloud import AZURE_CHINA_CLOUD from msrestazure.azure_active_directory import UserPassCredentials from azure.mgmt.resource import ResourceManagementClient credentials = UserPassCredentials( login, password, cloud_environment=AZURE_CHINA_CLOUD ) client = ResourceManagementClient( credentials, subscription_id, base_url=AZURE_CHINA_CLOUD.endpoints.resource_manager ) Azure Stack connection can be done: .. code:: python from msrestazure.azure_cloud import get_cloud_from_metadata_endpoint from msrestazure.azure_active_directory import UserPassCredentials from azure.mgmt.resource import ResourceManagementClient mystack_cloud = get_cloud_from_metadata_endpoint("https://myazurestack-arm-endpoint.com") credentials = UserPassCredentials( login, password, cloud_environment=mystack_cloud ) client = ResourceManagementClient( credentials, subscription_id, base_url=mystack_cloud.endpoints.resource_manager ) 2017-06-27 Version 0.4.10 +++++++++++++++++++++++++ **Bugfixes** - Accept PATCH/201 as LRO valid state - Close token session on exit (ServicePrincipal and UserPassword credentials) 2017-06-19 Version 0.4.9 ++++++++++++++++++++++++ **Features** - Add proxies parameters to ServicePrincipal and UserPassword credentials class #29 - Add automatic Azure provider registration if needed (requires msrest 0.4.10) #28 Thank you to likel for his contribution 2017-05-31 Version 0.4.8 ++++++++++++++++++++++++ **Bugfixes** - Fix LRO if first call never returns 200, but ends on 201 (#26) - FiX LRO AttributeError if timeout is short (#21) **Features** - Expose a "status()" method in AzureOperationPoller (#18) 2017-01-23 Version 0.4.7 ++++++++++++++++++++++++ **Bugfixes** - Adding `accept_language` and `generate_client_request_id ` default values 2016-12-12 Version 0.4.6 ++++++++++++++++++++++++ **Bugfixes** Refactor Long Running Operation algorithm. - There is no breaking changes, however you might need to record again your offline HTTP records if you use unittests with VCRpy. - Fix a couple of latent bugs 2016-11-30 Version 0.4.5 ++++++++++++++++++++++++ **New features** - Add AdalAuthentification class to wrap ADAL library (https://github.com/Azure/msrestazure-for-python/pull/8) 2016-10-17 Version 0.4.4 ++++++++++++++++++++++++ **Bugfixes** - More informative and well-formed CloudError exceptions (https://github.com/Azure/autorest/issues/1460) - Raise CustomException is defined in Swagger (https://github.com/Azure/autorest/issues/1404) 2016-09-14 Version 0.4.3 ++++++++++++++++++++++++ **Bugfixes** - Make AzureOperationPoller thread as daemon (do not block anymore a Ctrl+C) (https://github.com/Azure/autorest/pull/1379) 2016-09-01 Version 0.4.2 ++++++++++++++++++++++++ **Bugfixes** - Better exception message (https://github.com/Azure/autorest/pull/1300) This version needs msrest >= 0.4.3 2016-06-08 Version 0.4.1 ++++++++++++++++++++++++ **Bugfixes** - Fix for LRO PUT operation https://github.com/Azure/autorest/issues/1133 2016-05-25 Version 0.4.0 ++++++++++++++++++++++++ Update msrest dependency to 0.4.0 **Bugfixes** - Fix for several AAD issues https://github.com/Azure/autorest/issues/1055 - Fix for LRO PATCH bug and refactor https://github.com/Azure/autorest/issues/993 **Behaviour changes** - Needs Autorest > 0.17.0 Nightly 20160525 2016-04-26 Version 0.3.0 ++++++++++++++++++++++++ Update msrest dependency to 0.3.0 **Bugfixes** - Read only values are no longer in __init__ or sent to the server (https://github.com/Azure/autorest/pull/959) - Useless kwarg removed **Behaviour changes** - Needs Autorest > 0.16.0 Nightly 20160426 2016-03-31 Version 0.2.1 ++++++++++++++++++++++++ **Bugfixes** - Fix AzurePollerOperation if Swagger defines provisioning status as enum type (https://github.com/Azure/autorest/pull/892) 2016-03-25 Version 0.2.0 ++++++++++++++++++++++++ Update msrest dependency to 0.2.0 **Behaviour change** - async methods called with raw=True don't return anymore AzureOperationPoller but ClientRawResponse - Needs Autorest > 0.16.0 Nightly 20160324 2016-03-21 Version 0.1.2 ++++++++++++++++++++++++ Update msrest dependency to 0.1.3 **Bugfixes** - AzureOperationPoller.wait() failed to raise exception if query error (https://github.com/Azure/autorest/pull/856) 2016-03-04 Version 0.1.1 ++++++++++++++++++++++++ **Bugfixes** - Source package corrupted in Pypi (https://github.com/Azure/autorest/issues/799) 2016-03-04 Version 0.1.0 ++++++++++++++++++++++++ **Behaviour change** - Replaced _required attribute in CloudErrorData class with _validation dict. 2016-02-29 Version 0.0.2 ++++++++++++++++++++++++ **Bugfixes** - Fixed AAD bug to include connection verification in UserPassCredentials. (https://github.com/Azure/autorest/pull/725) - Source package corrupted in Pypi (https://github.com/Azure/autorest/issues/718) 2016-02-19 Version 0.0.1 ++++++++++++++++++++++++ - Initial release. Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: License :: OSI Approved :: MIT License Classifier: Topic :: Software Development msrestazure-0.4.13/msrestazure/0000775000372000037200000000000013147616231017415 5ustar travistravis00000000000000msrestazure-0.4.13/msrestazure/azure_operation.py0000664000372000037200000004462213147616126023210 0ustar travistravis00000000000000# -------------------------------------------------------------------------- # # Copyright (c) Microsoft Corporation. All rights reserved. # # The MIT License (MIT) # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the ""Software""), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. # # -------------------------------------------------------------------------- import re import threading import time import uuid try: from urlparse import urlparse except ImportError: from urllib.parse import urlparse from msrest.exceptions import DeserializationError, ClientException from msrestazure.azure_exceptions import CloudError FINISHED = frozenset(['succeeded', 'canceled', 'failed']) FAILED = frozenset(['canceled', 'failed']) SUCCEEDED = frozenset(['succeeded']) def finished(status): if hasattr(status, 'value'): status = status.value return str(status).lower() in FINISHED def failed(status): if hasattr(status, 'value'): status = status.value return str(status).lower() in FAILED def succeeded(status): if hasattr(status, 'value'): status = status.value return str(status).lower() in SUCCEEDED def _validate(url): """Validate a url. :param str url: Polling URL extracted from response header. :raises: ValueError if URL has no scheme or host. """ if url is None: return parsed = urlparse(url) if not parsed.scheme or not parsed.netloc: raise ValueError("Invalid URL header") def _get_header_url(response, header_name): """Get a URL from a header requests. :param requests.Response response: REST call response. :param str header_name: Header name. :returns: URL if not None AND valid, None otherwise """ url = response.headers.get(header_name) try: _validate(url) except ValueError: return None else: return url class BadStatus(Exception): pass class BadResponse(Exception): pass class OperationFailed(Exception): pass class SimpleResource: """An implementation of Python 3 SimpleNamespace. Used to deserialize resource objects from response bodies where no particular object type has been specified. """ def __init__(self, **kwargs): self.__dict__.update(kwargs) def __repr__(self): keys = sorted(self.__dict__) items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys) return "{}({})".format(type(self).__name__, ", ".join(items)) def __eq__(self, other): return self.__dict__ == other.__dict__ class LongRunningOperation(object): """LongRunningOperation Provides default logic for interpreting operation responses and status updates. """ _convert = re.compile('([a-z0-9])([A-Z])') def __init__(self, response, outputs): self.method = response.request.method self.status = "" self.resource = None self.get_outputs = outputs self.async_url = None self.location_url = None self.initial_status_code = None def _raise_if_bad_http_status_and_method(self, response): """Check response status code is valid for a Put or Patch request. Must be 200, 201, 202, or 204. :raises: BadStatus if invalid status. """ code = response.status_code if code in {200, 202} or \ (code == 201 and self.method in {'PUT', 'PATCH'}) or \ (code == 204 and self.method in {'DELETE', 'POST'}): return raise BadStatus( "Invalid return status for {!r} operation".format(self.method)) def _is_empty(self, response): """Check if response body contains meaningful content. :rtype: bool :raises: DeserializationError if response body contains invalid json data. """ if not response.content: return True try: body = response.json() return not body except ValueError: raise DeserializationError( "Error occurred in deserializing the response body.") def _deserialize(self, response): """Attempt to deserialize resource from response. :param requests.Response response: latest REST call response. """ # Hacking response with initial status_code previous_status = response.status_code response.status_code = self.initial_status_code resource = self.get_outputs(response) response.status_code = previous_status # Hack for Storage or SQL, to workaround the bug in the Python generator if resource is None: previous_status = response.status_code for status_code_to_test in [200, 201]: try: response.status_code = status_code_to_test resource = self.get_outputs(response) except ClientException: pass else: return resource finally: response.status_code = previous_status return resource def _get_async_status(self, response): """Attempt to find status info in response body. :param requests.Response response: latest REST call response. :rtype: str :returns: Status if found, else 'None'. """ if self._is_empty(response): return None body = response.json() return body.get('status') def _get_provisioning_state(self, response): """ Attempt to get provisioning state from resource. :param requests.Response response: latest REST call response. :returns: Status if found, else 'None'. """ if self._is_empty(response): return None body = response.json() return body.get("properties", {}).get("provisioningState") def should_do_final_get(self): """Check whether the polling should end doing a final GET. :param requests.Response response: latest REST call response. :rtype: bool """ return (self.async_url or not self.resource) and \ self.method in {'PUT', 'PATCH'} def set_initial_status(self, response): """Process first response after initiating long running operation and set self.status attribute. :param requests.Response response: initial REST call response. """ self._raise_if_bad_http_status_and_method(response) if self._is_empty(response): self.resource = None else: try: self.resource = self.get_outputs(response) except DeserializationError: self.resource = None self.set_async_url_if_present(response) if response.status_code in {200, 201, 202, 204}: self.initial_status_code = response.status_code if self.async_url or self.location_url or response.status_code == 202: self.status = 'InProgress' elif response.status_code == 201: status = self._get_provisioning_state(response) self.status = status or 'InProgress' elif response.status_code == 200: status = self._get_provisioning_state(response) self.status = status or 'Succeeded' elif response.status_code == 204: self.status = 'Succeeded' self.resource = None else: raise OperationFailed("Invalid status found") return raise OperationFailed("Operation failed or cancelled") def get_status_from_location(self, response): """Process the latest status update retrieved from a 'location' header. :param requests.Response response: latest REST call response. :raises: BadResponse if response has no body and not status 202. """ self._raise_if_bad_http_status_and_method(response) code = response.status_code if code == 202: self.status = "InProgress" else: self.status = 'Succeeded' if self._is_empty(response): self.resource = None else: self.resource = self._deserialize(response) def get_status_from_resource(self, response): """Process the latest status update retrieved from the same URL as the previous request. :param requests.Response response: latest REST call response. :raises: BadResponse if status not 200 or 204. """ self._raise_if_bad_http_status_and_method(response) if self._is_empty(response): raise BadResponse('The response from long running operation ' 'does not contain a body.') status = self._get_provisioning_state(response) self.status = status or 'Succeeded' self.resource = self._deserialize(response) def get_status_from_async(self, response): """Process the latest status update retrieved from a 'azure-asyncoperation' header. :param requests.Response response: latest REST call response. :raises: BadResponse if response has no body, or body does not contain status. """ self._raise_if_bad_http_status_and_method(response) if self._is_empty(response): raise BadResponse('The response from long running operation ' 'does not contain a body.') self.status = self._get_async_status(response) if not self.status: raise BadResponse("No status found in body") try: self.resource = self.get_outputs(response) except Exception: self.resource = None def set_async_url_if_present(self, response): async_url = _get_header_url(response, 'azure-asyncoperation') if async_url: self.async_url = async_url location_url = _get_header_url(response, 'location') if location_url: self.location_url = location_url class AzureOperationPoller(object): """Initiates long running operation and polls status in separate thread. :param callable send_cmd: The API request to initiate the operation. :param callable update_cmd: The API reuqest to check the status of the operation. :param callable output_cmd: The function to deserialize the resource of the operation. :param int timeout: Time in seconds to wait between status calls, default is 30. """ def __init__(self, send_cmd, output_cmd, update_cmd, timeout=30): self._timeout = timeout self._callbacks = [] try: self._response = send_cmd() self._operation = LongRunningOperation(self._response, output_cmd) self._operation.set_initial_status(self._response) except BadStatus: self._operation.status = 'Failed' raise CloudError(self._response) except BadResponse as err: self._operation.status = 'Failed' raise CloudError(self._response, str(err)) except OperationFailed: raise CloudError(self._response) self._thread = None self._done = None self._exception = None if not finished(self.status()): self._done = threading.Event() self._thread = threading.Thread( target=self._start, name="AzureOperationPoller({})".format(uuid.uuid4()), args=(update_cmd,)) self._thread.daemon = True self._thread.start() def _start(self, update_cmd): """Start the long running operation. On completetion, runs any callbacks. :param callable update_cmd: The API reuqest to check the status of the operation. """ try: self._poll(update_cmd) except BadStatus: self._operation.status = 'Failed' self._exception = CloudError(self._response) except BadResponse as err: self._operation.status = 'Failed' self._exception = CloudError(self._response, str(err)) except OperationFailed: self._exception = CloudError(self._response) except Exception as err: self._exception = err finally: self._done.set() callbacks, self._callbacks = self._callbacks, [] while callbacks: for call in callbacks: call(self._operation) callbacks, self._callbacks = self._callbacks, [] def _delay(self): """Check for a 'retry-after' header to set timeout, otherwise use configured timeout. """ if self._response is None: return if self._response.headers.get('retry-after'): time.sleep(int(self._response.headers['retry-after'])) else: time.sleep(self._timeout) def _polling_cookie(self): """Collect retry cookie - we only want to do this for the test server at this point, unless we implement a proper cookie policy. :returns: Dictionary containing a cookie header if required, otherwise an empty dictionary. """ parsed_url = urlparse(self._response.request.url) host = parsed_url.hostname.strip('.') if host == 'localhost': return {'cookie': self._response.headers.get('set-cookie', '')} return {} def _poll(self, update_cmd): """Poll status of operation so long as operation is incomplete and we have an endpoint to query. :param callable update_cmd: The function to call to retrieve the latest status of the long running operation. :raises: OperationFailed if operation status 'Failed' or 'Cancelled'. :raises: BadStatus if response status invalid. :raises: BadResponse if response invalid. """ initial_url = self._response.request.url while not finished(self.status()): self._delay() headers = self._polling_cookie() if self._operation.async_url: self._response = update_cmd( self._operation.async_url, headers) self._operation.set_async_url_if_present(self._response) self._operation.get_status_from_async( self._response) elif self._operation.location_url: self._response = update_cmd( self._operation.location_url, headers) self._operation.set_async_url_if_present(self._response) self._operation.get_status_from_location( self._response) elif self._operation.method == "PUT": self._response = update_cmd(initial_url, headers) self._operation.set_async_url_if_present(self._response) self._operation.get_status_from_resource( self._response) else: raise BadResponse( 'Location header is missing from long running operation.') if failed(self._operation.status): raise OperationFailed("Operation failed or cancelled") elif self._operation.should_do_final_get(): self._response = update_cmd(initial_url) self._operation.get_status_from_resource( self._response) def status(self): """Returns the current status string. :returns: The current status string :rtype: str """ return self._operation.status def result(self, timeout=None): """Return the result of the long running operation, or the result available after the specified timeout. :returns: The deserialized resource of the long running operation, if one is available. :raises CloudError: Server problem with the query. """ self.wait(timeout) return self._operation.resource def wait(self, timeout=None): """Wait on the long running operation for a specified length of time. :param int timeout: Perion of time to wait for the long running operation to complete. :raises CloudError: Server problem with the query. """ if self._thread is None: return self._thread.join(timeout=timeout) try: raise self._exception except TypeError: pass def done(self): """Check status of the long running operation. :returns: 'True' if the process has completed, else 'False'. """ return self._thread is None or not self._thread.isAlive() def add_done_callback(self, func): """Add callback function to be run once the long running operation has completed - regardless of the status of the operation. :param callable func: Callback function that takes at least one argument, a completed LongRunningOperation. :raises: ValueError if the long running operation has already completed. """ if self._done is None or self._done.is_set(): raise ValueError("Process is complete.") self._callbacks.append(func) def remove_done_callback(self, func): """Remove a callback from the long running operation. :param callable func: The function to be removed from the callbacks. :raises: ValueError if the long running operation has already completed. """ if self._done is None or self._done.is_set(): raise ValueError("Process is complete.") self._callbacks = [c for c in self._callbacks if c != func] msrestazure-0.4.13/msrestazure/version.py0000664000372000037200000000247113147616126021463 0ustar travistravis00000000000000# -------------------------------------------------------------------------- # # Copyright (c) Microsoft Corporation. All rights reserved. # # The MIT License (MIT) # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the ""Software""), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. # # -------------------------------------------------------------------------- msrestazure_version = "0.4.13"msrestazure-0.4.13/msrestazure/azure_active_directory.py0000664000372000037200000005227513147616126024552 0ustar travistravis00000000000000# -------------------------------------------------------------------------- # # Copyright (c) Microsoft Corporation. All rights reserved. # # The MIT License (MIT) # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the ""Software""), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. # # -------------------------------------------------------------------------- import ast import logging import re import time import warnings try: from urlparse import urlparse, parse_qs except ImportError: from urllib.parse import urlparse, parse_qs import adal from oauthlib.oauth2 import BackendApplicationClient, LegacyApplicationClient from oauthlib.oauth2.rfc6749.errors import ( InvalidGrantError, MismatchingStateError, OAuth2Error, TokenExpiredError) from requests import RequestException, ConnectionError import requests_oauthlib as oauth try: import keyring except Exception as err: keyring = False KEYRING_EXCEPTION = err from msrest.authentication import OAuthTokenAuthentication, Authentication from msrest.exceptions import TokenExpiredError as Expired from msrest.exceptions import AuthenticationError, raise_with_traceback from msrestazure.azure_cloud import AZURE_CHINA_CLOUD, AZURE_PUBLIC_CLOUD _LOGGER = logging.getLogger(__name__) if not keyring: _LOGGER.warning("Cannot load keyring on your system: %s", KEYRING_EXCEPTION) def _build_url(uri, paths, scheme): """Combine URL parts. :param str uri: The base URL. :param list paths: List of strings that make up the URL. :param str scheme: The URL scheme, 'http' or 'https'. :rtype: str :return: Combined, formatted URL. """ path = [str(p).strip('/') for p in paths] combined_path = '/'.join(path) parsed_url = urlparse(uri) replaced = parsed_url._replace(scheme=scheme) if combined_path: path = '/'.join([replaced.path, combined_path]) replaced = replaced._replace(path=path) new_url = replaced.geturl() new_url = new_url.replace('///', '//') return new_url def _http(uri, *extra): """Convert https URL to http. :param str uri: The base URL. :param str extra: Additional URL paths (optional). :rtype: str :return: An HTTP URL. """ return _build_url(uri, extra, 'http') def _https(uri, *extra): """Convert http URL to https. :param str uri: The base URL. :param str extra: Additional URL paths (optional). :rtype: str :return: An HTTPS URL. """ return _build_url(uri, extra, 'https') class AADMixin(OAuthTokenAuthentication): """Mixin for Authentication object. Provides some AAD functionality: - State validation - Token caching and retrieval - Default AAD configuration """ _token_uri = "/oauth2/token" _auth_uri = "/oauth2/authorize" _tenant = "common" _keyring = "AzureAAD" _case = re.compile('([a-z0-9])([A-Z])') def _configure(self, **kwargs): """Configure authentication endpoint. Optional kwargs may include: - cloud_environment (msrestazure.azure_cloud.Cloud): A targeted cloud environment - china (bool): Configure auth for China-based service, default is 'False'. - tenant (str): Alternative tenant, default is 'common'. - auth_uri (str): Alternative authentication endpoint. - token_uri (str): Alternative token retrieval endpoint. - resource (str): Alternative authentication resource, default is 'https://management.core.windows.net/'. - verify (bool): Verify secure connection, default is 'True'. - keyring (str): Name of local token cache, default is 'AzureAAD'. - timeout (int): Timeout of the request in seconds. - proxies (dict): Dictionary mapping protocol or protocol and hostname to the URL of the proxy. """ if kwargs.get('china'): err_msg = ("china parameter is deprecated, " "please use " "cloud_environment=msrestazure.azure_cloud.AZURE_CHINA_CLOUD") warnings.warn(err_msg, DeprecationWarning) self.cloud_environment = AZURE_CHINA_CLOUD else: self.cloud_environment = AZURE_PUBLIC_CLOUD self.cloud_environment = kwargs.get('cloud_environment', self.cloud_environment) auth_endpoint = self.cloud_environment.endpoints.active_directory resource = self.cloud_environment.endpoints.management tenant = kwargs.get('tenant', self._tenant) self.auth_uri = kwargs.get('auth_uri', _https( auth_endpoint, tenant, self._auth_uri)) self.token_uri = kwargs.get('token_uri', _https( auth_endpoint, tenant, self._token_uri)) self.verify = kwargs.get('verify', True) self.cred_store = kwargs.get('keyring', self._keyring) self.resource = kwargs.get('resource', resource) self.proxies = kwargs.get('proxies') self.timeout = kwargs.get('timeout') self.state = oauth.oauth2_session.generate_token() self.store_key = "{}_{}".format( auth_endpoint.strip('/'), self.store_key) def _check_state(self, response): """Validate state returned by AAD server. :param str response: URL returned by server redirect. :raises: ValueError if state does not match that of the request. :rtype: None """ query = parse_qs(urlparse(response).query) if self.state not in query.get('state', []): raise ValueError( "State received from server does not match that of request.") def _convert_token(self, token): """Convert token fields from camel case. :param dict token: An authentication token. :rtype: dict """ return {self._case.sub(r'\1_\2', k).lower(): v for k, v in token.items()} def _parse_token(self): # TODO: We could also check expires_on and use to update expires_in if self.token.get('expires_at'): countdown = float(self.token['expires_at']) - time.time() self.token['expires_in'] = countdown kwargs = {} if self.token.get('refresh_token'): kwargs['auto_refresh_url'] = self.token_uri kwargs['auto_refresh_kwargs'] = {'client_id': self.id, 'resource': self.resource} kwargs['token_updater'] = self._default_token_cache return kwargs def _default_token_cache(self, token): """Store token for future sessions. :param dict token: An authentication token. :rtype: None """ self.token = token if keyring: try: keyring.set_password(self.cred_store, self.store_key, str(token)) except Exception as err: _LOGGER.warning("Keyring cache token has failed: %s", str(err)) def _retrieve_stored_token(self): """Retrieve stored token for new session. :raises: ValueError if no cached token found. :rtype: dict :return: Retrieved token. """ token = keyring.get_password(self.cred_store, self.store_key) if token is None: raise ValueError("No stored token found.") self.token = ast.literal_eval(str(token)) self.signed_session() def signed_session(self): """Create token-friendly Requests session, using auto-refresh. Used internally when a request is made. :rtype: requests_oauthlib.OAuth2Session :raises: TokenExpiredError if token can no longer be refreshed. """ kwargs = self._parse_token() try: new_session = oauth.OAuth2Session( self.id, token=self.token, **kwargs) return new_session except TokenExpiredError as err: raise_with_traceback(Expired, "", err) def clear_cached_token(self): """Clear any stored tokens. :raises: KeyError if failed to clear token. :rtype: None """ try: keyring.delete_password(self.cred_store, self.store_key) except keyring.errors.PasswordDeleteError: raise_with_traceback(KeyError, "Unable to clear token.") class AADRefreshMixin(object): """ Additional token refresh logic """ def refresh_session(self): """Return updated session if token has expired, attempts to refresh using newly acquired token. :rtype: requests.Session. """ if self.token.get('refresh_token'): try: return self.signed_session() except Expired: pass self.set_token() return self.signed_session() class AADTokenCredentials(AADMixin): """ Credentials objects for AAD token retrieved through external process e.g. Python ADAL lib. Optional kwargs may include: - cloud_environment (msrestazure.azure_cloud.Cloud): A targeted cloud environment - china (bool): Configure auth for China-based service, default is 'False'. - tenant (str): Alternative tenant, default is 'common'. - auth_uri (str): Alternative authentication endpoint. - token_uri (str): Alternative token retrieval endpoint. - resource (str): Alternative authentication resource, default is 'https://management.core.windows.net/'. - verify (bool): Verify secure connection, default is 'True'. - keyring (str): Name of local token cache, default is 'AzureAAD'. - cached (bool): If true, will not attempt to collect a token, which can then be populated later from a cached token. :param dict token: Authentication token. :param str client_id: Client ID, if not set, Xplat Client ID will be used. """ def __init__(self, token, client_id=None, **kwargs): if not client_id: # Default to Xplat Client ID. client_id = '04b07795-8ddb-461a-bbee-02f9e1bf7b46' super(AADTokenCredentials, self).__init__(client_id, None) self._configure(**kwargs) if not kwargs.get('cached'): self.token = self._convert_token(token) self.signed_session() @classmethod def retrieve_session(cls, client_id=None): """Create AADTokenCredentials from a cached token if it has not yet expired. """ session = cls(None, None, client_id=client_id, cached=True) session._retrieve_stored_token() return session class UserPassCredentials(AADRefreshMixin, AADMixin): """Credentials object for Headless Authentication, i.e. AAD authentication via username and password. Headless Auth requires an AAD login (no a Live ID) that already has permission to access the resource e.g. an organization account, and that 2-factor auth be disabled. Optional kwargs may include: - cloud_environment (msrestazure.azure_cloud.Cloud): A targeted cloud environment - china (bool): Configure auth for China-based service, default is 'False'. - tenant (str): Alternative tenant, default is 'common'. - auth_uri (str): Alternative authentication endpoint. - token_uri (str): Alternative token retrieval endpoint. - resource (str): Alternative authentication resource, default is 'https://management.core.windows.net/'. - verify (bool): Verify secure connection, default is 'True'. - keyring (str): Name of local token cache, default is 'AzureAAD'. - timeout (int): Timeout of the request in seconds. - cached (bool): If true, will not attempt to collect a token, which can then be populated later from a cached token. - proxies (dict): Dictionary mapping protocol or protocol and hostname to the URL of the proxy. :param str username: Account username. :param str password: Account password. :param str client_id: Client ID, if not set, Xplat Client ID will be used. :param str secret: Client secret, only if required by server. """ def __init__(self, username, password, client_id=None, secret=None, **kwargs): if not client_id: # Default to Xplat Client ID. client_id = '04b07795-8ddb-461a-bbee-02f9e1bf7b46' super(UserPassCredentials, self).__init__(client_id, None) self._configure(**kwargs) self.store_key += "_{}".format(username) self.username = username self.password = password self.secret = secret self.client = LegacyApplicationClient(client_id=self.id) if not kwargs.get('cached'): self.set_token() @classmethod def retrieve_session(cls, username, client_id=None): """Create ServicePrincipalCredentials from a cached token if it has not yet expired. """ session = cls(username, None, client_id=client_id, cached=True) session._retrieve_stored_token() return session def _setup_session(self): """Create token-friendly Requests session. :rtype: requests_oauthlib.OAuth2Session """ return oauth.OAuth2Session(client=self.client) def set_token(self): """Get token using Username/Password credentials. :raises: AuthenticationError if credentials invalid, or call fails. """ with self._setup_session() as session: optional = {} if self.secret: optional['client_secret'] = self.secret try: token = session.fetch_token(self.token_uri, client_id=self.id, username=self.username, password=self.password, resource=self.resource, verify=self.verify, proxies=self.proxies, timeout=self.timeout, **optional) except (RequestException, OAuth2Error, InvalidGrantError) as err: raise_with_traceback(AuthenticationError, "", err) self.token = token class ServicePrincipalCredentials(AADRefreshMixin, AADMixin): """Credentials object for Service Principle Authentication. Authenticates via a Client ID and Secret. Optional kwargs may include: - cloud_environment (msrestazure.azure_cloud.Cloud): A targeted cloud environment - china (bool): Configure auth for China-based service, default is 'False'. - tenant (str): Alternative tenant, default is 'common'. - auth_uri (str): Alternative authentication endpoint. - token_uri (str): Alternative token retrieval endpoint. - resource (str): Alternative authentication resource, default is 'https://management.core.windows.net/'. - verify (bool): Verify secure connection, default is 'True'. - keyring (str): Name of local token cache, default is 'AzureAAD'. - timeout (int): Timeout of the request in seconds. - cached (bool): If true, will not attempt to collect a token, which can then be populated later from a cached token. - proxies (dict): Dictionary mapping protocol or protocol and hostname to the URL of the proxy. :param str client_id: Client ID. :param str secret: Client secret. """ def __init__(self, client_id, secret, **kwargs): super(ServicePrincipalCredentials, self).__init__(client_id, None) self._configure(**kwargs) self.secret = secret self.client = BackendApplicationClient(self.id) if not kwargs.get('cached'): self.set_token() @classmethod def retrieve_session(cls, client_id): """Create ServicePrincipalCredentials from a cached token if it has not yet expired. """ session = cls(client_id, None, cached=True) session._retrieve_stored_token() return session def _setup_session(self): """Create token-friendly Requests session. :rtype: requests_oauthlib.OAuth2Session """ return oauth.OAuth2Session(self.id, client=self.client) def set_token(self): """Get token using Client ID/Secret credentials. :raises: AuthenticationError if credentials invalid, or call fails. """ with self._setup_session() as session: try: token = session.fetch_token(self.token_uri, client_id=self.id, resource=self.resource, client_secret=self.secret, response_type="client_credentials", verify=self.verify, timeout=self.timeout, proxies=self.proxies) except (RequestException, OAuth2Error, InvalidGrantError) as err: raise_with_traceback(AuthenticationError, "", err) else: self.token = token # For backward compatibility of import, but I doubt someone uses that... class InteractiveCredentials(object): def __init__(self, *args, **kwargs): raise NotImplementedError("InteractiveCredentials was not functionning and was removed. Please use ADAL and device code instead.") class AdalAuthentication(Authentication): # pylint: disable=too-few-public-methods """A wrapper to use ADAL for Python easily to authenticate on Azure. .. versionadded:: 0.4.5 """ def __init__(self, adal_method, *args, **kwargs): """Take an ADAL `acquire_token` method and its parameters. :Example: .. code:: python context = adal.AuthenticationContext('https://login.microsoftonline.com/ABCDEFGH-1234-1234-1234-ABCDEFGHIJKL') RESOURCE = '00000002-0000-0000-c000-000000000000' #AAD graph resource token = context.acquire_token_with_client_credentials( RESOURCE, "http://PythonSDK", "Key-Configured-In-Portal") can be written here: .. code:: python context = adal.AuthenticationContext('https://login.microsoftonline.com/ABCDEFGH-1234-1234-1234-ABCDEFGHIJKL') RESOURCE = '00000002-0000-0000-c000-000000000000' #AAD graph resource credentials = AdalAuthentication( context.acquire_token_with_client_credentials, RESOURCE, "http://PythonSDK", "Key-Configured-In-Portal") or using a lambda if you prefer: .. code:: python context = adal.AuthenticationContext('https://login.microsoftonline.com/ABCDEFGH-1234-1234-1234-ABCDEFGHIJKL') RESOURCE = '00000002-0000-0000-c000-000000000000' #AAD graph resource credentials = AdalAuthentication( lambda: context.acquire_token_with_client_credentials( RESOURCE, "http://PythonSDK", "Key-Configured-In-Portal" ) ) :param adal_method: A lambda with no args, or `acquire_token` method with args using args/kwargs :param args: Optional args for the method :param kwargs: Optional kwargs for the method """ self._adal_method = adal_method self._args = args self._kwargs = kwargs def signed_session(self): """Get a signed session for requests. Usually called by the Azure SDKs for you to authenticate queries. :rtype: requests.Session """ session = super(AdalAuthentication, self).signed_session() try: raw_token = self._adal_method(*self._args, **self._kwargs) except adal.AdalError as err: # pylint: disable=no-member if (hasattr(err, 'error_response') and ('error_description' in err.error_response) and ('AADSTS70008:' in err.error_response['error_description'])): raise Expired("Credentials have expired due to inactivity.") else: raise AuthenticationError(err) except ConnectionError as err: raise AuthenticationError('Please ensure you have network connection. Error detail: ' + str(err)) scheme, token = raw_token['tokenType'], raw_token['accessToken'] header = "{} {}".format(scheme, token) session.headers['Authorization'] = header return session msrestazure-0.4.13/msrestazure/azure_exceptions.py0000664000372000037200000001602513147616126023365 0ustar travistravis00000000000000# -------------------------------------------------------------------------- # # Copyright (c) Microsoft Corporation. All rights reserved. # # The MIT License (MIT) # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the ""Software""), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. # # -------------------------------------------------------------------------- from requests import RequestException from msrest.exceptions import ClientException from msrest.serialization import Deserializer from msrest.exceptions import DeserializationError class CloudErrorData(object): """Cloud Error Data object, deserialized from error data returned during a failed REST API call. """ _validation = {} _attribute_map = { 'error': {'key': 'code', 'type': 'str'}, 'message': {'key': 'message', 'type': 'str'}, 'target': {'key': 'target', 'type': 'str'}, 'details': {'key': 'details', 'type': '[CloudErrorData]'}, 'data': {'key': 'values', 'type': '{str}'} } def __init__(self, *args, **kwargs): self.error = kwargs.get('error') self._message = kwargs.get('message') self.request_id = None self.error_time = None self.target = kwargs.get('target') self.details = kwargs.get('details') self.data = kwargs.get('data') super(CloudErrorData, self).__init__(*args) def __str__(self): """Cloud error message.""" error_str = "Azure Error: {}".format(self.error) error_str += "\nMessage: {}".format(self._message) if self.target: error_str += "\nTarget: {}".format(self.target) if self.request_id: error_str += "\nRequest ID: {}".format(self.request_id) if self.error_time: error_str += "\nError Time: {}".format(self.error_time) if self.data: error_str += "\nAdditional Data:" for key, value in self.data.items(): error_str += "\n\t{} : {}".format(key, value) if self.details: error_str += "\nException Details:" for error_obj in self.details: error_str += "\n\tError Code: {}".format(error_obj.error) error_str += "\n\tMessage: {}".format(error_obj.message) error_str += "\n\tTarget: {}".format(error_obj.target) error_bytes = error_str.encode() return error_bytes.decode('ascii') @classmethod def _get_subtype_map(cls): return {} @property def message(self): """Cloud error message.""" return self._message @message.setter def message(self, value): """Attempt to deconstruct error message to retrieve further error data. """ try: value = eval(value) except (SyntaxError, TypeError): pass try: value = value.get('value', value) msg_data = value.split('\n') self._message = msg_data[0] except AttributeError: self._message = value return try: self.request_id = msg_data[1].partition(':')[2] time_str = msg_data[2].partition(':') self.error_time = Deserializer.deserialize_iso( "".join(time_str[2:])) except (IndexError, DeserializationError): pass class CloudError(ClientException): """ClientError, exception raised for failed Azure REST call. Will attempt to deserialize response into meaningful error data. :param requests.Response response: Response object. :param str error: Optional error message. """ def __init__(self, response, error=None, *args, **kwargs): self.deserializer = Deserializer({'CloudErrorData': CloudErrorData}) self.error = None self.message = None self.response = response self.status_code = self.response.status_code self.request_id = None if error: self.message = error self.error = response else: self._build_error_data(response) if not self.error or not self.message: self._build_error_message(response) super(CloudError, self).__init__( self.message, self.error, *args, **kwargs) def __str__(self): """Cloud error message""" if self.error: return str(self.error) return str(self.message) def _build_error_data(self, response): try: data = response.json() except ValueError: data = response else: data = data.get('error', data) try: self.error = self.deserializer(CloudErrorData(), data) except DeserializationError: self.error = None else: if self.error: if not self.error.error or not self.error.message: self.error = None else: self.message = self.error.message def _get_state(self, content): state = content.get("status") if not state: resource_content = content.get('properties', content) state = resource_content.get("provisioningState") return "Resource state {}".format(state) if state else "none" def _build_error_message(self, response): try: data = response.json() except ValueError: message = "none" else: message = data.get("message", self._get_state(data)) try: response.raise_for_status() except RequestException as err: if not self.error: self.error = err if not self.message: if message == "none": message = str(err) msg = "Operation failed with status: {!r}. Details: {}" self.message = msg.format(response.reason, message) else: if not self.error: self.error = response if not self.message: msg = "Operation failed with status: {!r}. Details: {}" self.message = msg.format( response.status_code, message)msrestazure-0.4.13/msrestazure/azure_configuration.py0000664000372000037200000000726313147616126024057 0ustar travistravis00000000000000# -------------------------------------------------------------------------- # # Copyright (c) Microsoft Corporation. All rights reserved. # # The MIT License (MIT) # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the ""Software""), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. # # -------------------------------------------------------------------------- try: from configparser import NoOptionError except ImportError: from ConfigParser import NoOptionError import logging from msrest import Configuration from msrest.exceptions import raise_with_traceback from .version import msrestazure_version from .tools import register_rp_hook _LOGGER = logging.getLogger(__name__) class AzureConfiguration(Configuration): """Azure specific client configuration. :param str base_url: REST Service base URL. :param str filepath: Path to an existing config file (optional). """ def __init__(self, base_url, filepath=None): super(AzureConfiguration, self).__init__(base_url, filepath) self.long_running_operation_timeout = 30 self.accept_language = 'en-US' self.generate_client_request_id = True self.add_user_agent("msrest_azure/{}".format(msrestazure_version)) # Check if "hasattr", just in case msrest is older than msrestazure if hasattr(self, 'hooks'): self.hooks.append(register_rp_hook) else: _LOGGER.warning(("Your 'msrest' version is too old to activate all the " "features of 'msrestazure'. Please update using" "'pip install -U msrest'")) def save(self, filepath): """Save current configuration to file. :param str filepath: Path to save file to. :raises: ValueError if supplied filepath cannot be written to. :rtype: None """ self._config.add_section("Azure") self._config.set("Azure", "long_running_operation_timeout", self.long_running_operation_timeout) return super(AzureConfiguration, self).save(filepath) def load(self, filepath): """Load configuration from existing file. :param str filepath: Path to existing config file. :raises: ValueError if supplied config file is invalid. :rtype: None """ try: self._config.read(filepath) self.long_running_operation_timeout = self._config.getint( "Azure", "long_running_operation_timeout") except (ValueError, EnvironmentError, NoOptionError): msg = "Supplied config file incompatible" raise_with_traceback(ValueError, msg) finally: self._clear_config() return super(AzureConfiguration, self).load(filepath) msrestazure-0.4.13/msrestazure/azure_cloud.py0000664000372000037200000002556713147616126022325 0ustar travistravis00000000000000# -------------------------------------------------------------------------- # # Copyright (c) Microsoft Corporation. All rights reserved. # # The MIT License (MIT) # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the ""Software""), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. # # -------------------------------------------------------------------------- import os import logging from pprint import pformat _LOGGER = logging.getLogger(__name__) # The exact API version doesn't matter too much right now. It just has to be YYYY-MM-DD format. METADATA_ENDPOINT_SUFFIX = '/metadata/endpoints?api-version=2015-01-01' class CloudEndpointNotSetException(Exception): pass class CloudSuffixNotSetException(Exception): pass class MetadataEndpointError(Exception): pass class CloudEndpoints(object): # pylint: disable=too-few-public-methods,too-many-instance-attributes def __init__(self, management=None, resource_manager=None, sql_management=None, batch_resource_id=None, gallery=None, active_directory=None, active_directory_resource_id=None, active_directory_graph_resource_id=None): # Attribute names are significant. They are used when storing/retrieving clouds from config self.management = management self.resource_manager = resource_manager self.sql_management = sql_management self.batch_resource_id = batch_resource_id self.gallery = gallery self.active_directory = active_directory self.active_directory_resource_id = active_directory_resource_id self.active_directory_graph_resource_id = active_directory_graph_resource_id def has_endpoint_set(self, endpoint_name): try: # Can't simply use hasattr here as we override __getattribute__ below. # Python 3 hasattr() only returns False if an AttributeError is raised but we raise # CloudEndpointNotSetException. This exception is not a subclass of AttributeError. getattr(self, endpoint_name) return True except Exception: # pylint: disable=broad-except return False def __getattribute__(self, name): val = object.__getattribute__(self, name) if val is None: raise CloudEndpointNotSetException("The endpoint '{}' for this cloud " "is not set but is used.".format(name)) return val class CloudSuffixes(object): # pylint: disable=too-few-public-methods def __init__(self, storage_endpoint=None, keyvault_dns=None, sql_server_hostname=None, azure_datalake_store_file_system_endpoint=None, azure_datalake_analytics_catalog_and_job_endpoint=None): # Attribute names are significant. They are used when storing/retrieving clouds from config self.storage_endpoint = storage_endpoint self.keyvault_dns = keyvault_dns self.sql_server_hostname = sql_server_hostname self.azure_datalake_store_file_system_endpoint = azure_datalake_store_file_system_endpoint self.azure_datalake_analytics_catalog_and_job_endpoint = azure_datalake_analytics_catalog_and_job_endpoint # pylint: disable=line-too-long def __getattribute__(self, name): val = object.__getattribute__(self, name) if val is None: raise CloudSuffixNotSetException("The suffix '{}' for this cloud " "is not set but is used.".format(name)) return val class Cloud(object): # pylint: disable=too-few-public-methods """ Represents an Azure Cloud instance """ def __init__(self, name, endpoints=None, suffixes=None): self.name = name self.endpoints = endpoints or CloudEndpoints() self.suffixes = suffixes or CloudSuffixes() def __str__(self): o = { 'name': self.name, 'endpoints': vars(self.endpoints), 'suffixes': vars(self.suffixes), } return pformat(o) AZURE_PUBLIC_CLOUD = Cloud( 'AzureCloud', endpoints=CloudEndpoints( management='https://management.core.windows.net/', resource_manager='https://management.azure.com/', sql_management='https://management.core.windows.net:8443/', batch_resource_id='https://batch.core.windows.net/', gallery='https://gallery.azure.com/', active_directory='https://login.microsoftonline.com', active_directory_resource_id='https://management.core.windows.net/', active_directory_graph_resource_id='https://graph.windows.net/'), suffixes=CloudSuffixes( storage_endpoint='core.windows.net', keyvault_dns='.vault.azure.net', sql_server_hostname='.database.windows.net', azure_datalake_store_file_system_endpoint='azuredatalakestore.net', azure_datalake_analytics_catalog_and_job_endpoint='azuredatalakeanalytics.net')) AZURE_CHINA_CLOUD = Cloud( 'AzureChinaCloud', endpoints=CloudEndpoints( management='https://management.core.chinacloudapi.cn/', resource_manager='https://management.chinacloudapi.cn', sql_management='https://management.core.chinacloudapi.cn:8443/', batch_resource_id='https://batch.chinacloudapi.cn/', gallery='https://gallery.chinacloudapi.cn/', active_directory='https://login.chinacloudapi.cn', active_directory_resource_id='https://management.core.chinacloudapi.cn/', active_directory_graph_resource_id='https://graph.chinacloudapi.cn/'), suffixes=CloudSuffixes( storage_endpoint='core.chinacloudapi.cn', keyvault_dns='.vault.azure.cn', sql_server_hostname='.database.chinacloudapi.cn')) AZURE_US_GOV_CLOUD = Cloud( 'AzureUSGovernment', endpoints=CloudEndpoints( management='https://management.core.usgovcloudapi.net/', resource_manager='https://management.usgovcloudapi.net/', sql_management='https://management.core.usgovcloudapi.net:8443/', batch_resource_id='https://batch.core.usgovcloudapi.net/', gallery='https://gallery.usgovcloudapi.net/', active_directory='https://login-us.microsoftonline.com', active_directory_resource_id='https://management.core.usgovcloudapi.net/', active_directory_graph_resource_id='https://graph.windows.net/'), suffixes=CloudSuffixes( storage_endpoint='core.usgovcloudapi.net', keyvault_dns='.vault.usgovcloudapi.net', sql_server_hostname='.database.usgovcloudapi.net')) AZURE_GERMAN_CLOUD = Cloud( 'AzureGermanCloud', endpoints=CloudEndpoints( management='https://management.core.cloudapi.de/', resource_manager='https://management.microsoftazure.de', sql_management='https://management.core.cloudapi.de:8443/', batch_resource_id='https://batch.cloudapi.de/', gallery='https://gallery.cloudapi.de/', active_directory='https://login.microsoftonline.de', active_directory_resource_id='https://management.core.cloudapi.de/', active_directory_graph_resource_id='https://graph.cloudapi.de/'), suffixes=CloudSuffixes( storage_endpoint='core.cloudapi.de', keyvault_dns='.vault.microsoftazure.de', sql_server_hostname='.database.cloudapi.de')) def _populate_from_metadata_endpoint(cloud, arm_endpoint): endpoints_in_metadata = ['active_directory_graph_resource_id', 'active_directory_resource_id', 'active_directory'] if not arm_endpoint or all([cloud.endpoints.has_endpoint_set(n) for n in endpoints_in_metadata]): return try: error_msg_fmt = "Unable to get endpoints from the cloud.\n{}" import requests metadata_endpoint = arm_endpoint + METADATA_ENDPOINT_SUFFIX response = requests.get(metadata_endpoint) if response.status_code == 200: metadata = response.json() if not cloud.endpoints.has_endpoint_set('gallery'): setattr(cloud.endpoints, 'gallery', metadata.get('galleryEndpoint')) if not cloud.endpoints.has_endpoint_set('active_directory_graph_resource_id'): setattr(cloud.endpoints, 'active_directory_graph_resource_id', metadata.get('graphEndpoint')) if not cloud.endpoints.has_endpoint_set('active_directory'): setattr(cloud.endpoints, 'active_directory', metadata['authentication'].get('loginEndpoint')) if not cloud.endpoints.has_endpoint_set('active_directory_resource_id'): setattr(cloud.endpoints, 'active_directory_resource_id', metadata['authentication']['audiences'][0]) else: msg = 'Server returned status code {} for {}'.format(response.status_code, metadata_endpoint) raise MetadataEndpointError(error_msg_fmt.format(msg)) except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError) as err: msg = 'Please ensure you have network connection. Error detail: {}'.format(str(err)) raise MetadataEndpointError(error_msg_fmt.format(msg)) except ValueError as err: msg = 'Response body does not contain valid json. Error detail: {}'.format(str(err)) raise MetadataEndpointError(error_msg_fmt.format(msg)) def get_cloud_from_metadata_endpoint(arm_endpoint, name=None): """Get a Cloud object from an ARM endpoint. .. versionadded:: 0.4.11 :Example: .. code:: python get_cloud_from_metadata_endpoint(https://management.azure.com/, "Public Azure") :param str arm_endpoint: The ARM management endpoint :param str name: An optional name for the Cloud object. Otherwise it's the ARM endpoint :rtype Cloud: :returns: a Cloud object :raises: MetadataEndpointError if unable to build the Cloud object """ cloud = Cloud(name or arm_endpoint) _populate_from_metadata_endpoint(cloud, arm_endpoint) return cloud msrestazure-0.4.13/msrestazure/tools.py0000664000372000037200000000765413147616126021146 0ustar travistravis00000000000000# -------------------------------------------------------------------------- # # Copyright (c) Microsoft Corporation. All rights reserved. # # The MIT License (MIT) # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the ""Software""), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. # # -------------------------------------------------------------------------- import json import re import logging import time import uuid _LOGGER = logging.getLogger(__name__) def register_rp_hook(r, *args, **kwargs): """This is a requests hook to register RP automatically. See requests documentation for details of the signature of this function. http://docs.python-requests.org/en/master/user/advanced/#event-hooks """ if r.status_code == 409 and 'msrest' in kwargs: rp_name = _check_rp_not_registered_err(r) if rp_name: session = kwargs['msrest']['session'] url_prefix = _extract_subscription_url(r.request.url) if not _register_rp(session, url_prefix, rp_name): return req = r.request # Change the 'x-ms-client-request-id' otherwise the Azure endpoint # just returns the same 409 payload without looking at the actual query if 'x-ms-client-request-id' in req.headers: req.headers['x-ms-client-request-id'] = str(uuid.uuid1()) return session.send(req) def _check_rp_not_registered_err(response): try: response = json.loads(response.content.decode()) if response['error']['code'] == 'MissingSubscriptionRegistration': match = re.match(r".*'(.*)'", response['error']['message']) return match.group(1) except Exception: # pylint: disable=broad-except pass return None def _extract_subscription_url(url): """Extract the first part of the URL, just after subscription: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/ """ match = re.match(r".*/subscriptions/[a-f0-9-]+/", url, re.IGNORECASE) if not match: raise ValueError("Unable to extract subscription ID from URL") return match.group(0) def _register_rp(session, url_prefix, rp_name): """Synchronously register the RP is paremeter. Return False if we have a reason to believe this didn't work """ post_url = "{}providers/{}/register?api-version=2016-02-01".format(url_prefix, rp_name) get_url = "{}providers/{}?api-version=2016-02-01".format(url_prefix, rp_name) _LOGGER.warning("Resource provider '%s' used by this operation is not " "registered. We are registering for you.", rp_name) post_response = session.post(post_url) if post_response.status_code != 200: _LOGGER.warning("Registration failed. Please register manually.") return False while True: time.sleep(10) rp_info = session.get(get_url).json() if rp_info['registrationState'] == 'Registered': _LOGGER.warning("Registration succeeded.") return True msrestazure-0.4.13/msrestazure/__init__.py0000664000372000037200000000267613147616126021544 0ustar travistravis00000000000000# -------------------------------------------------------------------------- # # Copyright (c) Microsoft Corporation. All rights reserved. # # The MIT License (MIT) # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the ""Software""), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. # # -------------------------------------------------------------------------- from .azure_configuration import AzureConfiguration from .version import msrestazure_version __all__ = ["AzureConfiguration"] __version__ = msrestazure_version