1# --------------------------------------------------------------------------------------------
2# Copyright (c) Microsoft Corporation. All rights reserved.
3# Licensed under the MIT License. See License.txt in the project root for license information.
4# --------------------------------------------------------------------------------------------
5
6import sys
7
8import azure.cli.core.telemetry as telemetry
9from knack.util import CLIError
10from knack.log import get_logger
11
12logger = get_logger(__name__)
13# pylint: disable=unnecessary-pass
14
15
16# Error types in AzureCLI are from different sources, and there are many general error types like CLIError, AzureError.
17# Besides, many error types with different names are actually showing the same kind of error.
18# For example, CloudError, CLIError and ValidationError all could be a resource-not-found error.
19# Therefore, here we define the new error classes to map and categorize all of the error types from different sources.
20
21
22# region: Base Layer
23# Base class for all the AzureCLI defined error classes.
24class AzCLIError(CLIError):
25    """ Base class for all the AzureCLI defined error classes.
26    DO NOT raise this error class in your codes. """
27
28    def __init__(self, error_msg, recommendation=None):
29        # error message
30        self.error_msg = error_msg
31
32        # manual recommendations provided based on developers' knowledge
33        self.recommendations = []
34        self.set_recommendation(recommendation)
35
36        # AI recommendations provided by Aladdin service, with tuple form: (recommendation, description)
37        self.aladdin_recommendations = []
38
39        # exception trace for the error
40        self.exception_trace = None
41        super().__init__(error_msg)
42
43    def set_recommendation(self, recommendation):
44        """" Set manual recommendations for the error.
45        Command module or extension authors could call this method to provide recommendations,
46        the recommendations will be printed after the error message, one recommendation per line
47        """
48        if isinstance(recommendation, str):
49            self.recommendations.append(recommendation)
50        elif isinstance(recommendation, list):
51            self.recommendations.extend(recommendation)
52
53    def set_aladdin_recommendation(self, recommendations):
54        """ Set aladdin recommendations for the error.
55        One item should be a tuple with the form: (recommendation, description)
56        """
57        self.aladdin_recommendations.extend(recommendations)
58
59    def set_exception_trace(self, exception_trace):
60        self.exception_trace = exception_trace
61
62    def print_error(self):
63        from azure.cli.core.azlogging import CommandLoggerContext
64        from azure.cli.core.style import print_styled_text
65        with CommandLoggerContext(logger):
66            # print error message
67            logger.error(self.error_msg)
68
69            # print exception trace if there is
70            if self.exception_trace:
71                logger.exception(self.exception_trace)
72
73        # print recommendations to action
74        if self.recommendations:
75            for recommendation in self.recommendations:
76                print(recommendation, file=sys.stderr)
77
78        if self.aladdin_recommendations:
79            print('\nTRY THIS:', file=sys.stderr)
80            for recommendation, description in self.aladdin_recommendations:
81                print_styled_text(recommendation, file=sys.stderr)
82                print_styled_text(description, file=sys.stderr)
83
84    def send_telemetry(self):
85        telemetry.set_error_type(self.__class__.__name__)
86# endregion
87
88
89# region: Second Layer
90# Main categories of the AzureCLI error types, used for Telemetry analysis
91class UserFault(AzCLIError):
92    """ Users should be responsible for the errors.
93    DO NOT raise this error class in your codes. """
94    def send_telemetry(self):
95        super().send_telemetry()
96        telemetry.set_user_fault(self.error_msg)
97
98
99class ServiceError(AzCLIError):
100    """ Azure Services should be responsible for the errors.
101    DO NOT raise this error class in your codes. """
102    def send_telemetry(self):
103        super().send_telemetry()
104        telemetry.set_failure(self.error_msg)
105
106
107class ClientError(AzCLIError):
108    """ AzureCLI should be responsible for the errors.
109    DO NOT raise this error class in your codes. """
110    def send_telemetry(self):
111        super().send_telemetry()
112        telemetry.set_failure(self.error_msg)
113        if self.exception_trace:
114            telemetry.set_exception(self.exception_trace, '')
115
116
117class UnknownError(AzCLIError):
118    """ Unclear errors, could not know who should be responsible for the errors.
119    DO NOT raise this error class in your codes. """
120    def send_telemetry(self):
121        super().send_telemetry()
122        telemetry.set_failure(self.error_msg)
123
124# endregion
125
126
127# region: Third Layer
128# Specific categories of the AzureCLI error types
129# Raise the error classes here in your codes. Avoid using fallback error classes unless you can not find a proper one.
130# Command related error types
131class CommandNotFoundError(UserFault):
132    """ Command is misspelled or not recognized by AzureCLI. """
133    pass
134
135
136# Argument related error types
137class UnrecognizedArgumentError(UserFault):
138    """ Argument is misspelled or not recognized by AzureCLI. """
139    pass
140
141
142class RequiredArgumentMissingError(UserFault):
143    """ Required argument is not specified. """
144    pass
145
146
147class MutuallyExclusiveArgumentError(UserFault):
148    """ Arguments can not be specified together. """
149    pass
150
151
152class InvalidArgumentValueError(UserFault):
153    """ Argument value is not valid. """
154    pass
155
156
157class ArgumentUsageError(UserFault):
158    """ Fallback of the argument usage related errors.
159    Avoid using this class unless the error can not be classified
160    into the Argument related specific error types. """
161    pass
162
163
164# Response related error types
165class BadRequestError(UserFault):
166    """ Bad request from client: 400 error """
167    pass
168
169
170class UnauthorizedError(UserFault):
171    """ Unauthorized request: 401 error """
172    pass
173
174
175class ForbiddenError(UserFault):
176    """ Service refuse to response: 403 error """
177    pass
178
179
180class ResourceNotFoundError(UserFault):
181    """ Can not find Azure resources: 404 error """
182    pass
183
184
185class AzureInternalError(ServiceError):
186    """ Azure service internal error: 5xx error """
187    pass
188
189
190class AzureResponseError(UserFault):
191    """ Fallback of the response related errors.
192    Avoid using this class unless the error can not be classified
193    into the Response related specific error types. """
194    pass
195
196
197# Request related error types
198class AzureConnectionError(UserFault):
199    """ Connection issues like connection timeout, aborted or broken. """
200    pass
201
202
203class ClientRequestError(UserFault):
204    """ Fallback of the request related errors. Error occurs while attempting
205    to make a request to the service. No request is sent.
206    Avoid using this class unless the error can not be classified
207    into the Request related specific errors types. """
208    pass
209
210
211# File operation related error types
212class FileOperationError(UserFault):
213    """ For file or directory operation related errors. """
214    pass
215
216
217# Keyboard interrupt error type
218class ManualInterrupt(UserFault):
219    """ Keyboard interrupt. """
220    pass
221
222
223class NoTTYError(UserFault):
224    """ No tty available for prompt. """
225    pass
226
227
228# ARM template related error types
229class InvalidTemplateError(UserFault):
230    """ ARM template validation fails. It could be caused by incorrect template files or parameters """
231    pass
232
233
234class DeploymentError(UserFault):
235    """ ARM template deployment fails. Template file is valid, and error occurs in deployment. """
236    pass
237
238
239# Validation related error types
240class ValidationError(UserFault):
241    """ Fallback of the errors in validation functions.
242    Avoid using this class unless the error can not be classified into
243    the Argument, Request and Response related specific error types. """
244    pass
245
246
247class UnclassifiedUserFault(UserFault):
248    """ Fallback of the UserFault related error types.
249    Avoid using this class unless the error can not be classified into
250    the UserFault related specific error types.
251    """
252    pass
253
254
255# CLI internal error type
256class CLIInternalError(ClientError):
257    """ AzureCLI internal error """
258    pass
259
260
261# Client error for az next
262class RecommendationError(ClientError):
263    """ The client error raised by `az next`. It is needed in `az next` to skip error records. """
264    pass
265
266
267class AuthenticationError(ServiceError):
268    """ Raised when AAD authentication fails. """
269
270# endregion
271