Hello community,

here is the log from the commit of package azure-cli-core for openSUSE:Factory 
checked in at 2020-10-13 15:44:31
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/azure-cli-core (Old)
 and      /work/SRC/openSUSE:Factory/.azure-cli-core.new.3486 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "azure-cli-core"

Tue Oct 13 15:44:31 2020 rev:19 rq:841540 version:2.13.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/azure-cli-core/azure-cli-core.changes    
2020-10-02 17:32:48.394577620 +0200
+++ /work/SRC/openSUSE:Factory/.azure-cli-core.new.3486/azure-cli-core.changes  
2020-10-13 15:45:50.209454658 +0200
@@ -1,0 +2,9 @@
+Tue Oct 13 09:35:54 UTC 2020 - John Paul Adrian Glaubitz 
<adrian.glaub...@suse.com>
+
+- New upstream release
+  + Version 2.13.0
+  + For detailed information about changes see the
+    HISTORY.rst file provided with this package
+- Override upstream version in Requires for python-azure-mgmt-resource
+
+-------------------------------------------------------------------

Old:
----
  azure-cli-core-2.12.1.tar.gz

New:
----
  azure-cli-core-2.13.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ azure-cli-core.spec ++++++
--- /var/tmp/diff_new_pack.qHx9Ua/_old  2020-10-13 15:45:50.941454974 +0200
+++ /var/tmp/diff_new_pack.qHx9Ua/_new  2020-10-13 15:45:50.941454974 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           azure-cli-core
-Version:        2.12.1
+Version:        2.13.0
 Release:        0
 Summary:        Microsoft Azure CLI Core Module
 License:        MIT
@@ -43,8 +43,8 @@
 Requires:       python3-argcomplete >= 1.8
 Requires:       python3-azure-mgmt-core < 2.0.0
 Requires:       python3-azure-mgmt-core >= 1.2.0
-Requires:       python3-azure-mgmt-resource < 11.0.0
-Requires:       python3-azure-mgmt-resource >= 10.2.0
+Requires:       python3-azure-mgmt-resource < 16.0.0
+Requires:       python3-azure-mgmt-resource >= 15.0.0
 Requires:       python3-azure-nspkg >= 3.0.0
 Requires:       python3-colorama >= 0.4.1
 Requires:       python3-humanfriendly < 9.0

++++++ azure-cli-core-2.12.1.tar.gz -> azure-cli-core-2.13.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure-cli-core-2.12.1/HISTORY.rst 
new/azure-cli-core-2.13.0/HISTORY.rst
--- old/azure-cli-core-2.12.1/HISTORY.rst       2020-09-28 10:37:30.000000000 
+0200
+++ new/azure-cli-core-2.13.0/HISTORY.rst       2020-10-12 09:58:16.000000000 
+0200
@@ -3,6 +3,10 @@
 Release History
 ===============
 
+2.13.0
+++++++
+* Minor fixes
+
 2.12.1
 ++++++
 * No changes
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure-cli-core-2.12.1/PKG-INFO 
new/azure-cli-core-2.13.0/PKG-INFO
--- old/azure-cli-core-2.12.1/PKG-INFO  2020-09-28 10:37:45.000000000 +0200
+++ new/azure-cli-core-2.13.0/PKG-INFO  2020-10-12 09:58:34.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: azure-cli-core
-Version: 2.12.1
+Version: 2.13.0
 Summary: Microsoft Azure Command-Line Tools Core Module
 Home-page: https://github.com/Azure/azure-cli
 Author: Microsoft Corporation
@@ -15,6 +15,10 @@
         Release History
         ===============
         
+        2.13.0
+        ++++++
+        * Minor fixes
+        
         2.12.1
         ++++++
         * No changes
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure-cli-core-2.12.1/azure/cli/core/__init__.py 
new/azure-cli-core-2.13.0/azure/cli/core/__init__.py
--- old/azure-cli-core-2.12.1/azure/cli/core/__init__.py        2020-09-28 
10:37:30.000000000 +0200
+++ new/azure-cli-core-2.13.0/azure/cli/core/__init__.py        2020-10-12 
09:58:16.000000000 +0200
@@ -6,7 +6,7 @@
 
 from __future__ import print_function
 
-__version__ = "2.12.1"
+__version__ = "2.13.0"
 
 import os
 import sys
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure-cli-core-2.12.1/azure/cli/core/adal_authentication.py 
new/azure-cli-core-2.13.0/azure/cli/core/adal_authentication.py
--- old/azure-cli-core-2.12.1/azure/cli/core/adal_authentication.py     
2020-09-28 10:37:30.000000000 +0200
+++ new/azure-cli-core-2.13.0/azure/cli/core/adal_authentication.py     
2020-10-12 09:58:16.000000000 +0200
@@ -36,7 +36,7 @@
             if in_cloud_console():
                 AdalAuthentication._log_hostname()
 
-            err = (getattr(err, 'error_response', None) or 
{}).get('error_description') or ''
+            err = (getattr(err, 'error_response', None) or 
{}).get('error_description') or str(err)
             if 'AADSTS70008' in err:  # all errors starting with 70008 should 
be creds expiration related
                 raise CLIError("Credentials have expired due to inactivity. 
{}".format(
                     "Please run 'az login'" if not in_cloud_console() else ''))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure-cli-core-2.12.1/azure/cli/core/azclierror.py 
new/azure-cli-core-2.13.0/azure/cli/core/azclierror.py
--- old/azure-cli-core-2.12.1/azure/cli/core/azclierror.py      2020-09-28 
10:37:30.000000000 +0200
+++ new/azure-cli-core-2.13.0/azure/cli/core/azclierror.py      2020-10-12 
09:58:16.000000000 +0200
@@ -4,85 +4,209 @@
 # 
--------------------------------------------------------------------------------------------
 
 import sys
-from enum import Enum
 
+import azure.cli.core.telemetry as telemetry
 from knack.util import CLIError
 from knack.log import get_logger
 
 logger = get_logger(__name__)
+# pylint: disable=unnecessary-pass
 
 
-class AzCLIErrorType(Enum):
-    """ AzureCLI error types """
-
-    # userfaults
-    CommandNotFoundError = 'CommandNotFoundError'
-    ArgumentParseError = 'ArgumentParseError'
-    ValidationError = 'ValidationError'
-    ManualInterrupt = 'ManualInterrupt'
-    # service side error
-    ServiceError = 'ServiceError'
-    # client side error
-    ClientError = 'ClientError'
-    # unexpected error
-    UnexpectedError = 'UnexpectedError'
+# Error types in AzureCLI are from different sources, and there are many 
general error types like CLIError, AzureError.
+# Besides, many error types with different names are actually showing the same 
kind of error.
+# For example, CloudError, CLIError and ValidtionError all could be a 
resource-not-found error.
+# Therefore, here we define the new error classes to map and categorize all of 
the error types from different sources.
 
 
+# region: Base Layer
+# Base class for all the AzureCLI defined error classes.
+# DO NOT raise the error class here directly in your codes.
 class AzCLIError(CLIError):
-    """ AzureCLI error definition """
+    """ Base class for all the AzureCLI defined error classes. """
 
-    def __init__(self, error_type, error_msg, raw_exception=None, 
command=None):
-        """
-        :param error_type: The name of the AzureCLI error type.
-        :type error_type: azure.cli.core.util.AzCLIErrorType
-        :param error_msg: The error message detail.
-        :type error_msg: str
-        :param raw_exception: The raw exception.
-        :type raw_exception: Exception
-        :param command: The command which brings the error.
-        :type command: str
-        :param recommendations: The recommendations to resolve the error.
-        :type recommendations: list
-        """
-        self.error_type = error_type
+    def __init__(self, error_msg, recommendation=None):
+        # error message
         self.error_msg = error_msg
-        self.raw_exception = raw_exception
-        self.command = command
+
+        # set recommendations to fix the error if the message is not 
actionable,
+        # they will be printed to users after the error message, one 
recommendation per line
         self.recommendations = []
+        if isinstance(recommendation, str):
+            self.recommendations = [recommendation]
+        elif isinstance(recommendation, list):
+            self.recommendations = recommendation
+
+        # exception trace for the error
+        self.exception_trace = None
         super().__init__(error_msg)
 
     def set_recommendation(self, recommendation):
         self.recommendations.append(recommendation)
 
-    def set_raw_exception(self, raw_exception):
-        self.raw_exception = raw_exception
+    def set_exception_trace(self, exception_trace):
+        self.exception_trace = exception_trace
 
     def print_error(self):
         from azure.cli.core.azlogging import CommandLoggerContext
         with CommandLoggerContext(logger):
-            message = '{}: {}'.format(self.error_type.value, self.error_msg)
+            # print error type and error message
+            message = '{}: {}'.format(self.__class__.__name__, self.error_msg)
             logger.error(message)
-            if self.raw_exception:
-                logger.exception(self.raw_exception)
+            # print exception trace if there is
+            if self.exception_trace:
+                logger.exception(self.exception_trace)
+            # print recommendations to action
             if self.recommendations:
                 for recommendation in self.recommendations:
                     print(recommendation, file=sys.stderr)
 
     def send_telemetry(self):
-        import azure.cli.core.telemetry as telemetry
-        telemetry.set_error_type(self.error_type.value)
+        telemetry.set_error_type(self.__class__.__name__)
+# endregion
+
+
+# region: Second Layer
+# Main categories of the AzureCLI error types, used for Telemetry analysis
+# DO NOT raise the error classes here directly in your codes.
+class UserFault(AzCLIError):
+    """ Users should be responsible for the errors. """
+    def send_telemetry(self):
+        super().send_telemetry()
+        telemetry.set_user_fault(self.error_msg)
+
+
+class ServiceError(AzCLIError):
+    """ Azure Services should be responsible for the errors.  """
+    def send_telemetry(self):
+        super().send_telemetry()
+        telemetry.set_failure(self.error_msg)
+
+
+class ClientError(AzCLIError):
+    """ AzureCLI should be responsible for the errors. """
+    def send_telemetry(self):
+        super().send_telemetry()
+        telemetry.set_failure(self.error_msg)
+        if self.exception_trace:
+            telemetry.set_exception(self.exception_trace, '')
+# endregion
+
+
+# region: Third Layer
+# Sub-categories of the AzureCLI error types, shown to users
+# Raise the error classes here in your codes
+# Avoid using fallback error classes unless you can not find a proper one
+# Command related error types
+class CommandNotFoundError(UserFault):
+    """ Command is misspelled or not recognized by AzureCLI. """
+    pass
+
+
+# Argument related error types
+class UnrecognizedArgumentError(UserFault):
+    """ Argument is misspelled or not recognized by AzureCLI. """
+    pass
+
+
+class RequiredArgumentMissingError(UserFault):
+    """ Required argument is not specified. """
+    pass
+
+
+class MutuallyExclusiveArgumentError(UserFault):
+    """ Arguments can not be specfied together. """
+    pass
+
+
+class InvalidArgumentValueError(UserFault):
+    """ Argument value is not valid. """
+    pass
+
+
+class ArgumentParseError(UserFault):
+    """ Fallback of the argument parsing related errors.
+    Avoid using this class unless the error can not be classified
+    into the above Argument related error types. """
+    pass
+
+
+# Response related error types
+class BadRequestError(UserFault):
+    """ Bad request from client: 400 error """
+    pass
+
+
+class UnauthorizedError(UserFault):
+    """ Unauthorized request: 401 error """
+
+
+class ForbiddenError(UserFault):
+    """ Service refuse to response: 403 error """
+
+
+class ResourceNotFoundError(UserFault):
+    """ Can not find Azure resources: 404 error """
+    pass
+
+
+class AzureInternalError(ServiceError):
+    """ Azure service internal error: 5xx error """
+    pass
+
+
+class AzureResponseError(UserFault):
+    """ Fallback of the response related errors.
+    Avoid using this class unless the error can not be classified
+    into the above Response related error types. """
+
+
+# Request related error types
+class AzureConnectionError(UserFault):
+    """ Connection issues like connection timeout, aborted or broken. """
+    pass
+
+
+class ClientRequestError(UserFault):
+    """ Fallback of the request related errors. Error occurs while attempting
+    to make a request to the service. No request is sent.
+    Avoid using this class unless the error can not be classified
+    into the above Request related errors types. """
+
+
+# Validation related error types
+class ValidationError(UserFault):
+    """ Fallback of the errors in validation functions.
+    Avoid using this class unless the error can not be classified into
+    the Argument, Request and Response related error types above. """
+    pass
+
+
+# CLI internal error type
+class CLIInternalError(ClientError):
+    """ AzureCLI internal error """
+    pass
+
+
+# Keyboard interrupt error type
+class ManualInterrupt(UserFault):
+    """ Keyboard interrupt. """
+    pass
+
+
+# Unknow error type
+class UnknownError(UserFault):
+    """ Reserved for the errors which can not be categorized into the error 
types above.
+    Usually for the very general error type like CLIError, AzureError.
+    Error type info will not printed to users for this class. """
+    def print_error(self):
+        from azure.cli.core.azlogging import CommandLoggerContext
+        with CommandLoggerContext(logger):
+            # print only error message (no error type)
+            logger.error(self.error_msg)
+            # print recommendations to action
+            if self.recommendations:
+                for recommendation in self.recommendations:
+                    print(recommendation, file=sys.stderr)
 
-        # For userfaults
-        if self.error_type in [AzCLIErrorType.CommandNotFoundError,
-                               AzCLIErrorType.ArgumentParseError,
-                               AzCLIErrorType.ValidationError,
-                               AzCLIErrorType.ManualInterrupt]:
-            telemetry.set_user_fault(self.error_msg)
-
-        # For failures: service side error, client side error, unexpected error
-        else:
-            telemetry.set_failure(self.error_msg)
-
-        # For unexpected error
-        if self.raw_exception:
-            telemetry.set_exception(self.raw_exception, '')
+# endregion
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure-cli-core-2.12.1/azure/cli/core/cloud.py 
new/azure-cli-core-2.13.0/azure/cli/core/cloud.py
--- old/azure-cli-core-2.12.1/azure/cli/core/cloud.py   2020-09-28 
10:37:30.000000000 +0200
+++ new/azure-cli-core-2.13.0/azure/cli/core/cloud.py   2020-10-12 
09:58:16.000000000 +0200
@@ -78,6 +78,7 @@
                  app_insights_telemetry_channel_resource_id=None,
                  synapse_analytics_resource_id=None,
                  attestation_resource_id=None,
+                 portal=None,
                  **kwargs):  # To support init with __dict__ for 
deserialization
         # Attribute names are significant. They are used when 
storing/retrieving clouds from config
         self.management = management
@@ -98,6 +99,7 @@
         self.app_insights_telemetry_channel_resource_id = 
app_insights_telemetry_channel_resource_id
         self.synapse_analytics_resource_id = synapse_analytics_resource_id
         self.attestation_resource_id = attestation_resource_id
+        self.portal = portal
 
     def has_endpoint_set(self, endpoint_name):
         try:
@@ -309,7 +311,8 @@
             log_analytics_resource_id=get_endpoint('logAnalyticsResourceId', 
fallback_value=_get_log_analytics_resource_id(arm_dict['name'])),
             
synapse_analytics_resource_id=get_endpoint('synapseAnalyticsResourceId', 
fallback_value=_get_synapse_analytics_resource_id(arm_dict['name'])),
             
app_insights_telemetry_channel_resource_id=get_endpoint('appInsightsTelemetryChannelResourceId',
 
fallback_value=_get_app_insights_telemetry_channel_resource_id(arm_dict['name'])),
-            attestation_resource_id=get_endpoint('attestationResourceId', 
fallback_value=_get_attestation_resource_id(arm_dict['name']))),
+            attestation_resource_id=get_endpoint('attestationResourceId', 
fallback_value=_get_attestation_resource_id(arm_dict['name'])),
+            portal=arm_dict['portal'] if 'portal' in arm_dict else None),
         suffixes=CloudSuffixes(
             storage_endpoint=get_suffix('storage'),
             storage_sync_endpoint=get_suffix('storageSyncEndpointSuffix', 
fallback_value=_get_storage_sync_endpoint(arm_dict['name'])),
@@ -381,7 +384,8 @@
         log_analytics_resource_id='https://api.loganalytics.io',
         
app_insights_telemetry_channel_resource_id='https://dc.applicationinsights.azure.com/v2/track',
         synapse_analytics_resource_id='https://dev.azuresynapse.net',
-        attestation_resource_id='https://attest.azure.net'),
+        attestation_resource_id='https://attest.azure.net',
+        portal='https://portal.azure.com'),
     suffixes=CloudSuffixes(
         storage_endpoint='core.windows.net',
         storage_sync_endpoint='afs.azure.net',
@@ -415,7 +419,8 @@
         app_insights_resource_id='https://api.applicationinsights.azure.cn',
         log_analytics_resource_id='https://api.loganalytics.azure.cn',
         
app_insights_telemetry_channel_resource_id='https://dc.applicationinsights.azure.cn/v2/track',
-        synapse_analytics_resource_id='https://dev.azuresynapse.net'),
+        synapse_analytics_resource_id='https://dev.azuresynapse.net',
+        portal='https://portal.azure.cn'),
     suffixes=CloudSuffixes(
         storage_endpoint='core.chinacloudapi.cn',
         keyvault_dns='.vault.azure.cn',
@@ -444,7 +449,8 @@
         ossrdbms_resource_id='https://ossrdbms-aad.database.usgovcloudapi.net',
         app_insights_resource_id='https://api.applicationinsights.us',
         log_analytics_resource_id='https://api.loganalytics.us',
-        
app_insights_telemetry_channel_resource_id='https://dc.applicationinsights.us/v2/track'),
+        
app_insights_telemetry_channel_resource_id='https://dc.applicationinsights.us/v2/track',
+        portal='https://portal.azure.us'),
     suffixes=CloudSuffixes(
         storage_endpoint='core.usgovcloudapi.net',
         storage_sync_endpoint='afs.azure.us',
@@ -470,7 +476,8 @@
         microsoft_graph_resource_id='https://graph.microsoft.de',
         
vm_image_alias_doc='https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/arm-compute/quickstart-templates/aliases.json',
         media_resource_id='https://rest.media.cloudapi.de',
-        ossrdbms_resource_id='https://ossrdbms-aad.database.cloudapi.de'),
+        ossrdbms_resource_id='https://ossrdbms-aad.database.cloudapi.de',
+        portal='https://portal.microsoftazure.de'),
     suffixes=CloudSuffixes(
         storage_endpoint='core.cloudapi.de',
         keyvault_dns='.vault.microsoftazure.de',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure-cli-core-2.12.1/azure/cli/core/command_recommender.py 
new/azure-cli-core-2.13.0/azure/cli/core/command_recommender.py
--- old/azure-cli-core-2.12.1/azure/cli/core/command_recommender.py     
2020-09-28 10:37:30.000000000 +0200
+++ new/azure-cli-core-2.13.0/azure/cli/core/command_recommender.py     
2020-10-12 09:58:16.000000000 +0200
@@ -118,9 +118,11 @@
                 },
                 headers=headers,
                 timeout=1)
+            telemetry.set_debug_info('AladdinResponseTime', 
response.elapsed.total_seconds())
+
         except RequestException as ex:
             logger.debug('Recommendation requests.get() exception: %s', ex)
-            telemetry.set_debug_info('AladdinRecommendationService', 
ex.__class__.__name__)
+            telemetry.set_debug_info('AladdinException', ex.__class__.__name__)
 
         recommendations = []
         if response and response.status_code == HTTPStatus.OK:
@@ -174,8 +176,19 @@
         elif self.aladdin_recommendations:
             recommend_command = self.aladdin_recommendations[0]
 
+        # set the recommened command into Telemetry
+        self._set_recommended_command_to_telemetry(recommend_command)
+
         return recommend_command
 
+    def _set_recommended_command_to_telemetry(self, recommend_command):
+        """Set the recommended command to Telemetry for analysis. """
+
+        if recommend_command in self.aladdin_recommendations:
+            telemetry.set_debug_info('AladdinRecommendCommand', 
recommend_command)
+        elif recommend_command:
+            telemetry.set_debug_info('ExampleRecommendCommand', 
recommend_command)
+
     def _disable_aladdin_service(self):
         """Decide whether to disable aladdin request when a command fails.
         The possible cases to disable it are:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure-cli-core-2.12.1/azure/cli/core/commands/__init__.py 
new/azure-cli-core-2.13.0/azure/cli/core/commands/__init__.py
--- old/azure-cli-core-2.12.1/azure/cli/core/commands/__init__.py       
2020-09-28 10:37:30.000000000 +0200
+++ new/azure-cli-core-2.13.0/azure/cli/core/commands/__init__.py       
2020-10-12 09:58:16.000000000 +0200
@@ -845,7 +845,6 @@
             pass
 
     def _validate_arg_level(self, ns, **_):  # pylint: disable=no-self-use
-        from azure.cli.core.azclierror import AzCLIErrorType
         from azure.cli.core.azclierror import AzCLIError
         for validator in getattr(ns, '_argument_validators', []):
             try:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure-cli-core-2.12.1/azure/cli/core/commands/arm.py 
new/azure-cli-core-2.13.0/azure/cli/core/commands/arm.py
--- old/azure-cli-core-2.12.1/azure/cli/core/commands/arm.py    2020-09-28 
10:37:30.000000000 +0200
+++ new/azure-cli-core-2.13.0/azure/cli/core/commands/arm.py    2020-10-12 
09:58:16.000000000 +0200
@@ -762,10 +762,9 @@
     if getattr(getattr(ex, 'response', ex), 'status_code', None) == 404:
         import sys
         from azure.cli.core.azlogging import CommandLoggerContext
-        from azure.cli.core.azclierror import AzCLIErrorType
-        from azure.cli.core.azclierror import AzCLIError
+        from azure.cli.core.azclierror import ResourceNotFoundError
         with CommandLoggerContext(logger):
-            az_error = AzCLIError(AzCLIErrorType.ValidationError, getattr(ex, 
'message', ex))
+            az_error = ResourceNotFoundError(getattr(ex, 'message', ex))
             az_error.print_error()
             az_error.send_telemetry()
             sys.exit(3)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure-cli-core-2.12.1/azure/cli/core/parser.py 
new/azure-cli-core-2.13.0/azure/cli/core/parser.py
--- old/azure-cli-core-2.12.1/azure/cli/core/parser.py  2020-09-28 
10:37:30.000000000 +0200
+++ new/azure-cli-core-2.13.0/azure/cli/core/parser.py  2020-10-12 
09:58:16.000000000 +0200
@@ -17,9 +17,13 @@
 from azure.cli.core.commands import ExtensionCommandSource
 from azure.cli.core.commands import AzCliCommandInvoker
 from azure.cli.core.commands.events import EVENT_INVOKER_ON_TAB_COMPLETION
-from azure.cli.core.azclierror import AzCLIErrorType
-from azure.cli.core.azclierror import AzCLIError
 from azure.cli.core.command_recommender import CommandRecommender
+from azure.cli.core.azclierror import UnrecognizedArgumentError
+from azure.cli.core.azclierror import RequiredArgumentMissingError
+from azure.cli.core.azclierror import InvalidArgumentValueError
+from azure.cli.core.azclierror import ArgumentParseError
+from azure.cli.core.azclierror import CommandNotFoundError
+from azure.cli.core.azclierror import ValidationError
 
 from knack.log import get_logger
 from knack.parser import CLICommandParser
@@ -155,7 +159,7 @@
                 _parser=command_parser)
 
     def validation_error(self, message):
-        az_error = AzCLIError(AzCLIErrorType.ValidationError, message, 
command=self.prog)
+        az_error = ValidationError(message)
         az_error.print_error()
         az_error.send_telemetry()
         self.exit(2)
@@ -168,7 +172,14 @@
         recommender.set_help_examples(self.get_examples(self.prog))
         recommendation = recommender.recommend_a_command()
 
-        az_error = AzCLIError(AzCLIErrorType.ArgumentParseError, message, 
command=self.prog)
+        az_error = ArgumentParseError(message)
+        if 'unrecognized arguments' in message:
+            az_error = UnrecognizedArgumentError(message)
+        elif 'arguments are required' in message:
+            az_error = RequiredArgumentMissingError(message)
+        elif 'invalid' in message:
+            az_error = InvalidArgumentValueError(message)
+
         if '--query' in message:
             from azure.cli.core.util import QUERY_REFERENCE
             az_error.set_recommendation(QUERY_REFERENCE)
@@ -457,7 +468,7 @@
                 if not error_msg:
                     # parser has no `command_source`, value is part of command 
itself
                     error_msg = "'{value}' is misspelled or not recognized by 
the system.".format(value=value)
-                az_error = AzCLIError(AzCLIErrorType.CommandNotFoundError, 
error_msg, command=self.prog)
+                az_error = CommandNotFoundError(error_msg)
 
             else:
                 # `command_source` indicates command values have been parsed, 
value is an argument
@@ -465,7 +476,7 @@
                 error_msg = "{prog}: '{value}' is not a valid value for 
'{param}'.".format(
                     prog=self.prog, value=value, param=parameter)
                 candidates = difflib.get_close_matches(value, action.choices, 
cutoff=0.7)
-                az_error = AzCLIError(AzCLIErrorType.ArgumentParseError, 
error_msg, command=self.prog)
+                az_error = InvalidArgumentValueError(error_msg)
 
             command_arguments = self._get_failure_recovery_arguments(action)
             if candidates:
@@ -479,7 +490,7 @@
                 az_error.set_recommendation("Try this: 
'{}'".format(recommended_command))
 
             # remind user to check extensions if we can not find a command to 
recommend
-            if az_error.error_type == AzCLIErrorType.CommandNotFoundError \
+            if isinstance(az_error, CommandNotFoundError) \
                     and not az_error.recommendations and self.prog == 'az' \
                     and use_dynamic_install == 'no':
                 az_error.set_recommendation(EXTENSION_REFERENCE)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure-cli-core-2.12.1/azure/cli/core/profiles/_shared.py 
new/azure-cli-core-2.13.0/azure/cli/core/profiles/_shared.py
--- old/azure-cli-core-2.12.1/azure/cli/core/profiles/_shared.py        
2020-09-28 10:37:30.000000000 +0200
+++ new/azure-cli-core-2.13.0/azure/cli/core/profiles/_shared.py        
2020-10-12 09:58:16.000000000 +0200
@@ -48,6 +48,7 @@
     MGMT_RESOURCE_RESOURCES = ('azure.mgmt.resource.resources', 
'ResourceManagementClient')
     MGMT_RESOURCE_SUBSCRIPTIONS = ('azure.mgmt.resource.subscriptions', 
'SubscriptionClient')
     MGMT_RESOURCE_DEPLOYMENTSCRIPTS = 
('azure.mgmt.resource.deploymentscripts', 'DeploymentScriptsClient')
+    MGMT_RESOURCE_TEMPLATESPECS = ('azure.mgmt.resource.templatespecs', 
'TemplateSpecsClient')
     MGMT_MONITOR = ('azure.mgmt.monitor', 'MonitorManagementClient')
     DATA_KEYVAULT = ('azure.keyvault', 'KeyVaultClient')
     DATA_PRIVATE_KEYVAULT = 
('azure.cli.command_modules.keyvault.vendored_sdks.azure_keyvault_t1', 
'KeyVaultClient')
@@ -153,6 +154,7 @@
         ResourceType.MGMT_RESOURCE_RESOURCES: '2020-06-01',
         ResourceType.MGMT_RESOURCE_SUBSCRIPTIONS: '2019-11-01',
         ResourceType.MGMT_RESOURCE_DEPLOYMENTSCRIPTS: '2019-10-01-preview',
+        ResourceType.MGMT_RESOURCE_TEMPLATESPECS: '2019-06-01-preview',
         ResourceType.MGMT_NETWORK_DNS: '2018-05-01',
         ResourceType.MGMT_KEYVAULT: '2020-04-01-preview',
         ResourceType.MGMT_AUTHORIZATION: SDKProfile('2020-04-01-preview', {
@@ -221,6 +223,7 @@
         ResourceType.MGMT_RESOURCE_POLICY: '2016-12-01',
         ResourceType.MGMT_RESOURCE_RESOURCES: '2018-05-01',
         ResourceType.MGMT_RESOURCE_SUBSCRIPTIONS: '2016-06-01',
+        ResourceType.MGMT_RESOURCE_TEMPLATESPECS: '2015-01-01',
         ResourceType.MGMT_NETWORK_DNS: '2016-04-01',
         ResourceType.MGMT_KEYVAULT: '2016-10-01',
         ResourceType.MGMT_AUTHORIZATION: SDKProfile('2015-07-01', {
@@ -251,6 +254,7 @@
         ResourceType.MGMT_RESOURCE_POLICY: '2016-12-01',
         ResourceType.MGMT_RESOURCE_RESOURCES: '2018-02-01',
         ResourceType.MGMT_RESOURCE_SUBSCRIPTIONS: '2016-06-01',
+        ResourceType.MGMT_RESOURCE_TEMPLATESPECS: '2015-01-01',
         ResourceType.MGMT_NETWORK_DNS: '2016-04-01',
         ResourceType.MGMT_KEYVAULT: '2016-10-01',
         ResourceType.MGMT_AUTHORIZATION: SDKProfile('2015-07-01', {
@@ -273,6 +277,7 @@
         ResourceType.MGMT_RESOURCE_POLICY: '2015-10-01-preview',
         ResourceType.MGMT_RESOURCE_RESOURCES: '2016-02-01',
         ResourceType.MGMT_RESOURCE_SUBSCRIPTIONS: '2016-06-01',
+        ResourceType.MGMT_RESOURCE_TEMPLATESPECS: '2015-01-01',
         ResourceType.MGMT_NETWORK_DNS: '2016-04-01',
         ResourceType.MGMT_KEYVAULT: '2016-10-01',
         ResourceType.MGMT_AUTHORIZATION: SDKProfile('2015-07-01', {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure-cli-core-2.12.1/azure/cli/core/telemetry.py 
new/azure-cli-core-2.13.0/azure/cli/core/telemetry.py
--- old/azure-cli-core-2.12.1/azure/cli/core/telemetry.py       2020-09-28 
10:37:30.000000000 +0200
+++ new/azure-cli-core-2.13.0/azure/cli/core/telemetry.py       2020-10-12 
09:58:16.000000000 +0200
@@ -45,7 +45,7 @@
         self.extension_management_detail = None
         self.raw_command = None
         self.mode = 'default'
-        # The AzCLIErrorType
+        # The AzCLIError sub-class name
         self.error_type = 'None'
         # The class name of the raw exception
         self.exception_name = 'None'
@@ -453,7 +453,11 @@
     # of these sub-processes will be very close, usually in several 
milliseconds. We use 1 second as the threshold here.
     # When the difference of create time between current process and its 
parent process is larger than the threshold,
     # the parent process will be viewed as the terminal process.
-    import psutil
+    try:
+        # psutil is not available on cygwin
+        import psutil
+    except ImportError:
+        return ""
     time_threshold = 1
     process = psutil.Process()
     while process and process.ppid() and process.pid != process.ppid():
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure-cli-core-2.12.1/azure/cli/core/util.py 
new/azure-cli-core-2.13.0/azure/cli/core/util.py
--- old/azure-cli-core-2.12.1/azure/cli/core/util.py    2020-09-28 
10:37:30.000000000 +0200
+++ new/azure-cli-core-2.13.0/azure/cli/core/util.py    2020-10-12 
09:58:16.000000000 +0200
@@ -17,8 +17,6 @@
 
 import six
 from six.moves.urllib.request import urlopen  # pylint: disable=import-error
-from azure.cli.core.azclierror import AzCLIErrorType
-from azure.cli.core.azclierror import AzCLIError
 from knack.log import get_logger
 from knack.util import CLIError, to_snake_case
 
@@ -55,7 +53,7 @@
 ]
 
 
-def handle_exception(ex):  # pylint: disable=too-many-return-statements, 
too-many-statements
+def handle_exception(ex):  # pylint: disable=too-many-locals, 
too-many-statements, too-many-branches
     # For error code, follow guidelines at 
https://docs.python.org/2/library/sys.html#sys.exit,
     from jmespath.exceptions import JMESPathTypeError
     from msrestazure.azure_exceptions import CloudError
@@ -64,6 +62,7 @@
     from azure.common import AzureException
     from azure.core.exceptions import AzureError
     from requests.exceptions import SSLError
+    import azure.cli.core.azclierror as azclierror
     import traceback
 
     logger.debug("azure.cli.core.util.handle_exception is called with an 
exception:")
@@ -74,78 +73,156 @@
         error_msg = getattr(ex, 'message', str(ex))
         exit_code = 1
 
-        if isinstance(ex, AzCLIError):
+        if isinstance(ex, azclierror.AzCLIError):
             az_error = ex
 
         elif isinstance(ex, JMESPathTypeError):
             error_msg = "Invalid jmespath query supplied for `--query`: 
{}".format(error_msg)
-            az_error = AzCLIError(AzCLIErrorType.ArgumentParseError, error_msg)
+            az_error = azclierror.InvalidArgumentValueError(error_msg)
             az_error.set_recommendation(QUERY_REFERENCE)
 
+        elif isinstance(ex, SSLError):
+            az_error = azclierror.AzureConnectionError(error_msg)
+            az_error.set_recommendation(SSLERROR_TEMPLATE)
+
+        elif isinstance(ex, CloudError):
+            if extract_common_error_message(ex):
+                error_msg = extract_common_error_message(ex)
+            status_code = str(getattr(ex, 'status_code', 'Unknown Code'))
+            AzCLIErrorType = get_error_type_by_status_code(status_code)
+            az_error = AzCLIErrorType(error_msg)
+
         elif isinstance(ex, ValidationError):
-            az_error = AzCLIError(AzCLIErrorType.ValidationError, error_msg)
+            az_error = azclierror.ValidationError(error_msg)
 
-        # TODO: Fine-grained analysis to decide whether they are 
ValidationErrors
-        elif isinstance(ex, (CLIError, CloudError, AzureError)):
-            try:
-                error_msg = ex.args[0]
-                for detail in ex.args[0].error.details:
-                    error_msg += ('\n' + detail)
-            except Exception:  # pylint: disable=broad-except
-                pass
-            az_error = AzCLIError(AzCLIErrorType.ValidationError, error_msg)
-            exit_code = ex.args[1] if len(ex.args) >= 2 else 1
+        elif isinstance(ex, CLIError):
+            # TODO: Fine-grained analysis here for Unknown error
+            az_error = azclierror.UnknownError(error_msg)
+
+        elif isinstance(ex, AzureError):
+            if extract_common_error_message(ex):
+                error_msg = extract_common_error_message(ex)
+            AzCLIErrorType = get_error_type_by_azure_error(ex)
+            az_error = AzCLIErrorType(error_msg)
 
-        # TODO: Fine-grained analysis
         elif isinstance(ex, AzureException):
-            az_error = AzCLIError(AzCLIErrorType.ServiceError, error_msg)
-            exit_code = ex.args[1] if len(ex.args) >= 2 else 1
-
-        # TODO: Fine-grained analysis
-        elif isinstance(ex, (ClientRequestError, SSLError)):
-            az_error = AzCLIError(AzCLIErrorType.ClientError, error_msg)
-            if 'SSLError' in error_msg:
-                az_error.set_recommendation(SSLERROR_TEMPLATE)
+            if is_azure_connection_error(error_msg):
+                az_error = azclierror.AzureConnectionError(error_msg)
+            else:
+                # TODO: Fine-grained analysis here for Unknown error
+                az_error = azclierror.UnknownError(error_msg)
+
+        elif isinstance(ex, ClientRequestError):
+            if is_azure_connection_error(error_msg):
+                az_error = azclierror.AzureConnectionError(error_msg)
+            else:
+                az_error = azclierror.ClientRequestError(error_msg)
 
-        # TODO: Fine-grained analysis
         elif isinstance(ex, HttpOperationError):
-            try:
-                response = json.loads(ex.response.text)
-                if isinstance(response, str):
-                    error = response
-                else:
-                    error = response['error']
-
-                # ARM should use ODATA v4. So should try this first.
-                # 
http://docs.oasis-open.org/odata/odata-json-format/v4.0/os/odata-json-format-v4.0-os.html#_Toc372793091
-                if isinstance(error, dict):
-                    code = "{} - ".format(error.get('code', 'Unknown Code'))
-                    message = error.get('message', ex)
-                    error_msg = "code: {}, {}".format(code, message)
-                else:
-                    error_msg = error
-
-            except (ValueError, KeyError):
-                pass
-
-            az_error = AzCLIError(AzCLIErrorType.ServiceError, error_msg)
+            message, status_code = extract_http_operation_error(ex)
+            if message:
+                error_msg = message
+            AzCLIErrorType = get_error_type_by_status_code(status_code)
+            az_error = AzCLIErrorType(error_msg)
 
         elif isinstance(ex, KeyboardInterrupt):
             error_msg = 'Keyboard interrupt is captured.'
-            az_error = AzCLIError(AzCLIErrorType.ManualInterrupt, error_msg)
+            az_error = azclierror.ManualInterrupt(error_msg)
 
         else:
             error_msg = "The command failed with an unexpected error. Here is 
the traceback:"
-            az_error = AzCLIError(AzCLIErrorType.UnexpectedError, error_msg)
-            az_error.set_raw_exception(ex)
+            az_error = azclierror.CLIInternalError(error_msg)
+            az_error.set_exception_trace(ex)
             az_error.set_recommendation("To open an issue, please run: 'az 
feedback'")
 
+        if isinstance(az_error, azclierror.ResourceNotFoundError):
+            exit_code = 3
+
         az_error.print_error()
         az_error.send_telemetry()
 
         return exit_code
 
 
+def extract_common_error_message(ex):
+    error_msg = None
+    try:
+        error_msg = ex.args[0]
+        for detail in ex.args[0].error.details:
+            error_msg += ('\n' + detail)
+    except Exception:  # pylint: disable=broad-except
+        pass
+    return error_msg
+
+
+def extract_http_operation_error(ex):
+    error_msg = None
+    status_code = 'Unknown Code'
+    try:
+        response = json.loads(ex.response.text)
+        if isinstance(response, str):
+            error = response
+        else:
+            error = response['error']
+        # ARM should use ODATA v4. So should try this first.
+        # 
http://docs.oasis-open.org/odata/odata-json-format/v4.0/os/odata-json-format-v4.0-os.html#_Toc372793091
+        if isinstance(error, dict):
+            status_code = error.get('code', 'Unknown Code')
+            code_str = "{} - ".format(status_code)
+            message = error.get('message', ex)
+            error_msg = "code: {}, {}".format(code_str, message)
+        else:
+            error_msg = error
+    except (ValueError, KeyError):
+        pass
+    return error_msg, status_code
+
+
+def get_error_type_by_azure_error(ex):
+    import azure.core.exceptions as exceptions
+    import azure.cli.core.azclierror as azclierror
+
+    if isinstance(ex, exceptions.HttpResponseError):
+        status_code = str(ex.status_code)
+        return get_error_type_by_status_code(status_code)
+    if isinstance(ex, exceptions.ResourceNotFoundError):
+        return azclierror.ResourceNotFoundError
+    if isinstance(ex, exceptions.ServiceRequestError):
+        return azclierror.ClientRequestError
+    if isinstance(ex, exceptions.ServiceRequestTimeoutError):
+        return azclierror.AzureConnectionError
+    if isinstance(ex, (exceptions.ServiceResponseError, 
exceptions.ServiceResponseTimeoutError)):
+        return azclierror.AzureResponseError
+
+    return azclierror.UnknownError
+
+
+def get_error_type_by_status_code(status_code):
+    import azure.cli.core.azclierror as azclierror
+
+    if status_code == '400':
+        return azclierror.BadRequestError
+    if status_code == '401':
+        return azclierror.UnauthorizedError
+    if status_code == '403':
+        return azclierror.ForbiddenError
+    if status_code == '404':
+        return azclierror.ResourceNotFoundError
+    if status_code.startswith('5'):
+        return azclierror.AzureInternalError
+
+    return azclierror.AzureResponseError
+
+
+def is_azure_connection_error(error_msg):
+    error_msg = error_msg.lower()
+    if 'connection error' in error_msg \
+            or 'connection broken' in error_msg \
+            or 'connection aborted' in error_msg:
+        return True
+    return False
+
+
 # pylint: disable=inconsistent-return-statements
 def empty_on_404(ex):
     from msrestazure.azure_exceptions import CloudError
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure-cli-core-2.12.1/azure_cli_core.egg-info/PKG-INFO 
new/azure-cli-core-2.13.0/azure_cli_core.egg-info/PKG-INFO
--- old/azure-cli-core-2.12.1/azure_cli_core.egg-info/PKG-INFO  2020-09-28 
10:37:45.000000000 +0200
+++ new/azure-cli-core-2.13.0/azure_cli_core.egg-info/PKG-INFO  2020-10-12 
09:58:34.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: azure-cli-core
-Version: 2.12.1
+Version: 2.13.0
 Summary: Microsoft Azure Command-Line Tools Core Module
 Home-page: https://github.com/Azure/azure-cli
 Author: Microsoft Corporation
@@ -15,6 +15,10 @@
         Release History
         ===============
         
+        2.13.0
+        ++++++
+        * Minor fixes
+        
         2.12.1
         ++++++
         * No changes
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure-cli-core-2.12.1/setup.py 
new/azure-cli-core-2.13.0/setup.py
--- old/azure-cli-core-2.12.1/setup.py  2020-09-28 10:37:30.000000000 +0200
+++ new/azure-cli-core-2.13.0/setup.py  2020-10-12 09:58:16.000000000 +0200
@@ -9,7 +9,7 @@
 from codecs import open
 from setuptools import setup
 
-VERSION = "2.12.1"
+VERSION = "2.13.0"
 
 # If we have source, validate that our version numbers match
 # This should prevent uploading releases with mismatched versions.


Reply via email to