1# Copyright (c) 2014 Amazon.com, Inc. or its affiliates.  All Rights Reserved
2#
3# Permission is hereby granted, free of charge, to any person obtaining a
4# copy of this software and associated documentation files (the
5# "Software"), to deal in the Software without restriction, including
6# without limitation the rights to use, copy, modify, merge, publish, dis-
7# tribute, sublicense, and/or sell copies of the Software, and to permit
8# persons to whom the Software is furnished to do so, subject to the fol-
9# lowing conditions:
10#
11# The above copyright notice and this permission notice shall be included
12# in all copies or substantial portions of the Software.
13#
14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
16# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
17# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20# IN THE SOFTWARE.
21#
22
23import boto
24from boto.compat import json
25from boto.connection import AWSQueryConnection
26from boto.regioninfo import RegionInfo
27from boto.exception import JSONResponseError
28from boto.support import exceptions
29
30
31class SupportConnection(AWSQueryConnection):
32    """
33    AWS Support
34    The AWS Support API reference is intended for programmers who need
35    detailed information about the AWS Support operations and data
36    types. This service enables you to manage your AWS Support cases
37    programmatically. It uses HTTP methods that return results in JSON
38    format.
39
40    The AWS Support service also exposes a set of `Trusted Advisor`_
41    features. You can retrieve a list of checks and their
42    descriptions, get check results, specify checks to refresh, and
43    get the refresh status of checks.
44
45    The following list describes the AWS Support case management
46    operations:
47
48
49    + **Service names, issue categories, and available severity
50      levels. **The DescribeServices and DescribeSeverityLevels
51      operations return AWS service names, service codes, service
52      categories, and problem severity levels. You use these values when
53      you call the CreateCase operation.
54    + **Case creation, case details, and case resolution.** The
55      CreateCase, DescribeCases, DescribeAttachment, and ResolveCase
56      operations create AWS Support cases, retrieve information about
57      cases, and resolve cases.
58    + **Case communication.** The DescribeCommunications,
59      AddCommunicationToCase, and AddAttachmentsToSet operations
60      retrieve and add communications and attachments to AWS Support
61      cases.
62
63
64    The following list describes the operations available from the AWS
65    Support service for Trusted Advisor:
66
67
68    + DescribeTrustedAdvisorChecks returns the list of checks that run
69      against your AWS resources.
70    + Using the `CheckId` for a specific check returned by
71      DescribeTrustedAdvisorChecks, you can call
72      DescribeTrustedAdvisorCheckResult to obtain the results for the
73      check you specified.
74    + DescribeTrustedAdvisorCheckSummaries returns summarized results
75      for one or more Trusted Advisor checks.
76    + RefreshTrustedAdvisorCheck requests that Trusted Advisor rerun a
77      specified check.
78    + DescribeTrustedAdvisorCheckRefreshStatuses reports the refresh
79      status of one or more checks.
80
81
82    For authentication of requests, AWS Support uses `Signature
83    Version 4 Signing Process`_.
84
85    See `About the AWS Support API`_ in the AWS Support User Guide for
86    information about how to use this service to create and manage
87    your support cases, and how to call Trusted Advisor for results of
88    checks on your resources.
89    """
90    APIVersion = "2013-04-15"
91    DefaultRegionName = "us-east-1"
92    DefaultRegionEndpoint = "support.us-east-1.amazonaws.com"
93    ServiceName = "Support"
94    TargetPrefix = "AWSSupport_20130415"
95    ResponseError = JSONResponseError
96
97    _faults = {
98        "CaseCreationLimitExceeded": exceptions.CaseCreationLimitExceeded,
99        "AttachmentLimitExceeded": exceptions.AttachmentLimitExceeded,
100        "CaseIdNotFound": exceptions.CaseIdNotFound,
101        "DescribeAttachmentLimitExceeded": exceptions.DescribeAttachmentLimitExceeded,
102        "AttachmentSetIdNotFound": exceptions.AttachmentSetIdNotFound,
103        "InternalServerError": exceptions.InternalServerError,
104        "AttachmentSetExpired": exceptions.AttachmentSetExpired,
105        "AttachmentIdNotFound": exceptions.AttachmentIdNotFound,
106        "AttachmentSetSizeLimitExceeded": exceptions.AttachmentSetSizeLimitExceeded,
107    }
108
109
110    def __init__(self, **kwargs):
111        region = kwargs.pop('region', None)
112        if not region:
113            region = RegionInfo(self, self.DefaultRegionName,
114                                self.DefaultRegionEndpoint)
115
116        if 'host' not in kwargs or kwargs['host'] is None:
117            kwargs['host'] = region.endpoint
118
119        super(SupportConnection, self).__init__(**kwargs)
120        self.region = region
121
122    def _required_auth_capability(self):
123        return ['hmac-v4']
124
125    def add_attachments_to_set(self, attachments, attachment_set_id=None):
126        """
127        Adds one or more attachments to an attachment set. If an
128        `AttachmentSetId` is not specified, a new attachment set is
129        created, and the ID of the set is returned in the response. If
130        an `AttachmentSetId` is specified, the attachments are added
131        to the specified set, if it exists.
132
133        An attachment set is a temporary container for attachments
134        that are to be added to a case or case communication. The set
135        is available for one hour after it is created; the
136        `ExpiryTime` returned in the response indicates when the set
137        expires. The maximum number of attachments in a set is 3, and
138        the maximum size of any attachment in the set is 5 MB.
139
140        :type attachment_set_id: string
141        :param attachment_set_id: The ID of the attachment set. If an
142            `AttachmentSetId` is not specified, a new attachment set is
143            created, and the ID of the set is returned in the response. If an
144            `AttachmentSetId` is specified, the attachments are added to the
145            specified set, if it exists.
146
147        :type attachments: list
148        :param attachments: One or more attachments to add to the set. The
149            limit is 3 attachments per set, and the size limit is 5 MB per
150            attachment.
151
152        """
153        params = {'attachments': attachments, }
154        if attachment_set_id is not None:
155            params['attachmentSetId'] = attachment_set_id
156        return self.make_request(action='AddAttachmentsToSet',
157                                 body=json.dumps(params))
158
159    def add_communication_to_case(self, communication_body, case_id=None,
160                                  cc_email_addresses=None,
161                                  attachment_set_id=None):
162        """
163        Adds additional customer communication to an AWS Support case.
164        You use the `CaseId` value to identify the case to add
165        communication to. You can list a set of email addresses to
166        copy on the communication using the `CcEmailAddresses` value.
167        The `CommunicationBody` value contains the text of the
168        communication.
169
170        The response indicates the success or failure of the request.
171
172        This operation implements a subset of the behavior on the AWS
173        Support `Your Support Cases`_ web form.
174
175        :type case_id: string
176        :param case_id: The AWS Support case ID requested or returned in the
177            call. The case ID is an alphanumeric string formatted as shown in
178            this example: case- 12345678910-2013-c4c1d2bf33c5cf47
179
180        :type communication_body: string
181        :param communication_body: The body of an email communication to add to
182            the support case.
183
184        :type cc_email_addresses: list
185        :param cc_email_addresses: The email addresses in the CC line of an
186            email to be added to the support case.
187
188        :type attachment_set_id: string
189        :param attachment_set_id: The ID of a set of one or more attachments
190            for the communication to add to the case. Create the set by calling
191            AddAttachmentsToSet
192
193        """
194        params = {'communicationBody': communication_body, }
195        if case_id is not None:
196            params['caseId'] = case_id
197        if cc_email_addresses is not None:
198            params['ccEmailAddresses'] = cc_email_addresses
199        if attachment_set_id is not None:
200            params['attachmentSetId'] = attachment_set_id
201        return self.make_request(action='AddCommunicationToCase',
202                                 body=json.dumps(params))
203
204    def create_case(self, subject, communication_body, service_code=None,
205                    severity_code=None, category_code=None,
206                    cc_email_addresses=None, language=None, issue_type=None,
207                    attachment_set_id=None):
208        """
209        Creates a new case in the AWS Support Center. This operation
210        is modeled on the behavior of the AWS Support Center `Open a
211        new case`_ page. Its parameters require you to specify the
212        following information:
213
214
215        #. **IssueType.** The type of issue for the case. You can
216           specify either "customer-service" or "technical." If you do
217           not indicate a value, the default is "technical."
218        #. **ServiceCode.** The code for an AWS service. You obtain
219           the `ServiceCode` by calling DescribeServices.
220        #. **CategoryCode.** The category for the service defined for
221           the `ServiceCode` value. You also obtain the category code for
222           a service by calling DescribeServices. Each AWS service
223           defines its own set of category codes.
224        #. **SeverityCode.** A value that indicates the urgency of the
225           case, which in turn determines the response time according to
226           your service level agreement with AWS Support. You obtain the
227           SeverityCode by calling DescribeSeverityLevels.
228        #. **Subject.** The **Subject** field on the AWS Support
229           Center `Open a new case`_ page.
230        #. **CommunicationBody.** The **Description** field on the AWS
231           Support Center `Open a new case`_ page.
232        #. **AttachmentSetId.** The ID of a set of attachments that
233           has been created by using AddAttachmentsToSet.
234        #. **Language.** The human language in which AWS Support
235           handles the case. English and Japanese are currently
236           supported.
237        #. **CcEmailAddresses.** The AWS Support Center **CC** field
238           on the `Open a new case`_ page. You can list email addresses
239           to be copied on any correspondence about the case. The account
240           that opens the case is already identified by passing the AWS
241           Credentials in the HTTP POST method or in a method or function
242           call from one of the programming languages supported by an
243           `AWS SDK`_.
244
245
246        A successful CreateCase request returns an AWS Support case
247        number. Case numbers are used by the DescribeCases operation
248        to retrieve existing AWS Support cases.
249
250        :type subject: string
251        :param subject: The title of the AWS Support case.
252
253        :type service_code: string
254        :param service_code: The code for the AWS service returned by the call
255            to DescribeServices.
256
257        :type severity_code: string
258        :param severity_code: The code for the severity level returned by the
259            call to DescribeSeverityLevels.
260
261        :type category_code: string
262        :param category_code: The category of problem for the AWS Support case.
263
264        :type communication_body: string
265        :param communication_body: The communication body text when you create
266            an AWS Support case by calling CreateCase.
267
268        :type cc_email_addresses: list
269        :param cc_email_addresses: A list of email addresses that AWS Support
270            copies on case correspondence.
271
272        :type language: string
273        :param language: The ISO 639-1 code for the language in which AWS
274            provides support. AWS Support currently supports English ("en") and
275            Japanese ("ja"). Language parameters must be passed explicitly for
276            operations that take them.
277
278        :type issue_type: string
279        :param issue_type: The type of issue for the case. You can specify
280            either "customer-service" or "technical." If you do not indicate a
281            value, the default is "technical."
282
283        :type attachment_set_id: string
284        :param attachment_set_id: The ID of a set of one or more attachments
285            for the case. Create the set by using AddAttachmentsToSet.
286
287        """
288        params = {
289            'subject': subject,
290            'communicationBody': communication_body,
291        }
292        if service_code is not None:
293            params['serviceCode'] = service_code
294        if severity_code is not None:
295            params['severityCode'] = severity_code
296        if category_code is not None:
297            params['categoryCode'] = category_code
298        if cc_email_addresses is not None:
299            params['ccEmailAddresses'] = cc_email_addresses
300        if language is not None:
301            params['language'] = language
302        if issue_type is not None:
303            params['issueType'] = issue_type
304        if attachment_set_id is not None:
305            params['attachmentSetId'] = attachment_set_id
306        return self.make_request(action='CreateCase',
307                                 body=json.dumps(params))
308
309    def describe_attachment(self, attachment_id):
310        """
311        Returns the attachment that has the specified ID. Attachment
312        IDs are generated by the case management system when you add
313        an attachment to a case or case communication. Attachment IDs
314        are returned in the AttachmentDetails objects that are
315        returned by the DescribeCommunications operation.
316
317        :type attachment_id: string
318        :param attachment_id: The ID of the attachment to return. Attachment
319            IDs are returned by the DescribeCommunications operation.
320
321        """
322        params = {'attachmentId': attachment_id, }
323        return self.make_request(action='DescribeAttachment',
324                                 body=json.dumps(params))
325
326    def describe_cases(self, case_id_list=None, display_id=None,
327                       after_time=None, before_time=None,
328                       include_resolved_cases=None, next_token=None,
329                       max_results=None, language=None,
330                       include_communications=None):
331        """
332        Returns a list of cases that you specify by passing one or
333        more case IDs. In addition, you can filter the cases by date
334        by setting values for the `AfterTime` and `BeforeTime` request
335        parameters.
336
337        Case data is available for 12 months after creation. If a case
338        was created more than 12 months ago, a request for data might
339        cause an error.
340
341        The response returns the following in JSON format:
342
343
344        #. One or more CaseDetails data types.
345        #. One or more `NextToken` values, which specify where to
346           paginate the returned records represented by the `CaseDetails`
347           objects.
348
349        :type case_id_list: list
350        :param case_id_list: A list of ID numbers of the support cases you want
351            returned. The maximum number of cases is 100.
352
353        :type display_id: string
354        :param display_id: The ID displayed for a case in the AWS Support
355            Center user interface.
356
357        :type after_time: string
358        :param after_time: The start date for a filtered date search on support
359            case communications. Case communications are available for 12
360            months after creation.
361
362        :type before_time: string
363        :param before_time: The end date for a filtered date search on support
364            case communications. Case communications are available for 12
365            months after creation.
366
367        :type include_resolved_cases: boolean
368        :param include_resolved_cases: Specifies whether resolved support cases
369            should be included in the DescribeCases results. The default is
370            false .
371
372        :type next_token: string
373        :param next_token: A resumption point for pagination.
374
375        :type max_results: integer
376        :param max_results: The maximum number of results to return before
377            paginating.
378
379        :type language: string
380        :param language: The ISO 639-1 code for the language in which AWS
381            provides support. AWS Support currently supports English ("en") and
382            Japanese ("ja"). Language parameters must be passed explicitly for
383            operations that take them.
384
385        :type include_communications: boolean
386        :param include_communications: Specifies whether communications should
387            be included in the DescribeCases results. The default is true .
388
389        """
390        params = {}
391        if case_id_list is not None:
392            params['caseIdList'] = case_id_list
393        if display_id is not None:
394            params['displayId'] = display_id
395        if after_time is not None:
396            params['afterTime'] = after_time
397        if before_time is not None:
398            params['beforeTime'] = before_time
399        if include_resolved_cases is not None:
400            params['includeResolvedCases'] = include_resolved_cases
401        if next_token is not None:
402            params['nextToken'] = next_token
403        if max_results is not None:
404            params['maxResults'] = max_results
405        if language is not None:
406            params['language'] = language
407        if include_communications is not None:
408            params['includeCommunications'] = include_communications
409        return self.make_request(action='DescribeCases',
410                                 body=json.dumps(params))
411
412    def describe_communications(self, case_id, before_time=None,
413                                after_time=None, next_token=None,
414                                max_results=None):
415        """
416        Returns communications (and attachments) for one or more
417        support cases. You can use the `AfterTime` and `BeforeTime`
418        parameters to filter by date. You can use the `CaseId`
419        parameter to restrict the results to a particular case.
420
421        Case data is available for 12 months after creation. If a case
422        was created more than 12 months ago, a request for data might
423        cause an error.
424
425        You can use the `MaxResults` and `NextToken` parameters to
426        control the pagination of the result set. Set `MaxResults` to
427        the number of cases you want displayed on each page, and use
428        `NextToken` to specify the resumption of pagination.
429
430        :type case_id: string
431        :param case_id: The AWS Support case ID requested or returned in the
432            call. The case ID is an alphanumeric string formatted as shown in
433            this example: case- 12345678910-2013-c4c1d2bf33c5cf47
434
435        :type before_time: string
436        :param before_time: The end date for a filtered date search on support
437            case communications. Case communications are available for 12
438            months after creation.
439
440        :type after_time: string
441        :param after_time: The start date for a filtered date search on support
442            case communications. Case communications are available for 12
443            months after creation.
444
445        :type next_token: string
446        :param next_token: A resumption point for pagination.
447
448        :type max_results: integer
449        :param max_results: The maximum number of results to return before
450            paginating.
451
452        """
453        params = {'caseId': case_id, }
454        if before_time is not None:
455            params['beforeTime'] = before_time
456        if after_time is not None:
457            params['afterTime'] = after_time
458        if next_token is not None:
459            params['nextToken'] = next_token
460        if max_results is not None:
461            params['maxResults'] = max_results
462        return self.make_request(action='DescribeCommunications',
463                                 body=json.dumps(params))
464
465    def describe_services(self, service_code_list=None, language=None):
466        """
467        Returns the current list of AWS services and a list of service
468        categories that applies to each one. You then use service
469        names and categories in your CreateCase requests. Each AWS
470        service has its own set of categories.
471
472        The service codes and category codes correspond to the values
473        that are displayed in the **Service** and **Category** drop-
474        down lists on the AWS Support Center `Open a new case`_ page.
475        The values in those fields, however, do not necessarily match
476        the service codes and categories returned by the
477        `DescribeServices` request. Always use the service codes and
478        categories obtained programmatically. This practice ensures
479        that you always have the most recent set of service and
480        category codes.
481
482        :type service_code_list: list
483        :param service_code_list: A JSON-formatted list of service codes
484            available for AWS services.
485
486        :type language: string
487        :param language: The ISO 639-1 code for the language in which AWS
488            provides support. AWS Support currently supports English ("en") and
489            Japanese ("ja"). Language parameters must be passed explicitly for
490            operations that take them.
491
492        """
493        params = {}
494        if service_code_list is not None:
495            params['serviceCodeList'] = service_code_list
496        if language is not None:
497            params['language'] = language
498        return self.make_request(action='DescribeServices',
499                                 body=json.dumps(params))
500
501    def describe_severity_levels(self, language=None):
502        """
503        Returns the list of severity levels that you can assign to an
504        AWS Support case. The severity level for a case is also a
505        field in the CaseDetails data type included in any CreateCase
506        request.
507
508        :type language: string
509        :param language: The ISO 639-1 code for the language in which AWS
510            provides support. AWS Support currently supports English ("en") and
511            Japanese ("ja"). Language parameters must be passed explicitly for
512            operations that take them.
513
514        """
515        params = {}
516        if language is not None:
517            params['language'] = language
518        return self.make_request(action='DescribeSeverityLevels',
519                                 body=json.dumps(params))
520
521    def describe_trusted_advisor_check_refresh_statuses(self, check_ids):
522        """
523        Returns the refresh status of the Trusted Advisor checks that
524        have the specified check IDs. Check IDs can be obtained by
525        calling DescribeTrustedAdvisorChecks.
526
527        :type check_ids: list
528        :param check_ids: The IDs of the Trusted Advisor checks.
529
530        """
531        params = {'checkIds': check_ids, }
532        return self.make_request(action='DescribeTrustedAdvisorCheckRefreshStatuses',
533                                 body=json.dumps(params))
534
535    def describe_trusted_advisor_check_result(self, check_id, language=None):
536        """
537        Returns the results of the Trusted Advisor check that has the
538        specified check ID. Check IDs can be obtained by calling
539        DescribeTrustedAdvisorChecks.
540
541        The response contains a TrustedAdvisorCheckResult object,
542        which contains these three objects:
543
544
545        + TrustedAdvisorCategorySpecificSummary
546        + TrustedAdvisorResourceDetail
547        + TrustedAdvisorResourcesSummary
548
549
550        In addition, the response contains these fields:
551
552
553        + **Status.** The alert status of the check: "ok" (green),
554          "warning" (yellow), "error" (red), or "not_available".
555        + **Timestamp.** The time of the last refresh of the check.
556        + **CheckId.** The unique identifier for the check.
557
558        :type check_id: string
559        :param check_id: The unique identifier for the Trusted Advisor check.
560
561        :type language: string
562        :param language: The ISO 639-1 code for the language in which AWS
563            provides support. AWS Support currently supports English ("en") and
564            Japanese ("ja"). Language parameters must be passed explicitly for
565            operations that take them.
566
567        """
568        params = {'checkId': check_id, }
569        if language is not None:
570            params['language'] = language
571        return self.make_request(action='DescribeTrustedAdvisorCheckResult',
572                                 body=json.dumps(params))
573
574    def describe_trusted_advisor_check_summaries(self, check_ids):
575        """
576        Returns the summaries of the results of the Trusted Advisor
577        checks that have the specified check IDs. Check IDs can be
578        obtained by calling DescribeTrustedAdvisorChecks.
579
580        The response contains an array of TrustedAdvisorCheckSummary
581        objects.
582
583        :type check_ids: list
584        :param check_ids: The IDs of the Trusted Advisor checks.
585
586        """
587        params = {'checkIds': check_ids, }
588        return self.make_request(action='DescribeTrustedAdvisorCheckSummaries',
589                                 body=json.dumps(params))
590
591    def describe_trusted_advisor_checks(self, language):
592        """
593        Returns information about all available Trusted Advisor
594        checks, including name, ID, category, description, and
595        metadata. You must specify a language code; English ("en") and
596        Japanese ("ja") are currently supported. The response contains
597        a TrustedAdvisorCheckDescription for each check.
598
599        :type language: string
600        :param language: The ISO 639-1 code for the language in which AWS
601            provides support. AWS Support currently supports English ("en") and
602            Japanese ("ja"). Language parameters must be passed explicitly for
603            operations that take them.
604
605        """
606        params = {'language': language, }
607        return self.make_request(action='DescribeTrustedAdvisorChecks',
608                                 body=json.dumps(params))
609
610    def refresh_trusted_advisor_check(self, check_id):
611        """
612        Requests a refresh of the Trusted Advisor check that has the
613        specified check ID. Check IDs can be obtained by calling
614        DescribeTrustedAdvisorChecks.
615
616        The response contains a RefreshTrustedAdvisorCheckResult
617        object, which contains these fields:
618
619
620        + **Status.** The refresh status of the check: "none",
621          "enqueued", "processing", "success", or "abandoned".
622        + **MillisUntilNextRefreshable.** The amount of time, in
623          milliseconds, until the check is eligible for refresh.
624        + **CheckId.** The unique identifier for the check.
625
626        :type check_id: string
627        :param check_id: The unique identifier for the Trusted Advisor check.
628
629        """
630        params = {'checkId': check_id, }
631        return self.make_request(action='RefreshTrustedAdvisorCheck',
632                                 body=json.dumps(params))
633
634    def resolve_case(self, case_id=None):
635        """
636        Takes a `CaseId` and returns the initial state of the case
637        along with the state of the case after the call to ResolveCase
638        completed.
639
640        :type case_id: string
641        :param case_id: The AWS Support case ID requested or returned in the
642            call. The case ID is an alphanumeric string formatted as shown in
643            this example: case- 12345678910-2013-c4c1d2bf33c5cf47
644
645        """
646        params = {}
647        if case_id is not None:
648            params['caseId'] = case_id
649        return self.make_request(action='ResolveCase',
650                                 body=json.dumps(params))
651
652    def make_request(self, action, body):
653        headers = {
654            'X-Amz-Target': '%s.%s' % (self.TargetPrefix, action),
655            'Host': self.region.endpoint,
656            'Content-Type': 'application/x-amz-json-1.1',
657            'Content-Length': str(len(body)),
658        }
659        http_request = self.build_base_http_request(
660            method='POST', path='/', auth_path='/', params={},
661            headers=headers, data=body)
662        response = self._mexe(http_request, sender=None,
663                              override_num_retries=10)
664        response_body = response.read().decode('utf-8')
665        boto.log.debug(response_body)
666        if response.status == 200:
667            if response_body:
668                return json.loads(response_body)
669        else:
670            json_body = json.loads(response_body)
671            fault_name = json_body.get('__type', None)
672            exception_class = self._faults.get(fault_name, self.ResponseError)
673            raise exception_class(response.status, response.reason,
674                                  body=json_body)
675