gcpdiag.queries.lb

Queries related to load balancer.
class LoadBalancerType(enum.Enum):
28class LoadBalancerType(Enum):
29  """Load balancer type."""
30
31  LOAD_BALANCER_TYPE_UNSPECIFIED = 0
32  EXTERNAL_PASSTHROUGH_LB = 1
33  INTERNAL_PASSTHROUGH_LB = 2
34  TARGET_POOL_LB = 3  # deprecated but customers still have them
35  GLOBAL_EXTERNAL_PROXY_NETWORK_LB = 4  # envoy based proxy lb
36  REGIONAL_INTERNAL_PROXY_NETWORK_LB = 5
37  REGIONAL_EXTERNAL_PROXY_NETWORK_LB = 6
38  CROSS_REGION_INTERNAL_PROXY_NETWORK_LB = 7
39  CLASSIC_PROXY_NETWORK_LB = 8
40  GLOBAL_EXTERNAL_APPLICATION_LB = 9  # envoy based application lb
41  REGIONAL_INTERNAL_APPLICATION_LB = 10
42  REGIONAL_EXTERNAL_APPLICATION_LB = 11
43  CROSS_REGION_INTERNAL_APPLICATION_LB = 12
44  CLASSIC_APPLICATION_LB = 13

Load balancer type.

LOAD_BALANCER_TYPE_UNSPECIFIED = <LoadBalancerType.LOAD_BALANCER_TYPE_UNSPECIFIED: 0>
EXTERNAL_PASSTHROUGH_LB = <LoadBalancerType.EXTERNAL_PASSTHROUGH_LB: 1>
INTERNAL_PASSTHROUGH_LB = <LoadBalancerType.INTERNAL_PASSTHROUGH_LB: 2>
TARGET_POOL_LB = <LoadBalancerType.TARGET_POOL_LB: 3>
GLOBAL_EXTERNAL_PROXY_NETWORK_LB = <LoadBalancerType.GLOBAL_EXTERNAL_PROXY_NETWORK_LB: 4>
REGIONAL_INTERNAL_PROXY_NETWORK_LB = <LoadBalancerType.REGIONAL_INTERNAL_PROXY_NETWORK_LB: 5>
REGIONAL_EXTERNAL_PROXY_NETWORK_LB = <LoadBalancerType.REGIONAL_EXTERNAL_PROXY_NETWORK_LB: 6>
CROSS_REGION_INTERNAL_PROXY_NETWORK_LB = <LoadBalancerType.CROSS_REGION_INTERNAL_PROXY_NETWORK_LB: 7>
CLASSIC_PROXY_NETWORK_LB = <LoadBalancerType.CLASSIC_PROXY_NETWORK_LB: 8>
GLOBAL_EXTERNAL_APPLICATION_LB = <LoadBalancerType.GLOBAL_EXTERNAL_APPLICATION_LB: 9>
REGIONAL_INTERNAL_APPLICATION_LB = <LoadBalancerType.REGIONAL_INTERNAL_APPLICATION_LB: 10>
REGIONAL_EXTERNAL_APPLICATION_LB = <LoadBalancerType.REGIONAL_EXTERNAL_APPLICATION_LB: 11>
CROSS_REGION_INTERNAL_APPLICATION_LB = <LoadBalancerType.CROSS_REGION_INTERNAL_APPLICATION_LB: 12>
CLASSIC_APPLICATION_LB = <LoadBalancerType.CLASSIC_APPLICATION_LB: 13>
def get_load_balancer_type_name(lb_type: LoadBalancerType) -> str:
47def get_load_balancer_type_name(lb_type: LoadBalancerType) -> str:
48  """Returns a human-readable name for the given load balancer type."""
49
50  type_names = {
51      LoadBalancerType.LOAD_BALANCER_TYPE_UNSPECIFIED:
52          'Unspecified',
53      LoadBalancerType.EXTERNAL_PASSTHROUGH_LB:
54          ('External Passthrough Network Load Balancer'),
55      LoadBalancerType.INTERNAL_PASSTHROUGH_LB:
56          ('Internal Passthrough Network Load Balancer'),
57      LoadBalancerType.TARGET_POOL_LB:
58          'Target Pool Network Load Balancer',
59      LoadBalancerType.GLOBAL_EXTERNAL_PROXY_NETWORK_LB:
60          ('Global External Proxy Network Load Balancer'),
61      LoadBalancerType.REGIONAL_INTERNAL_PROXY_NETWORK_LB:
62          ('Regional Internal Proxy Network Load Balancer'),
63      LoadBalancerType.REGIONAL_EXTERNAL_PROXY_NETWORK_LB:
64          ('Regional External Proxy Network Load Balancer'),
65      LoadBalancerType.CROSS_REGION_INTERNAL_PROXY_NETWORK_LB:
66          ('Cross-Region Internal Proxy Network Load Balancer'),
67      LoadBalancerType.CLASSIC_PROXY_NETWORK_LB:
68          ('Classic Proxy Network Load Balancer'),
69      LoadBalancerType.GLOBAL_EXTERNAL_APPLICATION_LB:
70          ('Global External Application Load Balancer'),
71      LoadBalancerType.REGIONAL_INTERNAL_APPLICATION_LB:
72          ('Regional Internal Application Load Balancer'),
73      LoadBalancerType.REGIONAL_EXTERNAL_APPLICATION_LB:
74          ('Regional External Application Load Balancer'),
75      LoadBalancerType.CROSS_REGION_INTERNAL_APPLICATION_LB:
76          ('Cross-Region Internal Application Load Balancer'),
77      LoadBalancerType.CLASSIC_APPLICATION_LB:
78          ('Classic Application Load Balancer'),
79  }
80  return type_names.get(lb_type, 'Unspecified')

Returns a human-readable name for the given load balancer type.

def get_load_balancer_type( load_balancing_scheme: str, scope: str, layer: Literal['application', 'network'], backend_service_based: bool = True) -> LoadBalancerType:
 83def get_load_balancer_type(
 84    load_balancing_scheme: str,
 85    scope: str,
 86    layer: Literal['application', 'network'],
 87    backend_service_based: bool = True,
 88) -> LoadBalancerType:
 89  if load_balancing_scheme == 'EXTERNAL':
 90    if not scope or scope == 'global':
 91      if layer == 'application':
 92        return LoadBalancerType.CLASSIC_APPLICATION_LB
 93      else:
 94        return LoadBalancerType.CLASSIC_PROXY_NETWORK_LB
 95    else:
 96      return (LoadBalancerType.EXTERNAL_PASSTHROUGH_LB
 97              if backend_service_based else LoadBalancerType.TARGET_POOL_LB)
 98  elif load_balancing_scheme == 'INTERNAL':
 99    return LoadBalancerType.INTERNAL_PASSTHROUGH_LB
100  elif load_balancing_scheme == 'INTERNAL_MANAGED':
101    if not scope or scope == 'global':
102      if layer == 'application':
103        return LoadBalancerType.CROSS_REGION_INTERNAL_APPLICATION_LB
104      else:
105        return LoadBalancerType.CROSS_REGION_INTERNAL_PROXY_NETWORK_LB
106    else:
107      if layer == 'application':
108        return LoadBalancerType.REGIONAL_INTERNAL_APPLICATION_LB
109      else:
110        return LoadBalancerType.REGIONAL_INTERNAL_PROXY_NETWORK_LB
111  elif load_balancing_scheme == 'EXTERNAL_MANAGED':
112    if not scope or scope == 'global':
113      if layer == 'application':
114        return LoadBalancerType.GLOBAL_EXTERNAL_APPLICATION_LB
115      else:
116        return LoadBalancerType.GLOBAL_EXTERNAL_PROXY_NETWORK_LB
117    else:
118      if layer == 'application':
119        return LoadBalancerType.REGIONAL_EXTERNAL_APPLICATION_LB
120      else:
121        return LoadBalancerType.REGIONAL_EXTERNAL_PROXY_NETWORK_LB
122  return LoadBalancerType.LOAD_BALANCER_TYPE_UNSPECIFIED
def normalize_url(url: str) -> str:
125def normalize_url(url: str) -> str:
126  """Returns normalized url."""
127  result = re.match(r'https://www.googleapis.com/compute/v1/(.*)', url)
128  if result:
129    return result.group(1)
130  else:
131    return ''

Returns normalized url.

class BackendServices(gcpdiag.models.Resource):
134class BackendServices(models.Resource):
135  """A Backend Service resource."""
136
137  _resource_data: dict
138  _type: str
139
140  def __init__(self, project_id, resource_data):
141    super().__init__(project_id=project_id)
142    self._resource_data = resource_data
143
144  @property
145  def name(self) -> str:
146    return self._resource_data['name']
147
148  @property
149  def id(self) -> str:
150    return self._resource_data['id']
151
152  @property
153  def full_path(self) -> str:
154    result = re.match(r'https://www.googleapis.com/compute/v1/(.*)',
155                      self.self_link)
156    if result:
157      return result.group(1)
158    else:
159      return f'>> {self.self_link}'
160
161  @property
162  def short_path(self) -> str:
163    path = self.project_id + '/' + self.name
164    return path
165
166  @property
167  def self_link(self) -> str:
168    return self._resource_data['selfLink']
169
170  @property
171  def session_affinity(self) -> str:
172    return self._resource_data.get('sessionAffinity', 'NONE')
173
174  @property
175  def timeout_sec(self) -> int:
176    return self._resource_data.get('timeoutSec', None)
177
178  @property
179  def locality_lb_policy(self) -> str:
180    return self._resource_data.get('localityLbPolicy', 'ROUND_ROBIN')
181
182  @property
183  def is_enable_cdn(self) -> str:
184    return self._resource_data.get('enableCDN', False)
185
186  @property
187  def draining_timeout_sec(self) -> int:
188    return self._resource_data.get('connectionDraining',
189                                   {}).get('drainingTimeoutSec', 0)
190
191  @property
192  def load_balancing_scheme(self) -> str:
193    return self._resource_data.get('loadBalancingScheme', None)
194
195  @property
196  def health_check(self):
197    if 'healthChecks' not in self._resource_data:
198      return None
199    health_check_url = self._resource_data['healthChecks'][0]
200    matches = re.search(r'/([^/]+)$', health_check_url)
201    if matches:
202      healthcheck_name = matches.group(1)
203      return healthcheck_name
204    else:
205      return None
206
207  @property
208  def health_check_region(self):
209    if 'healthChecks' not in self._resource_data:
210      return None
211    health_check_url = self._resource_data['healthChecks'][0]
212    m = re.search(r'/regions/([^/]+)', health_check_url)
213    if m:
214      return m.group(1)
215    else:
216      return None
217
218  @property
219  def backends(self) -> List[dict]:
220    return self._resource_data.get('backends', [])
221
222  @property
223  def region(self):
224    try:
225      url = self._resource_data.get('region')
226      if url is not None:
227        match = re.search(r'/([^/]+)/?$', url)
228        if match is not None:
229          region = match.group(1)
230          return region
231        else:
232          return None
233    except KeyError:
234      return None
235
236  @property
237  def protocol(self) -> str:
238    return self._resource_data.get('protocol', None)
239
240  @property
241  def port_name(self) -> str:
242    return self._resource_data.get('portName', None)
243
244  @property
245  def used_by_refs(self) -> List[str]:
246    used_by = []
247    for x in self._resource_data.get('usedBy', []):
248      reference = x.get('reference')
249      if reference:
250        match = re.match(r'https://www.googleapis.com/compute/v1/(.*)',
251                         reference)
252        if match:
253          used_by.append(match.group(1))
254    return used_by
255
256  @property
257  def load_balancer_type(self) -> LoadBalancerType:
258    application_protocols = ['HTTP', 'HTTPS', 'HTTP2']
259    return get_load_balancer_type(
260        self.load_balancing_scheme,
261        self.region,
262        'application' if self.protocol in application_protocols else 'network',
263        backend_service_based=True,
264    )

A Backend Service resource.

BackendServices(project_id, resource_data)
140  def __init__(self, project_id, resource_data):
141    super().__init__(project_id=project_id)
142    self._resource_data = resource_data
name: str
144  @property
145  def name(self) -> str:
146    return self._resource_data['name']
id: str
148  @property
149  def id(self) -> str:
150    return self._resource_data['id']
full_path: str
152  @property
153  def full_path(self) -> str:
154    result = re.match(r'https://www.googleapis.com/compute/v1/(.*)',
155                      self.self_link)
156    if result:
157      return result.group(1)
158    else:
159      return f'>> {self.self_link}'

Returns the full path of this resource.

Example: 'projects/gcpdiag-gke-1-9b90/zones/europe-west4-a/clusters/gke1'

short_path: str
161  @property
162  def short_path(self) -> str:
163    path = self.project_id + '/' + self.name
164    return path

Returns the short name for this resource.

Note that it isn't clear from this name what kind of resource it is.

Example: 'gke1'

session_affinity: str
170  @property
171  def session_affinity(self) -> str:
172    return self._resource_data.get('sessionAffinity', 'NONE')
timeout_sec: int
174  @property
175  def timeout_sec(self) -> int:
176    return self._resource_data.get('timeoutSec', None)
locality_lb_policy: str
178  @property
179  def locality_lb_policy(self) -> str:
180    return self._resource_data.get('localityLbPolicy', 'ROUND_ROBIN')
is_enable_cdn: str
182  @property
183  def is_enable_cdn(self) -> str:
184    return self._resource_data.get('enableCDN', False)
draining_timeout_sec: int
186  @property
187  def draining_timeout_sec(self) -> int:
188    return self._resource_data.get('connectionDraining',
189                                   {}).get('drainingTimeoutSec', 0)
load_balancing_scheme: str
191  @property
192  def load_balancing_scheme(self) -> str:
193    return self._resource_data.get('loadBalancingScheme', None)
health_check
195  @property
196  def health_check(self):
197    if 'healthChecks' not in self._resource_data:
198      return None
199    health_check_url = self._resource_data['healthChecks'][0]
200    matches = re.search(r'/([^/]+)$', health_check_url)
201    if matches:
202      healthcheck_name = matches.group(1)
203      return healthcheck_name
204    else:
205      return None
health_check_region
207  @property
208  def health_check_region(self):
209    if 'healthChecks' not in self._resource_data:
210      return None
211    health_check_url = self._resource_data['healthChecks'][0]
212    m = re.search(r'/regions/([^/]+)', health_check_url)
213    if m:
214      return m.group(1)
215    else:
216      return None
backends: List[dict]
218  @property
219  def backends(self) -> List[dict]:
220    return self._resource_data.get('backends', [])
region
222  @property
223  def region(self):
224    try:
225      url = self._resource_data.get('region')
226      if url is not None:
227        match = re.search(r'/([^/]+)/?$', url)
228        if match is not None:
229          region = match.group(1)
230          return region
231        else:
232          return None
233    except KeyError:
234      return None
protocol: str
236  @property
237  def protocol(self) -> str:
238    return self._resource_data.get('protocol', None)
port_name: str
240  @property
241  def port_name(self) -> str:
242    return self._resource_data.get('portName', None)
used_by_refs: List[str]
244  @property
245  def used_by_refs(self) -> List[str]:
246    used_by = []
247    for x in self._resource_data.get('usedBy', []):
248      reference = x.get('reference')
249      if reference:
250        match = re.match(r'https://www.googleapis.com/compute/v1/(.*)',
251                         reference)
252        if match:
253          used_by.append(match.group(1))
254    return used_by
load_balancer_type: LoadBalancerType
256  @property
257  def load_balancer_type(self) -> LoadBalancerType:
258    application_protocols = ['HTTP', 'HTTPS', 'HTTP2']
259    return get_load_balancer_type(
260        self.load_balancing_scheme,
261        self.region,
262        'application' if self.protocol in application_protocols else 'network',
263        backend_service_based=True,
264    )
@caching.cached_api_call(in_memory=True)
def get_backend_services(project_id: str) -> List[BackendServices]:
267@caching.cached_api_call(in_memory=True)
268def get_backend_services(project_id: str) -> List[BackendServices]:
269  logging.debug('fetching Backend Services: %s', project_id)
270  compute = apis.get_api('compute', 'v1', project_id)
271  backend_services = []
272  request = compute.backendServices().aggregatedList(project=project_id)
273  response = request.execute(num_retries=config.API_RETRIES)
274  backend_services_by_region = response['items']
275  for _, data_ in backend_services_by_region.items():
276    if 'backendServices' not in data_:
277      continue
278    backend_services.extend([
279        BackendServices(project_id, backend_service)
280        for backend_service in data_['backendServices']
281    ])
282  return backend_services
@caching.cached_api_call(in_memory=True)
def get_backend_service( project_id: str, backend_service_name: str, region: str = None) -> BackendServices:
285@caching.cached_api_call(in_memory=True)
286def get_backend_service(project_id: str,
287                        backend_service_name: str,
288                        region: str = None) -> BackendServices:
289  """Returns instance object matching backend service name and region"""
290  compute = apis.get_api('compute', 'v1', project_id)
291  try:
292    if not region or region == 'global':
293      request = compute.backendServices().get(
294          project=project_id, backendService=backend_service_name)
295    else:
296      request = compute.regionBackendServices().get(
297          project=project_id,
298          region=region,
299          backendService=backend_service_name)
300
301    response = request.execute(num_retries=config.API_RETRIES)
302    return BackendServices(project_id, resource_data=response)
303  except googleapiclient.errors.HttpError as err:
304    raise utils.GcpApiError(err) from err

Returns instance object matching backend service name and region

class BackendHealth:
319class BackendHealth:
320  """A Backend Service resource."""
321
322  _resource_data: dict
323
324  def __init__(self, resource_data, group):
325    self._resource_data = resource_data
326    self._group = group
327
328  @property
329  def instance(self) -> str:
330    return self._resource_data['instance']
331
332  @property
333  def group(self) -> str:
334    return self._group
335
336  @property
337  def health_state(self) -> str:
338    return self._resource_data.get('healthState', 'UNHEALTHY')

A Backend Service resource.

BackendHealth(resource_data, group)
324  def __init__(self, resource_data, group):
325    self._resource_data = resource_data
326    self._group = group
instance: str
328  @property
329  def instance(self) -> str:
330    return self._resource_data['instance']
group: str
332  @property
333  def group(self) -> str:
334    return self._group
health_state: str
336  @property
337  def health_state(self) -> str:
338    return self._resource_data.get('healthState', 'UNHEALTHY')
@caching.cached_api_call(in_memory=True)
def get_backend_service_health( context: gcpdiag.models.Context, backend_service_name: str, backend_service_region: str = None) -> List[BackendHealth]:
341@caching.cached_api_call(in_memory=True)
342def get_backend_service_health(
343    context: models.Context,
344    backend_service_name: str,
345    backend_service_region: str = None,
346) -> List[BackendHealth]:
347  """Returns health data for backend service.
348
349  Args:
350    context: The project context.
351    backend_service_name: The name of the backend service.
352    backend_service_region: The region of the backend service.
353
354  Returns:
355    A list of BackendHealth objects.
356  """
357  project_id = context.project_id
358  try:
359    backend_service = get_backend_service(project_id, backend_service_name,
360                                          backend_service_region)
361  except googleapiclient.errors.HttpError:
362    return []
363
364  backend_health_statuses: List[BackendHealth] = []
365  compute = apis.get_api('compute', 'v1', project_id)
366  request_map = {}
367
368  for backend in backend_service.backends:
369    group = backend['group']
370    if not backend_service.region:
371      request = compute.backendServices().getHealth(
372          project=project_id,
373          backendService=backend_service.name,
374          body={'group': group})
375    else:
376      request = compute.regionBackendServices().getHealth(
377          project=project_id,
378          region=backend_service.region,
379          backendService=backend_service.name,
380          body={'group': group})
381    request_map[request] = group
382
383  for i, response, exception in apis_utils.execute_concurrently(
384      api=compute, requests=list(request_map.keys()), context=context):
385    group = request_map[i]
386    if exception:
387      logging.warning(
388          'getHealth API call failed for backend service %s, group %s: %s',
389          backend_service_name, group, exception)
390      continue
391
392    # None is returned when backend type doesn't support health check
393    if response is not None:
394      for health_status in response.get('healthStatus', []):
395        backend_health_statuses.append(BackendHealth(health_status, group))
396
397  return backend_health_statuses

Returns health data for backend service.

Arguments:
  • context: The project context.
  • backend_service_name: The name of the backend service.
  • backend_service_region: The region of the backend service.
Returns:

A list of BackendHealth objects.

class SslCertificate(gcpdiag.models.Resource):
400class SslCertificate(models.Resource):
401  """A SSL Certificate resource."""
402
403  _resource_data: dict
404  _type: str
405
406  def __init__(self, project_id, resource_data):
407    super().__init__(project_id=project_id)
408    self._resource_data = resource_data
409
410  @property
411  def name(self) -> str:
412    return self._resource_data['name']
413
414  @property
415  def id(self) -> str:
416    return self._resource_data['id']
417
418  @property
419  def full_path(self) -> str:
420    result = re.match(r'https://www.googleapis.com/compute/v1/(.*)',
421                      self.self_link)
422    if result:
423      return result.group(1)
424    else:
425      return f'>> {self.self_link}'
426
427  @property
428  def self_link(self) -> str:
429    return self._resource_data['selfLink']
430
431  @property
432  def type(self) -> str:
433    return self._resource_data.get('type', 'SELF_MANAGED')
434
435  @property
436  def status(self) -> str:
437    return self._resource_data.get('managed', {}).get('status')
438
439  @property
440  def domains(self) -> List[str]:
441    return self._resource_data.get('managed', {}).get('domains', [])
442
443  @property
444  def domain_status(self) -> Dict[str, str]:
445    return self._resource_data.get('managed', {}).get('domainStatus', {})

A SSL Certificate resource.

SslCertificate(project_id, resource_data)
406  def __init__(self, project_id, resource_data):
407    super().__init__(project_id=project_id)
408    self._resource_data = resource_data
name: str
410  @property
411  def name(self) -> str:
412    return self._resource_data['name']
id: str
414  @property
415  def id(self) -> str:
416    return self._resource_data['id']
full_path: str
418  @property
419  def full_path(self) -> str:
420    result = re.match(r'https://www.googleapis.com/compute/v1/(.*)',
421                      self.self_link)
422    if result:
423      return result.group(1)
424    else:
425      return f'>> {self.self_link}'

Returns the full path of this resource.

Example: 'projects/gcpdiag-gke-1-9b90/zones/europe-west4-a/clusters/gke1'

type: str
431  @property
432  def type(self) -> str:
433    return self._resource_data.get('type', 'SELF_MANAGED')
status: str
435  @property
436  def status(self) -> str:
437    return self._resource_data.get('managed', {}).get('status')
domains: List[str]
439  @property
440  def domains(self) -> List[str]:
441    return self._resource_data.get('managed', {}).get('domains', [])
domain_status: Dict[str, str]
443  @property
444  def domain_status(self) -> Dict[str, str]:
445    return self._resource_data.get('managed', {}).get('domainStatus', {})
@caching.cached_api_call(in_memory=True)
def get_ssl_certificate( project_id: str, certificate_name: str) -> SslCertificate:
448@caching.cached_api_call(in_memory=True)
449def get_ssl_certificate(
450    project_id: str,
451    certificate_name: str,
452) -> SslCertificate:
453  """Returns object matching certificate name and region"""
454  compute = apis.get_api('compute', 'v1', project_id)
455
456  request = compute.sslCertificates().get(project=project_id,
457                                          sslCertificate=certificate_name)
458
459  response = request.execute(num_retries=config.API_RETRIES)
460  return SslCertificate(project_id, resource_data=response)

Returns object matching certificate name and region

class ForwardingRules(gcpdiag.models.Resource):
463class ForwardingRules(models.Resource):
464  """A Forwarding Rule resource."""
465
466  _resource_data: dict
467  _type: str
468
469  def __init__(self, project_id, resource_data):
470    super().__init__(project_id=project_id)
471    self._resource_data = resource_data
472
473  @property
474  def name(self) -> str:
475    return self._resource_data['name']
476
477  @property
478  def id(self) -> str:
479    return self._resource_data['id']
480
481  @property
482  def full_path(self) -> str:
483    result = re.match(r'https://www.googleapis.com/compute/v1/(.*)',
484                      self.self_link)
485    if result:
486      return result.group(1)
487    else:
488      return f'>> {self.self_link}'
489
490  @property
491  def short_path(self) -> str:
492    path = self.project_id + '/' + self.name
493    return path
494
495  @property
496  def region(self):
497    url = self._resource_data.get('region', '')
498    if url is not None:
499      match = re.search(r'/([^/]+)/?$', url)
500      if match is not None:
501        region = match.group(1)
502        return region
503    return 'global'
504
505  @property
506  def self_link(self) -> str:
507    return self._resource_data['selfLink']
508
509  @property
510  def global_access_allowed(self) -> bool:
511    return self._resource_data.get('allowGlobalAccess', False)
512
513  @property
514  def load_balancing_scheme(self) -> str:
515    return self._resource_data.get('loadBalancingScheme', None)
516
517  @property
518  def target(self) -> str:
519    full_path = self._resource_data.get('target', '')
520    result = re.match(r'https://www.googleapis.com/compute/v1/(.*)', full_path)
521    if result:
522      return result.group(1)
523    else:
524      return ''
525
526  @property
527  def backend_service(self) -> str:
528    full_path = self._resource_data.get('backendService', '')
529    result = re.match(r'https://www.googleapis.com/compute/v1/(.*)', full_path)
530    if result:
531      return result.group(1)
532    else:
533      return ''
534
535  @property
536  def ip_address(self) -> str:
537    return self._resource_data.get('IPAddress', '')
538
539  @property
540  def port_range(self) -> str:
541    return self._resource_data.get('portRange', '')
542
543  @caching.cached_api_call(in_memory=True)
544  def get_related_backend_services(self) -> List[BackendServices]:
545    """Returns the backend services related to the forwarding rule."""
546    if self.backend_service:
547      resource = get_backend_service_by_self_link(self.backend_service)
548      return [resource] if resource else []
549    if self.target:
550      target_proxy_target = get_target_proxy_reference(self.target)
551      if not target_proxy_target:
552        return []
553      target_proxy_target_type = target_proxy_target.split('/')[-2]
554      if target_proxy_target_type == 'backendServices':
555        resource = get_backend_service_by_self_link(target_proxy_target)
556        return [resource] if resource else []
557      elif target_proxy_target_type == 'urlMaps':
558        # Currently it doesn't work for shared-vpc backend services
559        backend_services = get_backend_services(self.project_id)
560        return [
561            backend_service for backend_service in backend_services
562            if target_proxy_target in backend_service.used_by_refs
563        ]
564    return []
565
566  @property
567  def load_balancer_type(self) -> LoadBalancerType:
568    target_type = None
569    if self.target:
570      parts = self.target.split('/')
571      if len(parts) >= 2:
572        target_type = parts[-2]
573
574    application_targets = [
575        'targetHttpProxies',
576        'targetHttpsProxies',
577        'targetGrpcProxies',
578    ]
579
580    return get_load_balancer_type(
581        self.load_balancing_scheme,
582        self.region,
583        'application' if target_type in application_targets else 'network',
584        target_type != 'targetPools',
585    )

A Forwarding Rule resource.

ForwardingRules(project_id, resource_data)
469  def __init__(self, project_id, resource_data):
470    super().__init__(project_id=project_id)
471    self._resource_data = resource_data
name: str
473  @property
474  def name(self) -> str:
475    return self._resource_data['name']
id: str
477  @property
478  def id(self) -> str:
479    return self._resource_data['id']
full_path: str
481  @property
482  def full_path(self) -> str:
483    result = re.match(r'https://www.googleapis.com/compute/v1/(.*)',
484                      self.self_link)
485    if result:
486      return result.group(1)
487    else:
488      return f'>> {self.self_link}'

Returns the full path of this resource.

Example: 'projects/gcpdiag-gke-1-9b90/zones/europe-west4-a/clusters/gke1'

short_path: str
490  @property
491  def short_path(self) -> str:
492    path = self.project_id + '/' + self.name
493    return path

Returns the short name for this resource.

Note that it isn't clear from this name what kind of resource it is.

Example: 'gke1'

region
495  @property
496  def region(self):
497    url = self._resource_data.get('region', '')
498    if url is not None:
499      match = re.search(r'/([^/]+)/?$', url)
500      if match is not None:
501        region = match.group(1)
502        return region
503    return 'global'
global_access_allowed: bool
509  @property
510  def global_access_allowed(self) -> bool:
511    return self._resource_data.get('allowGlobalAccess', False)
load_balancing_scheme: str
513  @property
514  def load_balancing_scheme(self) -> str:
515    return self._resource_data.get('loadBalancingScheme', None)
target: str
517  @property
518  def target(self) -> str:
519    full_path = self._resource_data.get('target', '')
520    result = re.match(r'https://www.googleapis.com/compute/v1/(.*)', full_path)
521    if result:
522      return result.group(1)
523    else:
524      return ''
backend_service: str
526  @property
527  def backend_service(self) -> str:
528    full_path = self._resource_data.get('backendService', '')
529    result = re.match(r'https://www.googleapis.com/compute/v1/(.*)', full_path)
530    if result:
531      return result.group(1)
532    else:
533      return ''
ip_address: str
535  @property
536  def ip_address(self) -> str:
537    return self._resource_data.get('IPAddress', '')
port_range: str
539  @property
540  def port_range(self) -> str:
541    return self._resource_data.get('portRange', '')
load_balancer_type: LoadBalancerType
566  @property
567  def load_balancer_type(self) -> LoadBalancerType:
568    target_type = None
569    if self.target:
570      parts = self.target.split('/')
571      if len(parts) >= 2:
572        target_type = parts[-2]
573
574    application_targets = [
575        'targetHttpProxies',
576        'targetHttpsProxies',
577        'targetGrpcProxies',
578    ]
579
580    return get_load_balancer_type(
581        self.load_balancing_scheme,
582        self.region,
583        'application' if target_type in application_targets else 'network',
584        target_type != 'targetPools',
585    )
@caching.cached_api_call(in_memory=True)
def get_target_proxy_reference(target_proxy_self_link: str) -> str:
588@caching.cached_api_call(in_memory=True)
589def get_target_proxy_reference(target_proxy_self_link: str) -> str:
590  """Retrieves the URL map or backend service associated with a given target proxy.
591
592  Args:
593    target_proxy_self_link: self link of the target proxy
594
595  Returns:
596    The url map or the backend service self link
597  """
598  target_proxy_type = target_proxy_self_link.split('/')[-2]
599  target_proxy_name = target_proxy_self_link.split('/')[-1]
600  target_proxy_scope = target_proxy_self_link.split('/')[-3]
601  match_result = re.match(r'projects/([^/]+)/', target_proxy_self_link)
602  if not match_result:
603    return ''
604  project_id = match_result.group(1)
605  compute = apis.get_api('compute', 'v1', project_id)
606
607  request = None
608  if target_proxy_type == 'targetHttpsProxies':
609    if target_proxy_scope == 'global':
610      request = compute.targetHttpsProxies().get(
611          project=project_id, targetHttpsProxy=target_proxy_name)
612    else:
613      request = compute.regionTargetHttpsProxies().get(
614          project=project_id,
615          region=target_proxy_scope,
616          targetHttpsProxy=target_proxy_name,
617      )
618  elif target_proxy_type == 'targetHttpProxies':
619    if target_proxy_scope == 'global':
620      request = compute.targetHttpProxies().get(
621          project=project_id, targetHttpProxy=target_proxy_name)
622    else:
623      request = compute.regionTargetHttpProxies().get(
624          project=project_id,
625          region=target_proxy_scope,
626          targetHttpProxy=target_proxy_name,
627      )
628  elif target_proxy_type == 'targetTcpProxies':
629    if target_proxy_scope == 'global':
630      request = compute.targetTcpProxies().get(project=project_id,
631                                               targetTcpProxy=target_proxy_name)
632    else:
633      request = compute.regionTargetTcpProxies().get(
634          project=project_id,
635          region=target_proxy_scope,
636          targetTcpProxy=target_proxy_name,
637      )
638  elif target_proxy_type == 'targetSslProxies':
639    request = compute.targetSslProxies().get(project=project_id,
640                                             targetSslProxy=target_proxy_name)
641  elif target_proxy_type == 'targetGrcpProxies':
642    request = compute.targetGrpcProxies().get(project=project_id,
643                                              targetGrpcProxy=target_proxy_name)
644  if not request:
645    # target is not target proxy
646    return ''
647  response = request.execute(num_retries=config.API_RETRIES)
648  if 'urlMap' in response:
649    return normalize_url(response['urlMap'])
650  if 'service' in response:
651    return normalize_url(response['service'])
652  return ''

Retrieves the URL map or backend service associated with a given target proxy.

Arguments:
  • target_proxy_self_link: self link of the target proxy
Returns:

The url map or the backend service self link

@caching.cached_api_call(in_memory=True)
def get_forwarding_rules(project_id: str) -> List[ForwardingRules]:
655@caching.cached_api_call(in_memory=True)
656def get_forwarding_rules(project_id: str) -> List[ForwardingRules]:
657  logging.debug('fetching Forwarding Rules: %s', project_id)
658  compute = apis.get_api('compute', 'v1', project_id)
659  forwarding_rules = []
660  request = compute.forwardingRules().aggregatedList(project=project_id)
661  response = request.execute(num_retries=config.API_RETRIES)
662  forwarding_rules_by_region = response['items']
663  for _, data_ in forwarding_rules_by_region.items():
664    if 'forwardingRules' not in data_:
665      continue
666    forwarding_rules.extend([
667        ForwardingRules(project_id, forwarding_rule)
668        for forwarding_rule in data_['forwardingRules']
669    ])
670  return forwarding_rules
@caching.cached_api_call(in_memory=True)
def get_forwarding_rule( project_id: str, forwarding_rule_name: str, region: str = None) -> ForwardingRules:
673@caching.cached_api_call(in_memory=True)
674def get_forwarding_rule(project_id: str,
675                        forwarding_rule_name: str,
676                        region: str = None) -> ForwardingRules:
677  compute = apis.get_api('compute', 'v1', project_id)
678  if not region or region == 'global':
679    request = compute.globalForwardingRules().get(
680        project=project_id, forwardingRule=forwarding_rule_name)
681  else:
682    request = compute.forwardingRules().get(project=project_id,
683                                            region=region,
684                                            forwardingRule=forwarding_rule_name)
685  response = request.execute(num_retries=config.API_RETRIES)
686  return ForwardingRules(project_id, resource_data=response)
class TargetHttpsProxy(gcpdiag.models.Resource):
689class TargetHttpsProxy(models.Resource):
690  """A Target HTTPS Proxy resource."""
691
692  _resource_data: dict
693  _type: str
694
695  def __init__(self, project_id, resource_data):
696    super().__init__(project_id=project_id)
697    self._resource_data = resource_data
698
699  @property
700  def name(self) -> str:
701    return self._resource_data['name']
702
703  @property
704  def id(self) -> str:
705    return self._resource_data['id']
706
707  @property
708  def full_path(self) -> str:
709    result = re.match(r'https://www.googleapis.com/compute/v1/(.*)',
710                      self.self_link)
711    if result:
712      return result.group(1)
713    else:
714      return f'>> {self.self_link}'
715
716  @property
717  def self_link(self) -> str:
718    return self._resource_data['selfLink']
719
720  @property
721  def region(self):
722    url = self._resource_data.get('region', '')
723    if url is not None:
724      match = re.search(r'/([^/]+)/?$', url)
725      if match is not None:
726        region = match.group(1)
727        return region
728    return 'global'
729
730  @property
731  def ssl_certificates(self) -> List[str]:
732    return self._resource_data.get('sslCertificates', [])
733
734  @property
735  def certificate_map(self) -> str:
736    certificate_map = self._resource_data.get('certificateMap', '')
737    result = re.match(r'https://certificatemanager.googleapis.com/v1/(.*)',
738                      certificate_map)
739    if result:
740      return result.group(1)
741    return certificate_map

A Target HTTPS Proxy resource.

TargetHttpsProxy(project_id, resource_data)
695  def __init__(self, project_id, resource_data):
696    super().__init__(project_id=project_id)
697    self._resource_data = resource_data
name: str
699  @property
700  def name(self) -> str:
701    return self._resource_data['name']
id: str
703  @property
704  def id(self) -> str:
705    return self._resource_data['id']
full_path: str
707  @property
708  def full_path(self) -> str:
709    result = re.match(r'https://www.googleapis.com/compute/v1/(.*)',
710                      self.self_link)
711    if result:
712      return result.group(1)
713    else:
714      return f'>> {self.self_link}'

Returns the full path of this resource.

Example: 'projects/gcpdiag-gke-1-9b90/zones/europe-west4-a/clusters/gke1'

region
720  @property
721  def region(self):
722    url = self._resource_data.get('region', '')
723    if url is not None:
724      match = re.search(r'/([^/]+)/?$', url)
725      if match is not None:
726        region = match.group(1)
727        return region
728    return 'global'
ssl_certificates: List[str]
730  @property
731  def ssl_certificates(self) -> List[str]:
732    return self._resource_data.get('sslCertificates', [])
certificate_map: str
734  @property
735  def certificate_map(self) -> str:
736    certificate_map = self._resource_data.get('certificateMap', '')
737    result = re.match(r'https://certificatemanager.googleapis.com/v1/(.*)',
738                      certificate_map)
739    if result:
740      return result.group(1)
741    return certificate_map
@caching.cached_api_call(in_memory=True)
def get_target_https_proxies(project_id: str) -> List[TargetHttpsProxy]:
744@caching.cached_api_call(in_memory=True)
745def get_target_https_proxies(project_id: str) -> List[TargetHttpsProxy]:
746  logging.debug('fetching Target HTTPS Proxies: %s', project_id)
747  compute = apis.get_api('compute', 'v1', project_id)
748  target_https_proxies = []
749  request = compute.targetHttpsProxies().aggregatedList(project=project_id)
750  response = request.execute(num_retries=config.API_RETRIES)
751  target_https_proxies_by_region = response['items']
752  for _, data_ in target_https_proxies_by_region.items():
753    if 'targetHttpsProxies' not in data_:
754      continue
755    target_https_proxies.extend([
756        TargetHttpsProxy(project_id, target_https_proxy)
757        for target_https_proxy in data_['targetHttpsProxies']
758    ])
759
760  return target_https_proxies
class TargetSslProxy(gcpdiag.models.Resource):
763class TargetSslProxy(models.Resource):
764  """A Target SSL Proxy resource."""
765
766  _resource_data: dict
767  _type: str
768
769  def __init__(self, project_id, resource_data):
770    super().__init__(project_id=project_id)
771    self._resource_data = resource_data
772
773  @property
774  def name(self) -> str:
775    return self._resource_data['name']
776
777  @property
778  def id(self) -> str:
779    return self._resource_data['id']
780
781  @property
782  def full_path(self) -> str:
783    result = re.match(r'https://www.googleapis.com/compute/v1/(.*)',
784                      self.self_link)
785    if result:
786      return result.group(1)
787    else:
788      return f'>> {self.self_link}'
789
790  @property
791  def self_link(self) -> str:
792    return self._resource_data['selfLink']
793
794  @property
795  def region(self):
796    url = self._resource_data.get('region', '')
797    if url is not None:
798      match = re.search(r'/([^/]+)/?$', url)
799      if match is not None:
800        region = match.group(1)
801        return region
802    return 'global'
803
804  @property
805  def ssl_certificates(self) -> List[str]:
806    return self._resource_data.get('sslCertificates', [])
807
808  @property
809  def certificate_map(self) -> str:
810    certificate_map = self._resource_data.get('certificateMap', '')
811    result = re.match(r'https://certificatemanager.googleapis.com/v1/(.*)',
812                      certificate_map)
813    if result:
814      return result.group(1)
815    return certificate_map

A Target SSL Proxy resource.

TargetSslProxy(project_id, resource_data)
769  def __init__(self, project_id, resource_data):
770    super().__init__(project_id=project_id)
771    self._resource_data = resource_data
name: str
773  @property
774  def name(self) -> str:
775    return self._resource_data['name']
id: str
777  @property
778  def id(self) -> str:
779    return self._resource_data['id']
full_path: str
781  @property
782  def full_path(self) -> str:
783    result = re.match(r'https://www.googleapis.com/compute/v1/(.*)',
784                      self.self_link)
785    if result:
786      return result.group(1)
787    else:
788      return f'>> {self.self_link}'

Returns the full path of this resource.

Example: 'projects/gcpdiag-gke-1-9b90/zones/europe-west4-a/clusters/gke1'

region
794  @property
795  def region(self):
796    url = self._resource_data.get('region', '')
797    if url is not None:
798      match = re.search(r'/([^/]+)/?$', url)
799      if match is not None:
800        region = match.group(1)
801        return region
802    return 'global'
ssl_certificates: List[str]
804  @property
805  def ssl_certificates(self) -> List[str]:
806    return self._resource_data.get('sslCertificates', [])
certificate_map: str
808  @property
809  def certificate_map(self) -> str:
810    certificate_map = self._resource_data.get('certificateMap', '')
811    result = re.match(r'https://certificatemanager.googleapis.com/v1/(.*)',
812                      certificate_map)
813    if result:
814      return result.group(1)
815    return certificate_map
@caching.cached_api_call(in_memory=True)
def get_target_ssl_proxies(project_id: str) -> List[TargetSslProxy]:
818@caching.cached_api_call(in_memory=True)
819def get_target_ssl_proxies(project_id: str) -> List[TargetSslProxy]:
820  logging.debug('fetching Target SSL Proxies: %s', project_id)
821  compute = apis.get_api('compute', 'v1', project_id)
822  request = compute.targetSslProxies().list(project=project_id)
823  response = request.execute(num_retries=config.API_RETRIES)
824
825  return [
826      TargetSslProxy(project_id, item) for item in response.get('items', [])
827  ]
class LoadBalancerInsight(gcpdiag.models.Resource):
830class LoadBalancerInsight(models.Resource):
831  """Represents a Load Balancer Insights object"""
832
833  @property
834  def full_path(self) -> str:
835    return self._resource_data['name']
836
837  @property
838  def description(self) -> str:
839    return self._resource_data['description']
840
841  @property
842  def insight_subtype(self) -> str:
843    return self._resource_data['insightSubtype']
844
845  @property
846  def details(self) -> dict:
847    return self._resource_data['content']
848
849  @property
850  def is_firewall_rule_insight(self) -> bool:
851    firewall_rule_subtypes = (
852        'HEALTH_CHECK_FIREWALL_NOT_CONFIGURED',
853        'HEALTH_CHECK_FIREWALL_FULLY_BLOCKING',
854        'HEALTH_CHECK_FIREWALL_PARTIALLY_BLOCKING',
855        'HEALTH_CHECK_FIREWALL_INCONSISTENT',
856    )
857    return self.insight_subtype.startswith(firewall_rule_subtypes)
858
859  @property
860  def is_health_check_port_mismatch_insight(self) -> bool:
861    return self.insight_subtype == 'HEALTH_CHECK_PORT_MISMATCH'
862
863  def __init__(self, project_id, resource_data):
864    super().__init__(project_id=project_id)
865    self._resource_data = resource_data

Represents a Load Balancer Insights object

LoadBalancerInsight(project_id, resource_data)
863  def __init__(self, project_id, resource_data):
864    super().__init__(project_id=project_id)
865    self._resource_data = resource_data
full_path: str
833  @property
834  def full_path(self) -> str:
835    return self._resource_data['name']

Returns the full path of this resource.

Example: 'projects/gcpdiag-gke-1-9b90/zones/europe-west4-a/clusters/gke1'

description: str
837  @property
838  def description(self) -> str:
839    return self._resource_data['description']
insight_subtype: str
841  @property
842  def insight_subtype(self) -> str:
843    return self._resource_data['insightSubtype']
details: dict
845  @property
846  def details(self) -> dict:
847    return self._resource_data['content']
is_firewall_rule_insight: bool
849  @property
850  def is_firewall_rule_insight(self) -> bool:
851    firewall_rule_subtypes = (
852        'HEALTH_CHECK_FIREWALL_NOT_CONFIGURED',
853        'HEALTH_CHECK_FIREWALL_FULLY_BLOCKING',
854        'HEALTH_CHECK_FIREWALL_PARTIALLY_BLOCKING',
855        'HEALTH_CHECK_FIREWALL_INCONSISTENT',
856    )
857    return self.insight_subtype.startswith(firewall_rule_subtypes)
is_health_check_port_mismatch_insight: bool
859  @property
860  def is_health_check_port_mismatch_insight(self) -> bool:
861    return self.insight_subtype == 'HEALTH_CHECK_PORT_MISMATCH'
@caching.cached_api_call
def get_lb_insights_for_a_project(project_id: str, region: str = 'global'):
868@caching.cached_api_call
869def get_lb_insights_for_a_project(project_id: str, region: str = 'global'):
870  api = apis.get_api('recommender', 'v1', project_id)
871
872  insight_name = (f'projects/{project_id}/locations/{region}/insightTypes/'
873                  'google.networkanalyzer.networkservices.loadBalancerInsight')
874  insights = []
875  for insight in apis_utils.list_all(
876      request=api.projects().locations().insightTypes().insights().list(
877          parent=insight_name),
878      next_function=api.projects().locations().insightTypes().insights().
879      list_next,
880      response_keyword='insights',
881  ):
882    insights.append(LoadBalancerInsight(project_id, insight))
883  return insights