# -*- coding: utf-8 -*- # # Copyright 2015 Google LLC. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Code that's shared between multiple subnets subcommands.""" from __future__ import absolute_import from __future__ import division from __future__ import unicode_literals from googlecloudsdk.calliope import exceptions as calliope_exceptions from googlecloudsdk.command_lib.compute.networks.subnets import flags import six def _CreateSecondaryRange(client, name, ip_cidr_range, reserved_internal_range): """Creates a subnetwork secondary range.""" if reserved_internal_range and ip_cidr_range: return client.messages.SubnetworkSecondaryRange( rangeName=name, reservedInternalRange=reserved_internal_range, ipCidrRange=ip_cidr_range) elif reserved_internal_range: return client.messages.SubnetworkSecondaryRange( rangeName=name, reservedInternalRange=reserved_internal_range) else: return client.messages.SubnetworkSecondaryRange( rangeName=name, ipCidrRange=ip_cidr_range) def CreateSecondaryRanges(client, secondary_range, secondary_range_with_reserved_internal_range): """Creates all secondary ranges of a subnet.""" secondary_ranges = [] range_name_to_cidr = {} range_name_to_reserved_internal_range = {} range_names = set() if secondary_range: for secondary_range in secondary_range: for range_name, ip_cidr_range in six.iteritems(secondary_range): range_name_to_cidr[range_name] = ip_cidr_range range_names.add(range_name) if secondary_range_with_reserved_internal_range: for secondary_range in secondary_range_with_reserved_internal_range: for range_name, internal_range in six.iteritems(secondary_range): range_name_to_reserved_internal_range[range_name] = internal_range range_names.add(range_name) for range_name in sorted(range_names): secondary_ranges.append( _CreateSecondaryRange( client, range_name, range_name_to_cidr.get(range_name), range_name_to_reserved_internal_range.get(range_name))) return secondary_ranges def MakeSubnetworkUpdateRequest( client, subnet_ref, enable_private_ip_google_access=None, allow_cidr_routes_overlap=None, add_secondary_ranges=None, add_secondary_ranges_with_reserved_internal_range=None, remove_secondary_ranges=None, enable_flow_logs=None, aggregation_interval=None, flow_sampling=None, metadata=None, filter_expr=None, metadata_fields=None, set_new_purpose=None, set_role_active=None, drain_timeout_seconds=None, private_ipv6_google_access_type=None, stack_type=None, ipv6_access_type=None, external_ipv6_prefix=None, internal_ipv6_prefix=None, ip_collection=None, ): """Make the appropriate update request for the args. Args: client: GCE API client subnet_ref: Reference to a subnetwork enable_private_ip_google_access: Enable/disable access to Google Cloud APIs from this subnet for instances without a public ip address. allow_cidr_routes_overlap: Allow/Disallow this subnetwork's ranges to conflict with existing static routes. add_secondary_ranges: List of secondary IP ranges to add to the subnetwork for use in IP aliasing. add_secondary_ranges_with_reserved_internal_range: List of secondary IP ranges that are associated with InternalRange resources. remove_secondary_ranges: List of secondary ranges to remove from the subnetwork. enable_flow_logs: Enable/disable flow logging for this subnet. aggregation_interval: The internal at which to aggregate flow logs. flow_sampling: The sampling rate for flow logging in this subnet. metadata: Whether metadata fields should be added reported flow logs. filter_expr: custom CEL expression for filtering flow logs metadata_fields: custom metadata fields to be added to flow logs set_new_purpose: Update the purpose of the subnet. set_role_active: Updates the role of a BACKUP subnet to ACTIVE. drain_timeout_seconds: The maximum amount of time to drain connections from the active subnet to the backup subnet with set_role_active=True. private_ipv6_google_access_type: The private IPv6 google access type for the VMs in this subnet. stack_type: The stack type for this subnet. ipv6_access_type: The IPv6 access type for this subnet. external_ipv6_prefix: The IPv6 external prefix to be assigned to this subnet. internal_ipv6_prefix: The IPv6 internal prefix to be assigned to this subnet. When ULA is enabled, the prefix will be ignored. ip_collection: The IP collection that provisions BYOIP v6 addresses for this subnet. Returns: response, result of sending the update request for the subnetwork """ convert_to_enum = lambda x: x.replace('-', '_').upper() if enable_private_ip_google_access is not None: google_access = ( client.messages.SubnetworksSetPrivateIpGoogleAccessRequest()) google_access.privateIpGoogleAccess = enable_private_ip_google_access google_access_request = ( client.messages.ComputeSubnetworksSetPrivateIpGoogleAccessRequest( project=subnet_ref.project, region=subnet_ref.region, subnetwork=subnet_ref.Name(), subnetworksSetPrivateIpGoogleAccessRequest=google_access)) return client.MakeRequests([ (client.apitools_client.subnetworks, 'SetPrivateIpGoogleAccess', google_access_request) ]) elif (add_secondary_ranges is not None or add_secondary_ranges_with_reserved_internal_range is not None): subnetwork = client.messages.Subnetwork() original_subnetwork = client.MakeRequests([ (client.apitools_client.subnetworks, 'Get', client.messages.ComputeSubnetworksGetRequest(**subnet_ref.AsDict())) ])[0] subnetwork.secondaryIpRanges = original_subnetwork.secondaryIpRanges subnetwork.fingerprint = original_subnetwork.fingerprint subnetwork.secondaryIpRanges.extend( CreateSecondaryRanges( client, add_secondary_ranges, add_secondary_ranges_with_reserved_internal_range)) return client.MakeRequests( [CreateSubnetworkPatchRequest(client, subnet_ref, subnetwork)]) elif remove_secondary_ranges is not None: subnetwork = client.messages.Subnetwork() original_subnetwork = client.MakeRequests([ (client.apitools_client.subnetworks, 'Get', client.messages.ComputeSubnetworksGetRequest(**subnet_ref.AsDict())) ])[0] subnetwork.secondaryIpRanges = original_subnetwork.secondaryIpRanges subnetwork.fingerprint = original_subnetwork.fingerprint for name in remove_secondary_ranges[0]: if name not in [r.rangeName for r in subnetwork.secondaryIpRanges]: raise calliope_exceptions.UnknownArgumentException( 'remove-secondary-ranges', 'Subnetwork does not have a range {}, ' 'present ranges are {}.'.format( name, [r.rangeName for r in subnetwork.secondaryIpRanges])) subnetwork.secondaryIpRanges = [ r for r in original_subnetwork.secondaryIpRanges if r.rangeName not in remove_secondary_ranges[0] ] cleared_fields = [] if not subnetwork.secondaryIpRanges: cleared_fields.append('secondaryIpRanges') with client.apitools_client.IncludeFields(cleared_fields): return client.MakeRequests( [CreateSubnetworkPatchRequest(client, subnet_ref, subnetwork)]) elif (enable_flow_logs is not None or aggregation_interval is not None or flow_sampling is not None or metadata is not None or filter_expr is not None or metadata_fields is not None): subnetwork = client.messages.Subnetwork() original_subnetwork = client.MakeRequests([ (client.apitools_client.subnetworks, 'Get', client.messages.ComputeSubnetworksGetRequest(**subnet_ref.AsDict())) ])[0] subnetwork.fingerprint = original_subnetwork.fingerprint log_config = client.messages.SubnetworkLogConfig(enable=enable_flow_logs) if aggregation_interval is not None: log_config.aggregationInterval = flags.GetLoggingAggregationIntervalArg( client.messages).GetEnumForChoice(aggregation_interval) if flow_sampling is not None: log_config.flowSampling = flow_sampling if metadata is not None: log_config.metadata = flags.GetLoggingMetadataArg( client.messages).GetEnumForChoice(metadata) if filter_expr is not None: log_config.filterExpr = filter_expr if metadata_fields is not None: log_config.metadataFields = metadata_fields subnetwork.logConfig = log_config return client.MakeRequests( [CreateSubnetworkPatchRequest(client, subnet_ref, subnetwork)]) elif private_ipv6_google_access_type is not None: subnetwork = client.messages.Subnetwork() original_subnetwork = client.MakeRequests([ (client.apitools_client.subnetworks, 'Get', client.messages.ComputeSubnetworksGetRequest(**subnet_ref.AsDict())) ])[0] subnetwork.fingerprint = original_subnetwork.fingerprint subnetwork.privateIpv6GoogleAccess = ( client.messages.Subnetwork.PrivateIpv6GoogleAccessValueValuesEnum( ConvertPrivateIpv6GoogleAccess( convert_to_enum(private_ipv6_google_access_type)))) return client.MakeRequests( [CreateSubnetworkPatchRequest(client, subnet_ref, subnetwork)]) elif allow_cidr_routes_overlap is not None: subnetwork = client.messages.Subnetwork() original_subnetwork = client.MakeRequests([( client.apitools_client.subnetworks, 'Get', client.messages.ComputeSubnetworksGetRequest(**subnet_ref.AsDict()), )])[0] subnetwork.fingerprint = original_subnetwork.fingerprint subnetwork.allowSubnetCidrRoutesOverlap = allow_cidr_routes_overlap return client.MakeRequests( [CreateSubnetworkPatchRequest(client, subnet_ref, subnetwork)] ) elif set_new_purpose is not None: subnetwork = client.messages.Subnetwork() original_subnetwork = client.MakeRequests([ (client.apitools_client.subnetworks, 'Get', client.messages.ComputeSubnetworksGetRequest(**subnet_ref.AsDict())) ])[0] subnetwork.fingerprint = original_subnetwork.fingerprint subnetwork.purpose = client.messages.Subnetwork.PurposeValueValuesEnum( set_new_purpose) return client.MakeRequests( [CreateSubnetworkPatchRequest(client, subnet_ref, subnetwork)]) elif set_role_active is not None: subnetwork = client.messages.Subnetwork() original_subnetwork = client.MakeRequests([ (client.apitools_client.subnetworks, 'Get', client.messages.ComputeSubnetworksGetRequest(**subnet_ref.AsDict())) ])[0] subnetwork.fingerprint = original_subnetwork.fingerprint subnetwork.role = client.messages.Subnetwork.RoleValueValuesEnum.ACTIVE patch_request = client.messages.ComputeSubnetworksPatchRequest( project=subnet_ref.project, subnetwork=subnet_ref.subnetwork, region=subnet_ref.region, subnetworkResource=subnetwork, drainTimeoutSeconds=drain_timeout_seconds, ) return client.MakeRequests( [(client.apitools_client.subnetworks, 'Patch', patch_request)] ) elif ( stack_type is not None or ip_collection is not None or ipv6_access_type is not None or external_ipv6_prefix is not None or internal_ipv6_prefix is not None ): subnetwork = client.messages.Subnetwork() original_subnetwork = client.MakeRequests([( client.apitools_client.subnetworks, 'Get', client.messages.ComputeSubnetworksGetRequest(**subnet_ref.AsDict()), )])[0] subnetwork.fingerprint = original_subnetwork.fingerprint if stack_type is not None: subnetwork.stackType = ( client.messages.Subnetwork.StackTypeValueValuesEnum(stack_type) ) if ipv6_access_type is not None: subnetwork.ipv6AccessType = ( client.messages.Subnetwork.Ipv6AccessTypeValueValuesEnum( ipv6_access_type ) ) if external_ipv6_prefix is not None: subnetwork.externalIpv6Prefix = external_ipv6_prefix if internal_ipv6_prefix is not None: subnetwork.internalIpv6Prefix = internal_ipv6_prefix if ip_collection is not None: subnetwork.ipCollection = ip_collection return client.MakeRequests( [CreateSubnetworkPatchRequest(client, subnet_ref, subnetwork)] ) return client.MakeRequests([]) def CreateSubnetworkPatchRequest(client, subnet_ref, subnetwork_resource): patch_request = client.messages.ComputeSubnetworksPatchRequest( project=subnet_ref.project, subnetwork=subnet_ref.subnetwork, region=subnet_ref.region, subnetworkResource=subnetwork_resource) return (client.apitools_client.subnetworks, 'Patch', patch_request) def ConvertPrivateIpv6GoogleAccess(choice): """Return PrivateIpv6GoogleAccess enum defined in mixer. Args: choice: Enum value of PrivateIpv6GoogleAccess defined in gcloud. """ choices_to_enum = { 'DISABLE': 'DISABLE_GOOGLE_ACCESS', 'ENABLE_BIDIRECTIONAL_ACCESS': 'ENABLE_BIDIRECTIONAL_ACCESS_TO_GOOGLE', 'ENABLE_OUTBOUND_VM_ACCESS': 'ENABLE_OUTBOUND_VM_ACCESS_TO_GOOGLE', } return choices_to_enum.get(choice)