1# -*- coding: utf-8 -*- # 2# Copyright 2016 Google LLC. All Rights Reserved. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15"""Backend service.""" 16 17from __future__ import absolute_import 18from __future__ import division 19from __future__ import unicode_literals 20 21from apitools.base.py import batch 22from apitools.base.py import exceptions as apitools_exceptions 23from googlecloudsdk.api_lib.compute import exceptions 24from googlecloudsdk.api_lib.compute import request_helper 25from googlecloudsdk.api_lib.compute import utils 26from googlecloudsdk.api_lib.util import apis as core_apis 27from googlecloudsdk.api_lib.util import exceptions as api_exceptions 28 29from six.moves.urllib import parse 30 31# Upper bound on batch size 32# https://cloud.google.com/compute/docs/api/how-tos/batch 33_BATCH_SIZE_LIMIT = 1000 34 35 36class Error(exceptions.Error): 37 """Errors raised by this module.""" 38 39 40def _GetBatchUrl(endpoint_url, api_version): 41 """Return a batch URL for the given endpoint URL.""" 42 parsed_endpoint = parse.urlparse(endpoint_url) 43 return parse.urljoin( 44 '{0}://{1}'.format(parsed_endpoint.scheme, parsed_endpoint.netloc), 45 'batch/compute/' + api_version) 46 47 48class ClientAdapter(object): 49 """Encapsulates compute apitools interactions.""" 50 _API_NAME = 'compute' 51 52 def __init__(self, api_version=None, no_http=False, client=None): 53 self._api_version = core_apis.ResolveVersion( 54 self._API_NAME, api_version=api_version) 55 self._client = client or core_apis.GetClientInstance( 56 self._API_NAME, self._api_version, no_http=no_http) 57 58 # Turn the endpoint into just the host. 59 # eg. https://compute.googleapis.com/compute/v1 -> https://compute.googleapis.com 60 endpoint_url = core_apis.GetEffectiveApiEndpoint(self._API_NAME, 61 self._api_version) 62 self._batch_url = _GetBatchUrl(endpoint_url, self._api_version) 63 64 @property 65 def api_version(self): 66 return self._api_version 67 68 @property 69 def apitools_client(self): 70 return self._client 71 72 @property 73 def batch_url(self): 74 return self._batch_url 75 76 @property 77 def messages(self): 78 return self._client.MESSAGES_MODULE 79 80 def MakeRequests(self, 81 requests, 82 errors_to_collect=None, 83 progress_tracker=None, 84 no_followup=False, 85 always_return_operation=False, 86 followup_overrides=None, 87 log_result=True, 88 timeout=None): 89 """Sends given request in batch mode.""" 90 errors = errors_to_collect if errors_to_collect is not None else [] 91 objects = list( 92 request_helper.MakeRequests( 93 requests=requests, 94 http=self._client.http, 95 batch_url=self._batch_url, 96 errors=errors, 97 progress_tracker=progress_tracker, 98 no_followup=no_followup, 99 always_return_operation=always_return_operation, 100 followup_overrides=followup_overrides, 101 log_result=log_result, 102 timeout=timeout)) 103 if errors_to_collect is None and errors: 104 utils.RaiseToolException( 105 errors, error_message='Could not fetch resource:') 106 return objects 107 108 def BatchRequests(self, requests, errors_to_collect=None): 109 """Issues batch request for given set of requests. 110 111 Args: 112 requests: list(tuple(service, method, payload)), where service is 113 apitools.base.py.base_api.BaseApiService, method is str, method name, 114 e.g. 'Get', 'CreateInstance', payload is a subclass of 115 apitools.base.protorpclite.messages.Message. 116 errors_to_collect: list, output only, can be None, contains instances of 117 api_exceptions.HttpException for each request with exception. 118 119 Returns: 120 list of responses, matching list of requests. Some responses can be 121 errors. 122 """ 123 batch_request = batch.BatchApiRequest(batch_url=self._batch_url) 124 for service, method, request in requests: 125 batch_request.Add(service, method, request) 126 127 payloads = batch_request.Execute( 128 self._client.http, max_batch_size=_BATCH_SIZE_LIMIT) 129 130 responses = [] 131 errors = errors_to_collect if errors_to_collect is not None else [] 132 133 for payload in payloads: 134 if payload.is_error: 135 if isinstance(payload.exception, apitools_exceptions.HttpError): 136 errors.append(api_exceptions.HttpException(payload.exception)) 137 else: 138 errors.append(Error(payload.exception.message)) 139 140 responses.append(payload.response) 141 142 return responses 143