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: 'Unspecified',
52    LoadBalancerType.EXTERNAL_PASSTHROUGH_LB: ('External Passthrough Network Load Balancer'),
53    LoadBalancerType.INTERNAL_PASSTHROUGH_LB: ('Internal Passthrough Network Load Balancer'),
54    LoadBalancerType.TARGET_POOL_LB: 'Target Pool Network Load Balancer',
55    LoadBalancerType.GLOBAL_EXTERNAL_PROXY_NETWORK_LB: (
56      'Global External Proxy Network Load Balancer'
57    ),
58    LoadBalancerType.REGIONAL_INTERNAL_PROXY_NETWORK_LB: (
59      'Regional Internal Proxy Network Load Balancer'
60    ),
61    LoadBalancerType.REGIONAL_EXTERNAL_PROXY_NETWORK_LB: (
62      'Regional External Proxy Network Load Balancer'
63    ),
64    LoadBalancerType.CROSS_REGION_INTERNAL_PROXY_NETWORK_LB: (
65      'Cross-Region Internal Proxy Network Load Balancer'
66    ),
67    LoadBalancerType.CLASSIC_PROXY_NETWORK_LB: ('Classic Proxy Network Load Balancer'),
68    LoadBalancerType.GLOBAL_EXTERNAL_APPLICATION_LB: ('Global External Application Load Balancer'),
69    LoadBalancerType.REGIONAL_INTERNAL_APPLICATION_LB: (
70      'Regional Internal Application Load Balancer'
71    ),
72    LoadBalancerType.REGIONAL_EXTERNAL_APPLICATION_LB: (
73      'Regional External Application Load Balancer'
74    ),
75    LoadBalancerType.CROSS_REGION_INTERNAL_APPLICATION_LB: (
76      'Cross-Region Internal Application Load Balancer'
77    ),
78    LoadBalancerType.CLASSIC_APPLICATION_LB: ('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 (
 97        LoadBalancerType.EXTERNAL_PASSTHROUGH_LB
 98        if backend_service_based
 99        else LoadBalancerType.TARGET_POOL_LB
100      )
101  elif load_balancing_scheme == 'INTERNAL':
102    return LoadBalancerType.INTERNAL_PASSTHROUGH_LB
103  elif load_balancing_scheme == 'INTERNAL_MANAGED':
104    if not scope or scope == 'global':
105      if layer == 'application':
106        return LoadBalancerType.CROSS_REGION_INTERNAL_APPLICATION_LB
107      else:
108        return LoadBalancerType.CROSS_REGION_INTERNAL_PROXY_NETWORK_LB
109    else:
110      if layer == 'application':
111        return LoadBalancerType.REGIONAL_INTERNAL_APPLICATION_LB
112      else:
113        return LoadBalancerType.REGIONAL_INTERNAL_PROXY_NETWORK_LB
114  elif load_balancing_scheme == 'EXTERNAL_MANAGED':
115    if not scope or scope == 'global':
116      if layer == 'application':
117        return LoadBalancerType.GLOBAL_EXTERNAL_APPLICATION_LB
118      else:
119        return LoadBalancerType.GLOBAL_EXTERNAL_PROXY_NETWORK_LB
120    else:
121      if layer == 'application':
122        return LoadBalancerType.REGIONAL_EXTERNAL_APPLICATION_LB
123      else:
124        return LoadBalancerType.REGIONAL_EXTERNAL_PROXY_NETWORK_LB
125  return LoadBalancerType.LOAD_BALANCER_TYPE_UNSPECIFIED
def normalize_url(url: str) -> str:
128def normalize_url(url: str) -> str:
129  """Returns normalized url."""
130  result = re.match(r'https://www.googleapis.com/compute/v1/(.*)', url)
131  if result:
132    return result.group(1)
133  else:
134    return ''

Returns normalized url.

class BackendServices(gcpdiag.models.Resource):
137class BackendServices(models.Resource):
138  """A Backend Service resource."""
139
140  _resource_data: dict
141  _type: str
142
143  def __init__(self, project_id, resource_data):
144    super().__init__(project_id=project_id)
145    self._resource_data = resource_data
146
147  @property
148  def name(self) -> str:
149    return self._resource_data['name']
150
151  @property
152  def id(self) -> str:
153    return self._resource_data['id']
154
155  @property
156  def full_path(self) -> str:
157    result = re.match(r'https://www.googleapis.com/compute/v1/(.*)', self.self_link)
158    if result:
159      return result.group(1)
160    else:
161      return f'>> {self.self_link}'
162
163  @property
164  def short_path(self) -> str:
165    path = self.project_id + '/' + self.name
166    return path
167
168  @property
169  def self_link(self) -> str:
170    return self._resource_data['selfLink']
171
172  @property
173  def session_affinity(self) -> str:
174    return self._resource_data.get('sessionAffinity', 'NONE')
175
176  @property
177  def timeout_sec(self) -> int:
178    return self._resource_data.get('timeoutSec', None)
179
180  @property
181  def locality_lb_policy(self) -> str:
182    return self._resource_data.get('localityLbPolicy', 'ROUND_ROBIN')
183
184  @property
185  def is_enable_cdn(self) -> str:
186    return self._resource_data.get('enableCDN', False)
187
188  @property
189  def draining_timeout_sec(self) -> int:
190    return self._resource_data.get('connectionDraining', {}).get('drainingTimeoutSec', 0)
191
192  @property
193  def load_balancing_scheme(self) -> str:
194    return self._resource_data.get('loadBalancingScheme', None)
195
196  @property
197  def health_check(self):
198    if 'healthChecks' not in self._resource_data:
199      return None
200    health_check_url = self._resource_data['healthChecks'][0]
201    matches = re.search(r'/([^/]+)$', health_check_url)
202    if matches:
203      healthcheck_name = matches.group(1)
204      return healthcheck_name
205    else:
206      return None
207
208  @property
209  def health_check_region(self):
210    if 'healthChecks' not in self._resource_data:
211      return None
212    health_check_url = self._resource_data['healthChecks'][0]
213    m = re.search(r'/regions/([^/]+)', health_check_url)
214    if m:
215      return m.group(1)
216    else:
217      return None
218
219  @property
220  def backends(self) -> List[dict]:
221    return self._resource_data.get('backends', [])
222
223  @property
224  def region(self):
225    try:
226      url = self._resource_data.get('region')
227      if url is not None:
228        match = re.search(r'/([^/]+)/?$', url)
229        if match is not None:
230          region = match.group(1)
231          return region
232        else:
233          return None
234    except KeyError:
235      return None
236
237  @property
238  def protocol(self) -> str:
239    return self._resource_data.get('protocol', None)
240
241  @property
242  def port_name(self) -> str:
243    return self._resource_data.get('portName', None)
244
245  @property
246  def used_by_refs(self) -> List[str]:
247    used_by = []
248    for x in self._resource_data.get('usedBy', []):
249      reference = x.get('reference')
250      if reference:
251        match = re.match(r'https://www.googleapis.com/compute/v1/(.*)', 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)
143  def __init__(self, project_id, resource_data):
144    super().__init__(project_id=project_id)
145    self._resource_data = resource_data
name: str
147  @property
148  def name(self) -> str:
149    return self._resource_data['name']
id: str
151  @property
152  def id(self) -> str:
153    return self._resource_data['id']
full_path: str
155  @property
156  def full_path(self) -> str:
157    result = re.match(r'https://www.googleapis.com/compute/v1/(.*)', self.self_link)
158    if result:
159      return result.group(1)
160    else:
161      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
163  @property
164  def short_path(self) -> str:
165    path = self.project_id + '/' + self.name
166    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
172  @property
173  def session_affinity(self) -> str:
174    return self._resource_data.get('sessionAffinity', 'NONE')
timeout_sec: int
176  @property
177  def timeout_sec(self) -> int:
178    return self._resource_data.get('timeoutSec', None)
locality_lb_policy: str
180  @property
181  def locality_lb_policy(self) -> str:
182    return self._resource_data.get('localityLbPolicy', 'ROUND_ROBIN')
is_enable_cdn: str
184  @property
185  def is_enable_cdn(self) -> str:
186    return self._resource_data.get('enableCDN', False)
draining_timeout_sec: int
188  @property
189  def draining_timeout_sec(self) -> int:
190    return self._resource_data.get('connectionDraining', {}).get('drainingTimeoutSec', 0)
load_balancing_scheme: str
192  @property
193  def load_balancing_scheme(self) -> str:
194    return self._resource_data.get('loadBalancingScheme', None)
health_check
196  @property
197  def health_check(self):
198    if 'healthChecks' not in self._resource_data:
199      return None
200    health_check_url = self._resource_data['healthChecks'][0]
201    matches = re.search(r'/([^/]+)$', health_check_url)
202    if matches:
203      healthcheck_name = matches.group(1)
204      return healthcheck_name
205    else:
206      return None
health_check_region
208  @property
209  def health_check_region(self):
210    if 'healthChecks' not in self._resource_data:
211      return None
212    health_check_url = self._resource_data['healthChecks'][0]
213    m = re.search(r'/regions/([^/]+)', health_check_url)
214    if m:
215      return m.group(1)
216    else:
217      return None
backends: List[dict]
219  @property
220  def backends(self) -> List[dict]:
221    return self._resource_data.get('backends', [])
region
223  @property
224  def region(self):
225    try:
226      url = self._resource_data.get('region')
227      if url is not None:
228        match = re.search(r'/([^/]+)/?$', url)
229        if match is not None:
230          region = match.group(1)
231          return region
232        else:
233          return None
234    except KeyError:
235      return None
protocol: str
237  @property
238  def protocol(self) -> str:
239    return self._resource_data.get('protocol', None)
port_name: str
241  @property
242  def port_name(self) -> str:
243    return self._resource_data.get('portName', None)
used_by_refs: List[str]
245  @property
246  def used_by_refs(self) -> List[str]:
247    used_by = []
248    for x in self._resource_data.get('usedBy', []):
249      reference = x.get('reference')
250      if reference:
251        match = re.match(r'https://www.googleapis.com/compute/v1/(.*)', 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) for backend_service in data_['backendServices']]
280    )
281  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:
284@caching.cached_api_call(in_memory=True)
285def get_backend_service(
286  project_id: str, backend_service_name: str, region: str = None
287) -> BackendServices:
288  """Returns instance object matching backend service name and region"""
289  compute = apis.get_api('compute', 'v1', project_id)
290  try:
291    if not region or region == 'global':
292      request = compute.backendServices().get(
293        project=project_id, backendService=backend_service_name
294      )
295    else:
296      request = compute.regionBackendServices().get(
297        project=project_id, region=region, backendService=backend_service_name
298      )
299
300    response = request.execute(num_retries=config.API_RETRIES)
301    return BackendServices(project_id, resource_data=response)
302  except googleapiclient.errors.HttpError as err:
303    raise utils.GcpApiError(err) from err

Returns instance object matching backend service name and region

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

A Backend Service resource.

BackendHealth(resource_data, group)
323  def __init__(self, resource_data, group):
324    self._resource_data = resource_data
325    self._group = group
instance: str
327  @property
328  def instance(self) -> str:
329    return self._resource_data['instance']
group: str
331  @property
332  def group(self) -> str:
333    return self._group
health_state: str
335  @property
336  def health_state(self) -> str:
337    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]:
340@caching.cached_api_call(in_memory=True)
341def get_backend_service_health(
342  context: models.Context,
343  backend_service_name: str,
344  backend_service_region: str = None,
345) -> List[BackendHealth]:
346  """Returns health data for backend service.
347
348  Args:
349    context: The project context.
350    backend_service_name: The name of the backend service.
351    backend_service_region: The region of the backend service.
352
353  Returns:
354    A list of BackendHealth objects.
355  """
356  project_id = context.project_id
357  try:
358    backend_service = get_backend_service(project_id, backend_service_name, backend_service_region)
359  except googleapiclient.errors.HttpError:
360    return []
361
362  backend_health_statuses: List[BackendHealth] = []
363  compute = apis.get_api('compute', 'v1', project_id)
364  request_map = {}
365
366  for backend in backend_service.backends:
367    group = backend['group']
368    if not backend_service.region:
369      request = compute.backendServices().getHealth(
370        project=project_id, backendService=backend_service.name, body={'group': group}
371      )
372    else:
373      request = compute.regionBackendServices().getHealth(
374        project=project_id,
375        region=backend_service.region,
376        backendService=backend_service.name,
377        body={'group': group},
378      )
379    request_map[request] = group
380
381  for i, response, exception in apis_utils.execute_concurrently(
382    api=compute, requests=list(request_map.keys()), context=context
383  ):
384    group = request_map[i]
385    if exception:
386      logging.warning(
387        'getHealth API call failed for backend service %s, group %s: %s',
388        backend_service_name,
389        group,
390        exception,
391      )
392      continue
393
394    # None is returned when backend type doesn't support health check
395    if response is not None:
396      for health_status in response.get('healthStatus', []):
397        backend_health_statuses.append(BackendHealth(health_status, group))
398
399  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):
402class SslCertificate(models.Resource):
403  """A SSL Certificate resource."""
404
405  _resource_data: dict
406  _type: str
407
408  def __init__(self, project_id, resource_data):
409    super().__init__(project_id=project_id)
410    self._resource_data = resource_data
411
412  @property
413  def name(self) -> str:
414    return self._resource_data['name']
415
416  @property
417  def id(self) -> str:
418    return self._resource_data['id']
419
420  @property
421  def full_path(self) -> str:
422    result = re.match(r'https://www.googleapis.com/compute/v1/(.*)', self.self_link)
423    if result:
424      return result.group(1)
425    else:
426      return f'>> {self.self_link}'
427
428  @property
429  def self_link(self) -> str:
430    return self._resource_data['selfLink']
431
432  @property
433  def type(self) -> str:
434    return self._resource_data.get('type', 'SELF_MANAGED')
435
436  @property
437  def status(self) -> str:
438    return self._resource_data.get('managed', {}).get('status')
439
440  @property
441  def domains(self) -> List[str]:
442    return self._resource_data.get('managed', {}).get('domains', [])
443
444  @property
445  def domain_status(self) -> Dict[str, str]:
446    return self._resource_data.get('managed', {}).get('domainStatus', {})

A SSL Certificate resource.

SslCertificate(project_id, resource_data)
408  def __init__(self, project_id, resource_data):
409    super().__init__(project_id=project_id)
410    self._resource_data = resource_data
name: str
412  @property
413  def name(self) -> str:
414    return self._resource_data['name']
id: str
416  @property
417  def id(self) -> str:
418    return self._resource_data['id']
full_path: str
420  @property
421  def full_path(self) -> str:
422    result = re.match(r'https://www.googleapis.com/compute/v1/(.*)', self.self_link)
423    if result:
424      return result.group(1)
425    else:
426      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
432  @property
433  def type(self) -> str:
434    return self._resource_data.get('type', 'SELF_MANAGED')
status: str
436  @property
437  def status(self) -> str:
438    return self._resource_data.get('managed', {}).get('status')
domains: List[str]
440  @property
441  def domains(self) -> List[str]:
442    return self._resource_data.get('managed', {}).get('domains', [])
domain_status: Dict[str, str]
444  @property
445  def domain_status(self) -> Dict[str, str]:
446    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:
449@caching.cached_api_call(in_memory=True)
450def get_ssl_certificate(
451  project_id: str,
452  certificate_name: str,
453) -> SslCertificate:
454  """Returns object matching certificate name and region"""
455  compute = apis.get_api('compute', 'v1', project_id)
456
457  request = compute.sslCertificates().get(project=project_id, 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/(.*)', self.self_link)
484    if result:
485      return result.group(1)
486    else:
487      return f'>> {self.self_link}'
488
489  @property
490  def short_path(self) -> str:
491    path = self.project_id + '/' + self.name
492    return path
493
494  @property
495  def region(self):
496    url = self._resource_data.get('region', '')
497    if url is not None:
498      match = re.search(r'/([^/]+)/?$', url)
499      if match is not None:
500        region = match.group(1)
501        return region
502    return 'global'
503
504  @property
505  def self_link(self) -> str:
506    return self._resource_data['selfLink']
507
508  @property
509  def global_access_allowed(self) -> bool:
510    return self._resource_data.get('allowGlobalAccess', False)
511
512  @property
513  def load_balancing_scheme(self) -> str:
514    return self._resource_data.get('loadBalancingScheme', None)
515
516  @property
517  def target(self) -> str:
518    full_path = self._resource_data.get('target', '')
519    result = re.match(r'https://www.googleapis.com/compute/v1/(.*)', full_path)
520    if result:
521      return result.group(1)
522    else:
523      return ''
524
525  @property
526  def backend_service(self) -> str:
527    full_path = self._resource_data.get('backendService', '')
528    result = re.match(r'https://www.googleapis.com/compute/v1/(.*)', full_path)
529    if result:
530      return result.group(1)
531    else:
532      return ''
533
534  @property
535  def ip_address(self) -> str:
536    return self._resource_data.get('IPAddress', '')
537
538  @property
539  def port_range(self) -> str:
540    return self._resource_data.get('portRange', '')
541
542  @caching.cached_api_call(in_memory=True)
543  def get_related_backend_services(self) -> List[BackendServices]:
544    """Returns the backend services related to the forwarding rule."""
545    if self.backend_service:
546      resource = get_backend_service_by_self_link(self.backend_service)
547      return [resource] if resource else []
548    if self.target:
549      target_proxy_target = get_target_proxy_reference(self.target)
550      if not target_proxy_target:
551        return []
552      target_proxy_target_type = target_proxy_target.split('/')[-2]
553      if target_proxy_target_type == 'backendServices':
554        resource = get_backend_service_by_self_link(target_proxy_target)
555        return [resource] if resource else []
556      elif target_proxy_target_type == 'urlMaps':
557        # Currently it doesn't work for shared-vpc backend services
558        backend_services = get_backend_services(self.project_id)
559        return [
560          backend_service
561          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/(.*)', self.self_link)
484    if result:
485      return result.group(1)
486    else:
487      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
489  @property
490  def short_path(self) -> str:
491    path = self.project_id + '/' + self.name
492    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
494  @property
495  def region(self):
496    url = self._resource_data.get('region', '')
497    if url is not None:
498      match = re.search(r'/([^/]+)/?$', url)
499      if match is not None:
500        region = match.group(1)
501        return region
502    return 'global'
global_access_allowed: bool
508  @property
509  def global_access_allowed(self) -> bool:
510    return self._resource_data.get('allowGlobalAccess', False)
load_balancing_scheme: str
512  @property
513  def load_balancing_scheme(self) -> str:
514    return self._resource_data.get('loadBalancingScheme', None)
target: str
516  @property
517  def target(self) -> str:
518    full_path = self._resource_data.get('target', '')
519    result = re.match(r'https://www.googleapis.com/compute/v1/(.*)', full_path)
520    if result:
521      return result.group(1)
522    else:
523      return ''
backend_service: str
525  @property
526  def backend_service(self) -> str:
527    full_path = self._resource_data.get('backendService', '')
528    result = re.match(r'https://www.googleapis.com/compute/v1/(.*)', full_path)
529    if result:
530      return result.group(1)
531    else:
532      return ''
ip_address: str
534  @property
535  def ip_address(self) -> str:
536    return self._resource_data.get('IPAddress', '')
port_range: str
538  @property
539  def port_range(self) -> str:
540    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      )
613    else:
614      request = compute.regionTargetHttpsProxies().get(
615        project=project_id,
616        region=target_proxy_scope,
617        targetHttpsProxy=target_proxy_name,
618      )
619  elif target_proxy_type == 'targetHttpProxies':
620    if target_proxy_scope == 'global':
621      request = compute.targetHttpProxies().get(
622        project=project_id, targetHttpProxy=target_proxy_name
623      )
624    else:
625      request = compute.regionTargetHttpProxies().get(
626        project=project_id,
627        region=target_proxy_scope,
628        targetHttpProxy=target_proxy_name,
629      )
630  elif target_proxy_type == 'targetTcpProxies':
631    if target_proxy_scope == 'global':
632      request = compute.targetTcpProxies().get(project=project_id, targetTcpProxy=target_proxy_name)
633    else:
634      request = compute.regionTargetTcpProxies().get(
635        project=project_id,
636        region=target_proxy_scope,
637        targetTcpProxy=target_proxy_name,
638      )
639  elif target_proxy_type == 'targetSslProxies':
640    request = compute.targetSslProxies().get(project=project_id, targetSslProxy=target_proxy_name)
641  elif target_proxy_type == 'targetGrcpProxies':
642    request = compute.targetGrpcProxies().get(project=project_id, targetGrpcProxy=target_proxy_name)
643  if not request:
644    # target is not target proxy
645    return ''
646  response = request.execute(num_retries=config.API_RETRIES)
647  if 'urlMap' in response:
648    return normalize_url(response['urlMap'])
649  if 'service' in response:
650    return normalize_url(response['service'])
651  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]:
654@caching.cached_api_call(in_memory=True)
655def get_forwarding_rules(project_id: str) -> List[ForwardingRules]:
656  logging.debug('fetching Forwarding Rules: %s', project_id)
657  compute = apis.get_api('compute', 'v1', project_id)
658  forwarding_rules = []
659  request = compute.forwardingRules().aggregatedList(project=project_id)
660  response = request.execute(num_retries=config.API_RETRIES)
661  forwarding_rules_by_region = response['items']
662  for _, data_ in forwarding_rules_by_region.items():
663    if 'forwardingRules' not in data_:
664      continue
665    forwarding_rules.extend(
666      [ForwardingRules(project_id, forwarding_rule) for forwarding_rule in data_['forwardingRules']]
667    )
668  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:
671@caching.cached_api_call(in_memory=True)
672def get_forwarding_rule(
673  project_id: str, forwarding_rule_name: str, region: str = None
674) -> ForwardingRules:
675  compute = apis.get_api('compute', 'v1', project_id)
676  if not region or region == 'global':
677    request = compute.globalForwardingRules().get(
678      project=project_id, forwardingRule=forwarding_rule_name
679    )
680  else:
681    request = compute.forwardingRules().get(
682      project=project_id, region=region, forwardingRule=forwarding_rule_name
683    )
684  response = request.execute(num_retries=config.API_RETRIES)
685  return ForwardingRules(project_id, resource_data=response)
class TargetHttpsProxy(gcpdiag.models.Resource):
688class TargetHttpsProxy(models.Resource):
689  """A Target HTTPS Proxy resource."""
690
691  _resource_data: dict
692  _type: str
693
694  def __init__(self, project_id, resource_data):
695    super().__init__(project_id=project_id)
696    self._resource_data = resource_data
697
698  @property
699  def name(self) -> str:
700    return self._resource_data['name']
701
702  @property
703  def id(self) -> str:
704    return self._resource_data['id']
705
706  @property
707  def full_path(self) -> str:
708    result = re.match(r'https://www.googleapis.com/compute/v1/(.*)', self.self_link)
709    if result:
710      return result.group(1)
711    else:
712      return f'>> {self.self_link}'
713
714  @property
715  def self_link(self) -> str:
716    return self._resource_data['selfLink']
717
718  @property
719  def region(self):
720    url = self._resource_data.get('region', '')
721    if url is not None:
722      match = re.search(r'/([^/]+)/?$', url)
723      if match is not None:
724        region = match.group(1)
725        return region
726    return 'global'
727
728  @property
729  def ssl_certificates(self) -> List[str]:
730    return self._resource_data.get('sslCertificates', [])
731
732  @property
733  def certificate_map(self) -> str:
734    certificate_map = self._resource_data.get('certificateMap', '')
735    result = re.match(r'https://certificatemanager.googleapis.com/v1/(.*)', certificate_map)
736    if result:
737      return result.group(1)
738    return certificate_map

A Target HTTPS Proxy resource.

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

A Target SSL Proxy resource.

TargetSslProxy(project_id, resource_data)
768  def __init__(self, project_id, resource_data):
769    super().__init__(project_id=project_id)
770    self._resource_data = resource_data
name: str
772  @property
773  def name(self) -> str:
774    return self._resource_data['name']
id: str
776  @property
777  def id(self) -> str:
778    return self._resource_data['id']
full_path: str
780  @property
781  def full_path(self) -> str:
782    result = re.match(r'https://www.googleapis.com/compute/v1/(.*)', self.self_link)
783    if result:
784      return result.group(1)
785    else:
786      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
792  @property
793  def region(self):
794    url = self._resource_data.get('region', '')
795    if url is not None:
796      match = re.search(r'/([^/]+)/?$', url)
797      if match is not None:
798        region = match.group(1)
799        return region
800    return 'global'
ssl_certificates: List[str]
802  @property
803  def ssl_certificates(self) -> List[str]:
804    return self._resource_data.get('sslCertificates', [])
certificate_map: str
806  @property
807  def certificate_map(self) -> str:
808    certificate_map = self._resource_data.get('certificateMap', '')
809    result = re.match(r'https://certificatemanager.googleapis.com/v1/(.*)', certificate_map)
810    if result:
811      return result.group(1)
812    return certificate_map
@caching.cached_api_call(in_memory=True)
def get_target_ssl_proxies(project_id: str) -> List[TargetSslProxy]:
815@caching.cached_api_call(in_memory=True)
816def get_target_ssl_proxies(project_id: str) -> List[TargetSslProxy]:
817  logging.debug('fetching Target SSL Proxies: %s', project_id)
818  compute = apis.get_api('compute', 'v1', project_id)
819  request = compute.targetSslProxies().list(project=project_id)
820  response = request.execute(num_retries=config.API_RETRIES)
821
822  return [TargetSslProxy(project_id, item) for item in response.get('items', [])]
class LoadBalancerInsight(gcpdiag.models.Resource):
825class LoadBalancerInsight(models.Resource):
826  """Represents a Load Balancer Insights object"""
827
828  @property
829  def full_path(self) -> str:
830    return self._resource_data['name']
831
832  @property
833  def description(self) -> str:
834    return self._resource_data['description']
835
836  @property
837  def insight_subtype(self) -> str:
838    return self._resource_data['insightSubtype']
839
840  @property
841  def details(self) -> dict:
842    return self._resource_data['content']
843
844  @property
845  def is_firewall_rule_insight(self) -> bool:
846    firewall_rule_subtypes = (
847      'HEALTH_CHECK_FIREWALL_NOT_CONFIGURED',
848      'HEALTH_CHECK_FIREWALL_FULLY_BLOCKING',
849      'HEALTH_CHECK_FIREWALL_PARTIALLY_BLOCKING',
850      'HEALTH_CHECK_FIREWALL_INCONSISTENT',
851    )
852    return self.insight_subtype.startswith(firewall_rule_subtypes)
853
854  @property
855  def is_health_check_port_mismatch_insight(self) -> bool:
856    return self.insight_subtype == 'HEALTH_CHECK_PORT_MISMATCH'
857
858  def __init__(self, project_id, resource_data):
859    super().__init__(project_id=project_id)
860    self._resource_data = resource_data

Represents a Load Balancer Insights object

LoadBalancerInsight(project_id, resource_data)
858  def __init__(self, project_id, resource_data):
859    super().__init__(project_id=project_id)
860    self._resource_data = resource_data
full_path: str
828  @property
829  def full_path(self) -> str:
830    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
832  @property
833  def description(self) -> str:
834    return self._resource_data['description']
insight_subtype: str
836  @property
837  def insight_subtype(self) -> str:
838    return self._resource_data['insightSubtype']
details: dict
840  @property
841  def details(self) -> dict:
842    return self._resource_data['content']
is_firewall_rule_insight: bool
844  @property
845  def is_firewall_rule_insight(self) -> bool:
846    firewall_rule_subtypes = (
847      'HEALTH_CHECK_FIREWALL_NOT_CONFIGURED',
848      'HEALTH_CHECK_FIREWALL_FULLY_BLOCKING',
849      'HEALTH_CHECK_FIREWALL_PARTIALLY_BLOCKING',
850      'HEALTH_CHECK_FIREWALL_INCONSISTENT',
851    )
852    return self.insight_subtype.startswith(firewall_rule_subtypes)
is_health_check_port_mismatch_insight: bool
854  @property
855  def is_health_check_port_mismatch_insight(self) -> bool:
856    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'):
863@caching.cached_api_call
864def get_lb_insights_for_a_project(project_id: str, region: str = 'global'):
865  api = apis.get_api('recommender', 'v1', project_id)
866
867  insight_name = (
868    f'projects/{project_id}/locations/{region}/insightTypes/'
869    'google.networkanalyzer.networkservices.loadBalancerInsight'
870  )
871  insights = []
872  for insight in apis_utils.list_all(
873    request=api.projects().locations().insightTypes().insights().list(parent=insight_name),
874    next_function=api.projects().locations().insightTypes().insights().list_next,
875    response_keyword='insights',
876  ):
877    insights.append(LoadBalancerInsight(project_id, insight))
878  return insights