gcpdiag.queries.network
34class Subnetwork(models.Resource): 35 """A VPC subnetwork.""" 36 37 _resource_data: dict 38 _context: models.Context 39 40 def __init__(self, project_id, resource_data, context: models.Context): 41 super().__init__(project_id=project_id) 42 self._resource_data = resource_data 43 self._context = context 44 45 @property 46 def full_path(self) -> str: 47 result = re.match(r'https://www.googleapis.com/compute/v1/(.*)', self.self_link) 48 if result: 49 return result.group(1) 50 else: 51 return f'>> {self.self_link}' 52 53 @property 54 def short_path(self) -> str: 55 path = self.project_id + '/' + self.name 56 return path 57 58 @property 59 def name(self) -> str: 60 return self._resource_data['name'] 61 62 @property 63 def self_link(self) -> str: 64 return self._resource_data.get('selfLink', '') 65 66 @property 67 def ip_network(self) -> IPv4NetOrIPv6Net: 68 return ipaddress.ip_network(self._resource_data['ipCidrRange']) 69 70 @property 71 def region(self) -> str: 72 # https://www.googleapis.com/compute/v1/projects/gcpdiag-gke1-aaaa/regions/europe-west4 73 m = re.match( 74 r'https://www.googleapis.com/compute/v1/projects/([^/]+)/regions/([^/]+)', 75 self._resource_data['region'], 76 ) 77 if not m: 78 raise RuntimeError(f"can't parse region URL: {self._resource_data['region']}") 79 return m.group(2) 80 81 def is_private_ip_google_access(self) -> bool: 82 return self._resource_data.get('privateIpGoogleAccess', False) 83 84 @property 85 def network(self): 86 return self._resource_data['network']
A VPC subnetwork.
45 @property 46 def full_path(self) -> str: 47 result = re.match(r'https://www.googleapis.com/compute/v1/(.*)', self.self_link) 48 if result: 49 return result.group(1) 50 else: 51 return f'>> {self.self_link}'
Returns the full path of this resource.
Example: 'projects/gcpdiag-gke-1-9b90/zones/europe-west4-a/clusters/gke1'
53 @property 54 def short_path(self) -> str: 55 path = self.project_id + '/' + self.name 56 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'
70 @property 71 def region(self) -> str: 72 # https://www.googleapis.com/compute/v1/projects/gcpdiag-gke1-aaaa/regions/europe-west4 73 m = re.match( 74 r'https://www.googleapis.com/compute/v1/projects/([^/]+)/regions/([^/]+)', 75 self._resource_data['region'], 76 ) 77 if not m: 78 raise RuntimeError(f"can't parse region URL: {self._resource_data['region']}") 79 return m.group(2)
89class Route(models.Resource): 90 """A VPC Route.""" 91 92 _resource_data: dict 93 94 def __init__(self, project_id, resource_data): 95 super().__init__(project_id=project_id) 96 self._resource_data = resource_data 97 98 @property 99 def full_path(self) -> str: 100 result = re.match(r'https://www.googleapis.com/compute/v1/(.*)', self.self_link) 101 if result: 102 return result.group(1) 103 else: 104 return f'>> {self.self_link}' 105 106 @property 107 def short_path(self) -> str: 108 path = self.project_id + '/' + self.name 109 return path 110 111 @property 112 def name(self) -> str: 113 return self._resource_data['name'] 114 115 @property 116 def kind(self) -> str: 117 return self._resource_data['kind'] 118 119 @property 120 def self_link(self) -> str: 121 return self._resource_data['selfLink'] 122 123 @property 124 def network(self) -> str: 125 return self._resource_data['network'] 126 127 @property 128 def tags(self) -> List[str]: 129 if 'tags' in self._resource_data: 130 return self._resource_data['tags'] 131 return [] 132 133 @property 134 def dest_range(self) -> str: 135 return self._resource_data['destRange'] 136 137 @property 138 def next_hop_gateway(self) -> Optional[str]: 139 if 'nextHopGateway' in self._resource_data: 140 return self._resource_data['nextHopGateway'] 141 return None 142 143 @property 144 def next_hop_vpn_tunnel(self) -> Optional[str]: 145 return self._resource_data.get('nextHopVpnTunnel') 146 147 @property 148 def next_hop_hub(self) -> Optional[str]: 149 return self._resource_data.get('nextHopHub') 150 151 @property 152 def priority(self) -> int: 153 return self._resource_data['priority'] 154 155 def get_next_hop(self) -> Union[Dict[str, Any], Optional[str]]: 156 hop_types = { 157 'nextHopGateway': 'nextHopGateway', 158 'nextHopVpnTunnel': 'nextHopVpnTunnel', 159 'nextHopHub': 'nextHopHub', 160 'nextHopInstance': 'nextHopInstance', 161 'nextHopAddress': 'nextHopAddress', 162 'nextHopPeering': 'nextHopPeering', 163 'nextHopIlb': 'nextHopIlb', 164 'nextHopNetwork': 'nextHopNetwork', 165 'nextHopIp': 'nextHopIp', 166 } 167 168 for hop_type, value in hop_types.items(): 169 if self._resource_data.get(hop_type): 170 return {'type': value, 'link': self._resource_data[hop_type]} 171 return None 172 173 def check_route_match(self, ip1: IPAddrOrNet, ip2: str) -> bool: 174 ip2_list = [ipaddress.ip_network(ip2)] 175 if _ip_match(ip1, ip2_list, 'allow'): 176 return True 177 return False
A VPC Route.
98 @property 99 def full_path(self) -> str: 100 result = re.match(r'https://www.googleapis.com/compute/v1/(.*)', self.self_link) 101 if result: 102 return result.group(1) 103 else: 104 return f'>> {self.self_link}'
Returns the full path of this resource.
Example: 'projects/gcpdiag-gke-1-9b90/zones/europe-west4-a/clusters/gke1'
106 @property 107 def short_path(self) -> str: 108 path = self.project_id + '/' + self.name 109 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'
155 def get_next_hop(self) -> Union[Dict[str, Any], Optional[str]]: 156 hop_types = { 157 'nextHopGateway': 'nextHopGateway', 158 'nextHopVpnTunnel': 'nextHopVpnTunnel', 159 'nextHopHub': 'nextHopHub', 160 'nextHopInstance': 'nextHopInstance', 161 'nextHopAddress': 'nextHopAddress', 162 'nextHopPeering': 'nextHopPeering', 163 'nextHopIlb': 'nextHopIlb', 164 'nextHopNetwork': 'nextHopNetwork', 165 'nextHopIp': 'nextHopIp', 166 } 167 168 for hop_type, value in hop_types.items(): 169 if self._resource_data.get(hop_type): 170 return {'type': value, 'link': self._resource_data[hop_type]} 171 return None
180class ManagedZone(models.Resource): 181 """ 182 Represent a DNS zone (public or private 183 184 https://cloud.google.com/dns/docs/reference/v1beta2/managedZones 185 """ 186 187 _resource_data: dict 188 189 def __init__(self, project_id, resource_data): 190 super().__init__(project_id=project_id) 191 self._resource_data = resource_data 192 193 @property 194 def cloud_logging_config(self) -> bool: 195 return self._resource_data['cloudLoggingConfig'].get('enableLogging', False) 196 197 @property 198 def is_public(self) -> bool: 199 return self._resource_data['visibility'] == 'public' 200 201 @property 202 def vpc_attached(self) -> bool: 203 if 'privateVisibilityConfig' not in self._resource_data: 204 self._resource_data['privateVisibilityConfig'] = {} 205 206 return self._resource_data['privateVisibilityConfig'].get( 207 'networks', False 208 ) or self._resource_data['privateVisibilityConfig'].get('gkeClusters', False) 209 210 @property 211 def dnssec_config_state(self) -> bool: 212 if 'dnssecConfig' not in self._resource_data: 213 self._resource_data['dnssecConfig'] = {} 214 215 return self._resource_data['dnssecConfig'].get('state', False) 216 217 @property 218 def name(self) -> str: 219 return self._resource_data['name'] 220 221 @property 222 def full_path(self) -> str: 223 result = re.match(r'https://dns.googleapis.com/dns/v1beta2/(.*)', self.self_link) 224 if result: 225 return result.group(1) 226 else: 227 return f'>> {self.self_link}' 228 229 @property 230 def short_path(self) -> str: 231 path = self.project_id + '/' + self.name 232 return path 233 234 @property 235 def self_link(self) -> str: 236 return self._resource_data.get('selfLink', '')
Represent a DNS zone (public or private
https://cloud.google.com/dns/docs/reference/v1beta2/managedZones
201 @property 202 def vpc_attached(self) -> bool: 203 if 'privateVisibilityConfig' not in self._resource_data: 204 self._resource_data['privateVisibilityConfig'] = {} 205 206 return self._resource_data['privateVisibilityConfig'].get( 207 'networks', False 208 ) or self._resource_data['privateVisibilityConfig'].get('gkeClusters', False)
221 @property 222 def full_path(self) -> str: 223 result = re.match(r'https://dns.googleapis.com/dns/v1beta2/(.*)', self.self_link) 224 if result: 225 return result.group(1) 226 else: 227 return f'>> {self.self_link}'
Returns the full path of this resource.
Example: 'projects/gcpdiag-gke-1-9b90/zones/europe-west4-a/clusters/gke1'
239class Router(models.Resource): 240 """A VPC Router.""" 241 242 _resource_data: dict 243 244 def __init__(self, project_id, resource_data): 245 super().__init__(project_id=project_id) 246 self._resource_data = resource_data 247 self._nats = None 248 249 @property 250 def full_path(self) -> str: 251 result = re.match(r'https://www.googleapis.com/compute/v1/(.*)', self.self_link) 252 if result: 253 return result.group(1) 254 else: 255 return f'>> {self.self_link}' 256 257 @property 258 def short_path(self) -> str: 259 path = self.project_id + '/' + self.name 260 return path 261 262 @property 263 def name(self) -> str: 264 return self._resource_data['name'] 265 266 @property 267 def self_link(self) -> str: 268 return self._resource_data.get('selfLink', '') 269 270 @property 271 def network(self) -> str: 272 return self._resource_data['network'] 273 274 def get_network_name(self) -> str: 275 logging.info('inside get_network_name function') 276 if self._resource_data['network']: 277 return self._resource_data['network'].split('/')[-1] 278 return '' 279 280 @property 281 def nats(self): 282 return self._resource_data.get('nats', []) 283 284 def get_nat_ip_allocate_option(self, nat_gateway) -> str: 285 nats = self._resource_data.get('nats', []) 286 nat = [n for n in nats if n['name'] == nat_gateway] 287 return nat[0].get('natIpAllocateOption', '') 288 289 def get_enable_dynamic_port_allocation(self, nat_gateway) -> str: 290 nats = self._resource_data.get('nats', []) 291 nat = [n for n in nats if n['name'] == nat_gateway] 292 return nat[0].get('enableDynamicPortAllocation', '') 293 294 def subnet_has_nat(self, subnetwork): 295 if not self._resource_data.get('nats', []): 296 return False 297 for n in self._resource_data.get('nats', []): 298 if n['sourceSubnetworkIpRangesToNat'] == 'LIST_OF_SUBNETWORKS': 299 # Cloud NAT configure for specific subnets 300 if 'subnetworks' in n and subnetwork.self_link in [s['name'] for s in n['subnetworks']]: 301 return True 302 else: 303 # Cloud NAT configured for all subnets 304 return True 305 return False
A VPC Router.
249 @property 250 def full_path(self) -> str: 251 result = re.match(r'https://www.googleapis.com/compute/v1/(.*)', self.self_link) 252 if result: 253 return result.group(1) 254 else: 255 return f'>> {self.self_link}'
Returns the full path of this resource.
Example: 'projects/gcpdiag-gke-1-9b90/zones/europe-west4-a/clusters/gke1'
257 @property 258 def short_path(self) -> str: 259 path = self.project_id + '/' + self.name 260 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'
294 def subnet_has_nat(self, subnetwork): 295 if not self._resource_data.get('nats', []): 296 return False 297 for n in self._resource_data.get('nats', []): 298 if n['sourceSubnetworkIpRangesToNat'] == 'LIST_OF_SUBNETWORKS': 299 # Cloud NAT configure for specific subnets 300 if 'subnetworks' in n and subnetwork.self_link in [s['name'] for s in n['subnetworks']]: 301 return True 302 else: 303 # Cloud NAT configured for all subnets 304 return True 305 return False
308class RouterStatus(models.Resource): 309 """NAT Router Status""" 310 311 _resource_data: dict 312 313 def __init__(self, project_id, resource_data): 314 super().__init__(project_id=project_id) 315 self._resource_data = resource_data 316 317 @property 318 def full_path(self) -> str: 319 result = re.match(r'https://www.googleapis.com/compute/v1/(.*)', self.self_link) 320 if result: 321 return result.group(1) 322 else: 323 return f'>> {self.self_link}' 324 325 @property 326 def short_path(self) -> str: 327 path = self.project_id + '/' + self.name 328 return path 329 330 @property 331 def name(self) -> str: 332 return self._resource_data.get('name', '') 333 334 @property 335 def self_link(self) -> str: 336 return self._resource_data.get('selfLink', '') 337 338 @property 339 def min_extra_nat_ips_needed(self) -> str: 340 nat_status = self._resource_data.get('result', {}).get('natStatus', {}) 341 return nat_status[0].get('minExtraNatIpsNeeded', None) 342 343 @property 344 def num_vms_with_nat_mappings(self) -> str: 345 nat_status = self._resource_data.get('result', {}).get('natStatus', {}) 346 return nat_status[0].get('numVmEndpointsWithNatMappings', None) 347 348 @property 349 def bgp_peer_status(self) -> str: 350 bgp_peer_status = self._resource_data.get('result', {}).get('bgpPeerStatus', {}) 351 return bgp_peer_status
NAT Router Status
317 @property 318 def full_path(self) -> str: 319 result = re.match(r'https://www.googleapis.com/compute/v1/(.*)', self.self_link) 320 if result: 321 return result.group(1) 322 else: 323 return f'>> {self.self_link}'
Returns the full path of this resource.
Example: 'projects/gcpdiag-gke-1-9b90/zones/europe-west4-a/clusters/gke1'
325 @property 326 def short_path(self) -> str: 327 path = self.project_id + '/' + self.name 328 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'
354class RouterNatIpInfo(models.Resource): 355 """NAT IP Info""" 356 357 _resource_data: dict 358 359 def __init__(self, project_id, resource_data): 360 super().__init__(project_id=project_id) 361 self._resource_data = resource_data 362 363 @property 364 def full_path(self) -> str: 365 result = re.match(r'https://www.googleapis.com/compute/v1/(.*)', self.self_link) 366 if result: 367 return result.group(1) 368 else: 369 return f'>> {self.self_link}' 370 371 @property 372 def short_path(self) -> str: 373 path = self.project_id + '/' + self.name 374 return path 375 376 @property 377 def self_link(self) -> str: 378 return self._resource_data.get('selfLink', '') 379 380 @property 381 def name(self) -> str: 382 return self._resource_data.get('name', '') 383 384 @property 385 def result(self) -> str: 386 return self._resource_data.get('result', [])
NAT IP Info
363 @property 364 def full_path(self) -> str: 365 result = re.match(r'https://www.googleapis.com/compute/v1/(.*)', self.self_link) 366 if result: 367 return result.group(1) 368 else: 369 return f'>> {self.self_link}'
Returns the full path of this resource.
Example: 'projects/gcpdiag-gke-1-9b90/zones/europe-west4-a/clusters/gke1'
371 @property 372 def short_path(self) -> str: 373 path = self.project_id + '/' + self.name 374 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'
389@dataclasses.dataclass 390class Peering: 391 """VPC Peerings""" 392 393 name: str 394 url: str 395 state: str 396 exports_custom_routes: bool 397 imports_custom_routes: bool 398 auto_creates_routes: bool 399 400 def __str__(self): 401 return self.name
VPC Peerings
404class Network(models.Resource): 405 """A VPC network.""" 406 407 _resource_data: dict 408 _subnetworks: Optional[Dict[str, Subnetwork]] 409 _context: models.Context 410 411 def __init__(self, project_id, resource_data, context: models.Context): 412 super().__init__(project_id=project_id) 413 self._resource_data = resource_data 414 self._subnetworks = None 415 self._context = context 416 417 @property 418 def full_path(self) -> str: 419 result = re.match(r'https://www.googleapis.com/compute/v1/(.*)', self.self_link) 420 if result: 421 return result.group(1) 422 else: 423 return f'>> {self.self_link}' 424 425 @property 426 def short_path(self) -> str: 427 path = self.project_id + '/' + self.name 428 return path 429 430 @property 431 def name(self) -> str: 432 return self._resource_data['name'] 433 434 @property 435 def self_link(self) -> str: 436 return self._resource_data.get('selfLink', '') 437 438 @property 439 def firewall(self) -> 'EffectiveFirewalls': 440 return _get_effective_firewalls(self) 441 442 @property 443 def mtu(self) -> int: 444 if 'mtu' in self._resource_data: 445 return self._resource_data['mtu'] 446 return DEFAULT_MTU 447 448 @property 449 def subnetworks(self) -> Dict[str, Subnetwork]: 450 return _batch_get_subnetworks( 451 self._project_id, frozenset(self._resource_data.get('subnetworks', [])), self._context 452 ) 453 454 @property 455 def peerings(self) -> List[Peering]: 456 return [ 457 Peering( 458 peer['name'], 459 peer['network'], 460 peer['state'], 461 peer['exportCustomRoutes'], 462 peer['importCustomRoutes'], 463 peer['autoCreateRoutes'], 464 ) 465 for peer in self._resource_data.get('peerings', []) 466 ]
A VPC network.
417 @property 418 def full_path(self) -> str: 419 result = re.match(r'https://www.googleapis.com/compute/v1/(.*)', self.self_link) 420 if result: 421 return result.group(1) 422 else: 423 return f'>> {self.self_link}'
Returns the full path of this resource.
Example: 'projects/gcpdiag-gke-1-9b90/zones/europe-west4-a/clusters/gke1'
425 @property 426 def short_path(self) -> str: 427 path = self.project_id + '/' + self.name 428 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'
454 @property 455 def peerings(self) -> List[Peering]: 456 return [ 457 Peering( 458 peer['name'], 459 peer['network'], 460 peer['state'], 461 peer['exportCustomRoutes'], 462 peer['importCustomRoutes'], 463 peer['autoCreateRoutes'], 464 ) 465 for peer in self._resource_data.get('peerings', []) 466 ]
573@dataclasses.dataclass 574class FirewallCheckResult: 575 """The result of a firewall connectivity check.""" 576 577 action: str 578 firewall_policy_name: Optional[str] = None 579 firewall_policy_rule_description: Optional[str] = None 580 vpc_firewall_rule_id: Optional[str] = None 581 vpc_firewall_rule_name: Optional[str] = None 582 583 def __str__(self): 584 return self.action 585 586 @property 587 def matched_by_str(self): 588 if self.firewall_policy_name: 589 if self.firewall_policy_rule_description: 590 return f'policy: {self.firewall_policy_name}, rule: {self.firewall_policy_rule_description}' 591 else: 592 return f'policy: {self.firewall_policy_name}' 593 elif self.vpc_firewall_rule_name: 594 return f'vpc firewall rule: {self.vpc_firewall_rule_name}'
The result of a firewall connectivity check.
586 @property 587 def matched_by_str(self): 588 if self.firewall_policy_name: 589 if self.firewall_policy_rule_description: 590 return f'policy: {self.firewall_policy_name}, rule: {self.firewall_policy_rule_description}' 591 else: 592 return f'policy: {self.firewall_policy_name}' 593 elif self.vpc_firewall_rule_name: 594 return f'vpc firewall rule: {self.vpc_firewall_rule_name}'
597class FirewallRuleNotFoundError(Exception): 598 rule_name: str 599 600 def __init__(self, name, disabled=False): 601 # Call the base class constructor with the parameters it needs 602 super().__init__(f'firewall rule not found: {name}') 603 self.rule_name = name 604 self.disabled = disabled
Common base class for all non-exit exceptions.
607class VpcFirewallRule: 608 """Represents firewall rule""" 609 610 def __init__(self, resource_data): 611 self._resource_data = resource_data 612 613 @property 614 def name(self) -> str: 615 return self._resource_data['name'] 616 617 @property 618 def source_ranges(self) -> List[ipaddress.IPv4Network]: 619 return self._resource_data['sourceRanges'] 620 621 @property 622 def target_tags(self) -> set: 623 return self._resource_data['targetTags'] 624 625 @property 626 def allowed(self) -> List[dict]: 627 return self._resource_data['allowed'] 628 629 def is_enabled(self) -> bool: 630 return not self._resource_data['disabled']
Represents firewall rule
993class EffectiveFirewalls: 994 """Effective firewall rules for a VPC network or Instance. 995 996 Includes org/folder firewall policies).""" 997 998 _resource_data: dict 999 _policies: List[_FirewallPolicy] 1000 _vpc_firewall: _VpcFirewall 1001 1002 def __init__(self, resource_data): 1003 self._resource_data = resource_data 1004 self._policies = [] 1005 if 'firewallPolicys' in resource_data: 1006 for policy in resource_data['firewallPolicys']: 1007 self._policies.append(_FirewallPolicy(policy)) 1008 self._vpc_firewall = _VpcFirewall(resource_data.get('firewalls', {})) 1009 1010 def check_connectivity_ingress( 1011 self, # 1012 *, 1013 src_ip: IPAddrOrNet, 1014 ip_protocol: str, 1015 port: Optional[int] = None, 1016 source_service_account: Optional[str] = None, 1017 source_tags: Optional[List[str]] = None, 1018 target_service_account: Optional[str] = None, 1019 target_tags: Optional[List[str]] = None, 1020 ) -> FirewallCheckResult: 1021 if ip_protocol != 'ICMP' and port is None: 1022 raise ValueError('TCP and UDP must have port numbers') 1023 1024 # Firewall policies (organization, folders) 1025 for p in self._policies: 1026 result = p.check_connectivity_ingress( 1027 src_ip=src_ip, 1028 ip_protocol=ip_protocol, 1029 port=port, 1030 # target_network=self._network, 1031 target_service_account=target_service_account, 1032 ) 1033 if result.action != 'goto_next': 1034 return result 1035 1036 # VPC firewall rules 1037 return self._vpc_firewall.check_connectivity_ingress( 1038 src_ip=src_ip, 1039 ip_protocol=ip_protocol, 1040 port=port, 1041 source_service_account=source_service_account, 1042 source_tags=source_tags, 1043 target_service_account=target_service_account, 1044 target_tags=target_tags, 1045 ) 1046 1047 def check_connectivity_egress( 1048 self, # 1049 *, 1050 src_ip: IPAddrOrNet, 1051 ip_protocol: str, 1052 port: Optional[int] = None, 1053 source_service_account: Optional[str] = None, 1054 source_tags: Optional[List[str]] = None, 1055 target_service_account: Optional[str] = None, 1056 target_tags: Optional[List[str]] = None, 1057 ) -> FirewallCheckResult: 1058 if ip_protocol != 'ICMP' and port is None: 1059 raise ValueError('TCP and UDP must have port numbers') 1060 1061 # Firewall policies (organization, folders) 1062 for p in self._policies: 1063 result = p.check_connectivity_egress( 1064 src_ip=src_ip, 1065 ip_protocol=ip_protocol, 1066 port=port, 1067 # target_network=self._network, 1068 target_service_account=target_service_account, 1069 ) 1070 if result.action != 'goto_next': 1071 return result 1072 1073 # VPC firewall rules 1074 return self._vpc_firewall.check_connectivity_egress( 1075 src_ip=src_ip, 1076 ip_protocol=ip_protocol, 1077 port=port, 1078 source_service_account=source_service_account, 1079 source_tags=source_tags, 1080 target_service_account=target_service_account, 1081 target_tags=target_tags, 1082 ) 1083 1084 def get_vpc_ingress_rules( 1085 self, 1086 name: Optional[str] = None, 1087 name_pattern: Optional[re.Pattern] = None, 1088 target_tags: Optional[List[str]] = None, 1089 ) -> List[VpcFirewallRule]: 1090 """Retrieve the list of ingress firewall rules matching name or name pattern and target tags. 1091 1092 Args: 1093 name (Optional[str], optional): firewall rune name. Defaults to None. 1094 name_pattern (Optional[re.Pattern], optional): firewall rule name pattern. Defaults to None. 1095 target_tags (Optional[List[str]], optional): firewall target tags 1096 (if not specified any tag will match). Defaults to None. 1097 1098 Returns: 1099 List[VpcFirewallRule]: List of ingress firewall rules 1100 """ 1101 rules = self._vpc_firewall.get_vpc_ingress_rules(name, name_pattern, target_tags) 1102 return rules 1103 1104 def get_vpc_egress_rules( 1105 self, 1106 name: Optional[str] = None, 1107 name_pattern: Optional[re.Pattern] = None, 1108 target_tags: Optional[List[str]] = None, 1109 ) -> List[VpcFirewallRule]: 1110 """Retrieve the list of egress firewall rules matching name or name pattern and target tags. 1111 1112 Args: 1113 name (Optional[str], optional): firewall rune name. Defaults to None. 1114 name_pattern (Optional[re.Pattern], optional): firewall rule name pattern. Defaults to None. 1115 target_tags (Optional[List[str]], optional): firewall target tags 1116 (if not specified any tag will match). Defaults to None. 1117 1118 Returns: 1119 List[VpcFirewallRule]: List of egress firewall rules 1120 """ 1121 rules = self._vpc_firewall.get_vpc_egress_rules(name, name_pattern, target_tags) 1122 return rules 1123 1124 def verify_ingress_rule_exists(self, name: str): 1125 """Verify that a certain VPC rule exists. This is useful to verify 1126 whether maybe a permission was missing on a shared VPC and an 1127 automatic rule couldn't be created.""" 1128 return self._vpc_firewall.verify_ingress_rule_exists(name) 1129 1130 def verify_egress_rule_exists(self, name: str): 1131 """Verify that a certain VPC rule exists. This is useful to verify 1132 whether maybe a permission was missing on a shared VPC and an 1133 automatic rule couldn't be created.""" 1134 return self._vpc_firewall.verify_egress_rule_exists(name)
Effective firewall rules for a VPC network or Instance.
Includes org/folder firewall policies).
1002 def __init__(self, resource_data): 1003 self._resource_data = resource_data 1004 self._policies = [] 1005 if 'firewallPolicys' in resource_data: 1006 for policy in resource_data['firewallPolicys']: 1007 self._policies.append(_FirewallPolicy(policy)) 1008 self._vpc_firewall = _VpcFirewall(resource_data.get('firewalls', {}))
1010 def check_connectivity_ingress( 1011 self, # 1012 *, 1013 src_ip: IPAddrOrNet, 1014 ip_protocol: str, 1015 port: Optional[int] = None, 1016 source_service_account: Optional[str] = None, 1017 source_tags: Optional[List[str]] = None, 1018 target_service_account: Optional[str] = None, 1019 target_tags: Optional[List[str]] = None, 1020 ) -> FirewallCheckResult: 1021 if ip_protocol != 'ICMP' and port is None: 1022 raise ValueError('TCP and UDP must have port numbers') 1023 1024 # Firewall policies (organization, folders) 1025 for p in self._policies: 1026 result = p.check_connectivity_ingress( 1027 src_ip=src_ip, 1028 ip_protocol=ip_protocol, 1029 port=port, 1030 # target_network=self._network, 1031 target_service_account=target_service_account, 1032 ) 1033 if result.action != 'goto_next': 1034 return result 1035 1036 # VPC firewall rules 1037 return self._vpc_firewall.check_connectivity_ingress( 1038 src_ip=src_ip, 1039 ip_protocol=ip_protocol, 1040 port=port, 1041 source_service_account=source_service_account, 1042 source_tags=source_tags, 1043 target_service_account=target_service_account, 1044 target_tags=target_tags, 1045 )
1047 def check_connectivity_egress( 1048 self, # 1049 *, 1050 src_ip: IPAddrOrNet, 1051 ip_protocol: str, 1052 port: Optional[int] = None, 1053 source_service_account: Optional[str] = None, 1054 source_tags: Optional[List[str]] = None, 1055 target_service_account: Optional[str] = None, 1056 target_tags: Optional[List[str]] = None, 1057 ) -> FirewallCheckResult: 1058 if ip_protocol != 'ICMP' and port is None: 1059 raise ValueError('TCP and UDP must have port numbers') 1060 1061 # Firewall policies (organization, folders) 1062 for p in self._policies: 1063 result = p.check_connectivity_egress( 1064 src_ip=src_ip, 1065 ip_protocol=ip_protocol, 1066 port=port, 1067 # target_network=self._network, 1068 target_service_account=target_service_account, 1069 ) 1070 if result.action != 'goto_next': 1071 return result 1072 1073 # VPC firewall rules 1074 return self._vpc_firewall.check_connectivity_egress( 1075 src_ip=src_ip, 1076 ip_protocol=ip_protocol, 1077 port=port, 1078 source_service_account=source_service_account, 1079 source_tags=source_tags, 1080 target_service_account=target_service_account, 1081 target_tags=target_tags, 1082 )
1084 def get_vpc_ingress_rules( 1085 self, 1086 name: Optional[str] = None, 1087 name_pattern: Optional[re.Pattern] = None, 1088 target_tags: Optional[List[str]] = None, 1089 ) -> List[VpcFirewallRule]: 1090 """Retrieve the list of ingress firewall rules matching name or name pattern and target tags. 1091 1092 Args: 1093 name (Optional[str], optional): firewall rune name. Defaults to None. 1094 name_pattern (Optional[re.Pattern], optional): firewall rule name pattern. Defaults to None. 1095 target_tags (Optional[List[str]], optional): firewall target tags 1096 (if not specified any tag will match). Defaults to None. 1097 1098 Returns: 1099 List[VpcFirewallRule]: List of ingress firewall rules 1100 """ 1101 rules = self._vpc_firewall.get_vpc_ingress_rules(name, name_pattern, target_tags) 1102 return rules
Retrieve the list of ingress firewall rules matching name or name pattern and target tags.
Arguments:
- name (Optional[str], optional): firewall rune name. Defaults to None.
- name_pattern (Optional[re.Pattern], optional): firewall rule name pattern. Defaults to None.
- target_tags (Optional[List[str]], optional): firewall target tags (if not specified any tag will match). Defaults to None.
Returns:
List[VpcFirewallRule]: List of ingress firewall rules
1104 def get_vpc_egress_rules( 1105 self, 1106 name: Optional[str] = None, 1107 name_pattern: Optional[re.Pattern] = None, 1108 target_tags: Optional[List[str]] = None, 1109 ) -> List[VpcFirewallRule]: 1110 """Retrieve the list of egress firewall rules matching name or name pattern and target tags. 1111 1112 Args: 1113 name (Optional[str], optional): firewall rune name. Defaults to None. 1114 name_pattern (Optional[re.Pattern], optional): firewall rule name pattern. Defaults to None. 1115 target_tags (Optional[List[str]], optional): firewall target tags 1116 (if not specified any tag will match). Defaults to None. 1117 1118 Returns: 1119 List[VpcFirewallRule]: List of egress firewall rules 1120 """ 1121 rules = self._vpc_firewall.get_vpc_egress_rules(name, name_pattern, target_tags) 1122 return rules
Retrieve the list of egress firewall rules matching name or name pattern and target tags.
Arguments:
- name (Optional[str], optional): firewall rune name. Defaults to None.
- name_pattern (Optional[re.Pattern], optional): firewall rule name pattern. Defaults to None.
- target_tags (Optional[List[str]], optional): firewall target tags (if not specified any tag will match). Defaults to None.
Returns:
List[VpcFirewallRule]: List of egress firewall rules
1124 def verify_ingress_rule_exists(self, name: str): 1125 """Verify that a certain VPC rule exists. This is useful to verify 1126 whether maybe a permission was missing on a shared VPC and an 1127 automatic rule couldn't be created.""" 1128 return self._vpc_firewall.verify_ingress_rule_exists(name)
Verify that a certain VPC rule exists. This is useful to verify whether maybe a permission was missing on a shared VPC and an automatic rule couldn't be created.
1130 def verify_egress_rule_exists(self, name: str): 1131 """Verify that a certain VPC rule exists. This is useful to verify 1132 whether maybe a permission was missing on a shared VPC and an 1133 automatic rule couldn't be created.""" 1134 return self._vpc_firewall.verify_egress_rule_exists(name)
Verify that a certain VPC rule exists. This is useful to verify whether maybe a permission was missing on a shared VPC and an automatic rule couldn't be created.
1137class VPCEffectiveFirewalls(EffectiveFirewalls): 1138 """Effective firewall rules for a VPC network. 1139 1140 Includes org/folder firewall policies). 1141 """ 1142 1143 _network: Network 1144 1145 def __init__(self, network, resource_data): 1146 super().__init__(resource_data) 1147 self._network = network
Effective firewall rules for a VPC network.
Includes org/folder firewall policies).
1160@caching.cached_api_call(in_memory=True) 1161def get_network(project_id: str, network_name: str, context: models.Context) -> Network: 1162 logging.debug('fetching network: %s/%s', project_id, network_name) 1163 compute = apis.get_api('compute', 'v1', project_id) 1164 request = compute.networks().get(project=project_id, network=network_name) 1165 response = request.execute(num_retries=config.API_RETRIES) 1166 return Network(project_id, response, context)
1169def get_subnetwork_from_url(url: str) -> Subnetwork: 1170 """Returns Subnetwork object given subnetwork url""" 1171 m = re.match( 1172 ( 1173 r'https://www.googleapis.com/compute/v1/projects/' 1174 r'([^/]+)/regions/([^/]+)/subnetworks/([^/]+)$' 1175 ), 1176 url, 1177 ) 1178 if not m: 1179 raise ValueError(f"can't parse network url: {url}") 1180 (project_id, region, subnetwork_name) = (m.group(1), m.group(2), m.group(3)) 1181 return get_subnetwork(project_id, region, subnetwork_name)
Returns Subnetwork object given subnetwork url
1184def get_network_from_url(url: str) -> Network: 1185 m = re.match( 1186 r'https://www.googleapis.com/compute/v1/projects/([^/]+)/global/networks/([^/]+)', url 1187 ) 1188 if not m: 1189 raise ValueError(f"can't parse network url: {url}") 1190 (project_id, network_name) = (m.group(1), m.group(2)) 1191 return get_network(project_id, network_name, models.Context(project_id=project_id))
1194@caching.cached_api_call(in_memory=True) 1195def get_networks(context: models.Context) -> List[Network]: 1196 logging.debug('fetching network: %s', context.project_id) 1197 compute = apis.get_api('compute', 'v1', context.project_id) 1198 request = compute.networks().list(project=context.project_id) 1199 response = request.execute(num_retries=config.API_RETRIES) 1200 return [Network(context.project_id, item, context) for item in response.get('items', [])]
1203@caching.cached_api_call(in_memory=True) 1204def get_subnetwork(project_id: str, region: str, subnetwork_name: str) -> Subnetwork: 1205 logging.debug('fetching network: %s/%s', project_id, subnetwork_name) 1206 compute = apis.get_api('compute', 'v1', project_id) 1207 request = compute.subnetworks().get(project=project_id, region=region, subnetwork=subnetwork_name) 1208 response = request.execute(num_retries=config.API_RETRIES) 1209 if response is None: 1210 raise RuntimeError(f'failed to fetch subnetwork: {project_id}/{region}/{subnetwork_name}') 1211 return Subnetwork(project_id, response, context=models.Context(project_id=project_id))
1250@caching.cached_api_call(in_memory=True) 1251def get_routes(project_id: str) -> List[Route]: 1252 logging.debug('fetching routes: %s', project_id) 1253 compute = apis.get_api('compute', 'v1', project_id) 1254 request = compute.routes().list(project=project_id) 1255 response = request.execute(num_retries=config.API_RETRIES) 1256 return [Route(project_id, item) for item in response.get('items', [])]
1259@caching.cached_api_call(in_memory=True) 1260def get_zones(project_id: str) -> List[ManagedZone]: 1261 logging.debug('fetching DNS zones: %s', project_id) 1262 dns = apis.get_api('dns', 'v1beta2', project_id) 1263 request = dns.managedZones().list(project=project_id) 1264 response = request.execute(num_retries=config.API_RETRIES) 1265 zones = [] 1266 for zone in response.get('managedZones', []): 1267 request2 = dns.managedZones().get(project=project_id, managedZone=zone['name']) 1268 response2 = request2.execute(num_retries=config.API_RETRIES) 1269 zones.append(ManagedZone(project_id, response2)) 1270 return zones
1273@caching.cached_api_call(in_memory=True) 1274def get_routers(project_id: str, region: str, network) -> List[Router]: 1275 logging.debug('fetching routers: %s/%s', project_id, region) 1276 compute = apis.get_api('compute', 'v1', project_id) 1277 request = compute.routers().list( 1278 project=project_id, region=region, filter=f'network="{network.self_link}"' 1279 ) 1280 response = request.execute(num_retries=config.API_RETRIES) 1281 return [Router(project_id, item) for item in response.get('items', [])]
1284@caching.cached_api_call(in_memory=True) 1285def get_router(project_id: str, region: str, network) -> Router: 1286 logging.debug('fetching routers: %s/%s', project_id, region) 1287 compute = apis.get_api('compute', 'v1', project_id) 1288 request = compute.routers().list( 1289 project=project_id, region=region, filter=f'network="{network.self_link}"' 1290 ) 1291 response = request.execute(num_retries=config.API_RETRIES) 1292 return Router(project_id, next(iter(response.get('items', [{}]))))
1295@caching.cached_api_call(in_memory=True) 1296def get_router_by_name(project_id: str, region: str, router_name: str) -> Router: 1297 logging.debug('fetching router list: %s/%s in region %s', project_id, router_name, region) 1298 compute = apis.get_api('compute', 'v1', project_id) 1299 request = compute.routers().list(project=project_id, region=region) 1300 response = request.execute(num_retries=config.API_RETRIES) 1301 return next( 1302 Router(project_id, item) for item in response.get('items', []) if item['name'] == router_name 1303 )
1306@caching.cached_api_call(in_memory=True) 1307def nat_router_status(project_id: str, router_name: str, region: str) -> RouterStatus: 1308 logging.debug('fetching router status: %s/%s in region %s', project_id, router_name, region) 1309 compute = apis.get_api('compute', 'v1', project_id) 1310 request = compute.routers().getRouterStatus(project=project_id, router=router_name, region=region) 1311 response = request.execute(num_retries=config.API_RETRIES) 1312 if 'result' in str(response): 1313 return RouterStatus(project_id, response) 1314 else: 1315 logging.debug( 1316 'unable to fetch router status: %s/%s in region %s', project_id, router_name, region 1317 ) 1318 return RouterStatus(project_id, {})
1321@caching.cached_api_call(in_memory=True) 1322def get_nat_ip_info(project_id: str, router_name: str, region: str) -> RouterNatIpInfo: 1323 logging.debug( 1324 'fetching NAT IP info for router: %s/%s in region %s', project_id, router_name, region 1325 ) 1326 compute = apis.get_api('compute', 'v1', project_id) 1327 request = compute.routers().getNatIpInfo(project=project_id, router=router_name, region=region) 1328 response = request.execute(num_retries=config.API_RETRIES) 1329 if 'result' in str(response): 1330 return RouterNatIpInfo(project_id, response) 1331 else: 1332 logging.debug( 1333 'unable to fetch Nat IP Info for router: %s/%s in region %s', project_id, router_name, region 1334 ) 1335 return RouterNatIpInfo(project_id, {})
1338class VPCSubnetworkIAMPolicy(iam.BaseIAMPolicy): 1339 def _is_resource_permission(self, permission): 1340 return True
Common class for IAM policies
1343@caching.cached_api_call(in_memory=True) 1344def get_subnetwork_iam_policy( 1345 context: models.Context, region: str, subnetwork_name: str 1346) -> VPCSubnetworkIAMPolicy: 1347 project_id = context.project_id 1348 resource_name = f'projects/{project_id}/regions/{region}/subnetworks/{subnetwork_name}' 1349 1350 compute = apis.get_api('compute', 'v1', project_id) 1351 request = compute.subnetworks().getIamPolicy( 1352 project=project_id, region=region, resource=subnetwork_name 1353 ) 1354 1355 return iam.fetch_iam_policy(request, VPCSubnetworkIAMPolicy, project_id, resource_name, context)
1358class Address(models.Resource): 1359 """IP Addresses.""" 1360 1361 _resource_data: dict 1362 1363 def __init__(self, project_id, resource_data): 1364 super().__init__(project_id=project_id) 1365 self._resource_data = resource_data 1366 1367 @property 1368 def full_path(self) -> str: 1369 result = re.match(r'https://www.googleapis.com/compute/v1/(.*)', self.self_link) 1370 if result: 1371 return result.group(1) 1372 else: 1373 return f'>> {self.self_link}' 1374 1375 @property 1376 def short_path(self) -> str: 1377 path = self.project_id + '/' + self.name 1378 return path 1379 1380 @property 1381 def name(self) -> str: 1382 return self._resource_data['name'] 1383 1384 @property 1385 def self_link(self) -> str: 1386 return self._resource_data.get('selfLink', '') 1387 1388 @property 1389 def subnetwork(self) -> str: 1390 return self._resource_data['subnetwork'] 1391 1392 @property 1393 def status(self) -> str: 1394 return self._resource_data['status']
IP Addresses.
1367 @property 1368 def full_path(self) -> str: 1369 result = re.match(r'https://www.googleapis.com/compute/v1/(.*)', self.self_link) 1370 if result: 1371 return result.group(1) 1372 else: 1373 return f'>> {self.self_link}'
Returns the full path of this resource.
Example: 'projects/gcpdiag-gke-1-9b90/zones/europe-west4-a/clusters/gke1'
1375 @property 1376 def short_path(self) -> str: 1377 path = self.project_id + '/' + self.name 1378 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'
1397@caching.cached_api_call(in_memory=True) 1398def get_addresses(project_id: str) -> List[Address]: 1399 logging.debug('fetching addresses list: %s', project_id) 1400 compute = apis.get_api('compute', 'v1', project_id) 1401 addresses = [] 1402 request = compute.addresses().aggregatedList(project=project_id) 1403 response = request.execute(num_retries=config.API_RETRIES) 1404 addresses_by_regions = response['items'] 1405 for _, data_ in addresses_by_regions.items(): 1406 if 'addresses' not in data_: 1407 continue 1408 addresses.extend([Address(project_id, address) for address in data_['addresses']]) 1409 return addresses