# -*- coding: utf-8 -*- # # Copyright 2016 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. """Flags and helpers for the compute routers commands.""" from __future__ import absolute_import from __future__ import division from __future__ import unicode_literals from googlecloudsdk.api_lib.compute import utils from googlecloudsdk.calliope import arg_parsers from googlecloudsdk.command_lib.compute import completers as compute_completers from googlecloudsdk.command_lib.compute import flags as compute_flags from googlecloudsdk.command_lib.util.apis import arg_utils DEFAULT_CREATE_FORMAT = """\ table( name, region.basename(), network.basename().if(network), ncc_gateway.basename().if(ncc_gateway) )""" DEFAULT_LIST_FORMAT = """\ table( name, region.basename(), network.basename().yesno(no="N/A") )""" _MODE_CHOICES = { 'DEFAULT': 'Default (Google-managed) BGP advertisements.', 'CUSTOM': 'Custom (user-configured) BGP advertisements.', } _GROUP_CHOICES = { 'ALL_SUBNETS': ( 'Automatically advertise all available subnets. This excludes ' 'any routes learned for subnets that use VPC Network Peering.' ), } _BFD_SESSION_INITIALIZATION_MODE_CHOICES = { 'ACTIVE': ( 'The Cloud Router will initiate the BFD session for this BGP peer.' ), 'PASSIVE': ( 'The Cloud Router will wait for the peer router to initiate the BFD ' 'session for this BGP peer.' ), 'DISABLED': 'BFD is disabled for this BGP peer.', } class RoutersCompleter(compute_completers.ListCommandCompleter): def __init__(self, **kwargs): super(RoutersCompleter, self).__init__( collection='compute.routers', list_command='compute routers list --uri', **kwargs ) def RouterArgument(required=True, plural=False): return compute_flags.ResourceArgument( resource_name='router', completer=RoutersCompleter, plural=plural, required=required, regional_collection='compute.routers', region_explanation=compute_flags.REGION_PROPERTY_EXPLANATION, ) def RouterArgumentForVpnTunnel(required=True): return compute_flags.ResourceArgument( resource_name='router', name='--router', completer=RoutersCompleter, plural=False, required=required, regional_collection='compute.routers', short_help='Router to use for dynamic routing.', region_explanation=compute_flags.REGION_PROPERTY_EXPLANATION, ) def RouterArgumentForOtherResources(required=True, suppress_region=True): region_explanation = ( 'Should be the same as --region, if not specified, ' 'it will be inherited from --region.' ) return compute_flags.ResourceArgument( resource_name='router', name='--router', completer=RoutersCompleter, plural=False, required=required, regional_collection='compute.routers', short_help='Google Cloud Router to use for dynamic routing.', region_explanation=region_explanation, region_hidden=suppress_region, ) def RouterArgumentForNat(): return compute_flags.ResourceArgument( resource_name='router', name='--router', completer=RoutersCompleter, plural=False, required=True, regional_collection='compute.routers', short_help='Router to use for NAT.', region_hidden=True, ) def AddCreateRouterArgs(parser): """Adds common arguments for creating routers.""" parser.add_argument( '--description', help='An optional description of this router.' ) AddAsnArg(parser) def AddKeepaliveIntervalArg(parser): """Adds keepalive interval argument for routers.""" parser.add_argument( '--keepalive-interval', type=arg_parsers.Duration( default_unit='s', lower_bound='20s', upper_bound='60s' ), help=( 'The interval between BGP keepalive messages that are sent to the' ' peer. If set, this value must be between 20 and 60 seconds. The' ' default is 20 seconds. See $ gcloud topic datetimes for information' ' on duration formats.\n\nBGP systems exchange keepalive messages to' ' determine whether a link or host has failed or is no longer' ' available. Hold time is the length of time in seconds that the BGP' ' session is considered operational without any activity. After the' ' hold time expires, the session is dropped.\n\nHold time is three' ' times the interval at which keepalive messages are sent, and the' ' hold time is the maximum number of seconds allowed to elapse' ' between successive keepalive messages that BGP receives from a' ' peer. BGP will use the smaller of either the local hold time value' " or the peer's hold time value as the hold time for the BGP" ' connection between the two peers.' ), ) def AddBgpIdentifierRangeArg(parser): """Adds BGP identifier range argument for routers.""" parser.add_argument( '--bgp-identifier-range', type=utils.IPV4RangeArgument, help=( 'The range of valid BGP Identifiers for this Router. Must be a' ' link-local IPv4 range from 169.254.0.0/16, of size at least /30,' ' even if the BGP sessions are over IPv6. It must not overlap with' ' any IPv4 BGP session ranges. This is commonly called "router ID" by' ' other vendors.' ), ) def AddNccGatewayArg(parser): """NccGateway for router.""" parser.add_argument( '--ncc-gateway', type=str, help='The NCC gateway for this router.', ) def AddAsnArg(parser): """Adds Asn argument for routers.""" parser.add_argument( '--asn', required=False, type=int, help=( 'The optional BGP autonomous system number (ASN) for this router. ' 'Must be a 16-bit or 32-bit private ASN as defined in ' 'https://tools.ietf.org/html/rfc6996, for example `--asn=64512`.' ), ) def AddInterfaceArgs(parser, for_update=False): """Adds common arguments for routers add-interface or update-interface.""" operation = 'added' if for_update: operation = 'updated' parser.add_argument( '--interface-name', required=True, help='The name of the interface being {0}.'.format(operation), ) parser.add_argument( '--ip-address', type=utils.IPArgument, help=( 'The link local (IPv4) or ULA (IPv6) address of the router for this' ' interface.' ), ) parser.add_argument( '--mask-length', type=arg_parsers.BoundedInt(lower_bound=0, upper_bound=126), help=( 'The subnet mask for the IP range of the interface. The interface' ' IP address and BGP peer IP address must be selected from the' ' subnet defined by this range.' ), ) parser.add_argument( '--ip-version', type=arg_utils.ChoiceToEnumName, choices={ 'IPV4': 'Interface with IPv4-based BGP.', 'IPV6': 'Interface with IPv6-based BGP.', }, help=( 'IP version of the interface. Possible values are IPV4 and IPV6.' ' Defaults to IPV4.' ), ) if not for_update: parser.add_argument( '--redundant-interface', help='The interface that is redundant to the current interface.', ) def AddBgpPeerArgs(parser, for_add_bgp_peer=False, is_update=False): """Adds common arguments for managing BGP peers.""" operation = 'updated' if for_add_bgp_peer: operation = 'added' parser.add_argument( '--peer-name', required=True, help='The name of the new BGP peer being {0}.'.format(operation), ) parser.add_argument( '--interface', required=for_add_bgp_peer, help='The name of the interface for this BGP peer.', ) parser.add_argument( '--peer-asn', required=for_add_bgp_peer, type=int, help=( 'The BGP autonomous system number (ASN) for this BGP peer. ' 'Must be a 16-bit or 32-bit private ASN as defined in ' 'https://tools.ietf.org/html/rfc6996, for example `--asn=64512`.' ), ) # For add_bgp_peer, we only require the interface and infer the IP instead. if not for_add_bgp_peer: parser.add_argument( '--ip-address', type=utils.IPArgument, help=( 'The address of the Cloud Router interface for this BGP peer.' ' Must be a link-local IPv4 address in the range 169.254.0.0/16' ' or an ULA IPv6 address in the range fdff:1::/64. It must also' ' be in the same subnet as the interface address of the peer' ' router.' ), ) parser.add_argument( '--peer-ip-address', type=utils.IPArgument, help=( 'The address of the peer router. Must be a link-local IPv4 address' ' in the range 169.254.0.0/16 or an ULA IPv6 address in the' ' range fdff:1::/64.' ), ) parser.add_argument( '--advertised-route-priority', type=arg_parsers.BoundedInt(lower_bound=0, upper_bound=65535), help=( 'The priority of routes advertised to this BGP peer. In the case ' 'where there is more than one matching route of maximum length, ' 'the routes with lowest priority value win. 0 <= priority <= ' '65535. If not specified, will use Google-managed priorities.' ), ) bfd_group_help = ( 'Arguments to {0} BFD (Bidirectional Forwarding Detection) ' 'settings:'.format('update' if is_update else 'configure') ) bfd_group = parser.add_group(help=bfd_group_help) bfd_group.add_argument( '--bfd-session-initialization-mode', choices=_BFD_SESSION_INITIALIZATION_MODE_CHOICES, type=lambda mode: mode.upper(), metavar='BFD_SESSION_INITIALIZATION_MODE', help=( 'The BFD session initialization mode for this BGP peer. Must be one' ' of:\n\nACTIVE - The Cloud Router will initiate the BFD session for' ' this BGP peer.\n\nPASSIVE - The Cloud Router will wait for the peer' ' router to initiate the BFD session for this BGP peer.\n\nDISABLED -' ' BFD is disabled for this BGP peer.' ), ) bfd_group.add_argument( '--bfd-min-transmit-interval', type=arg_parsers.Duration( default_unit='ms', lower_bound='1000ms', upper_bound='30000ms', parsed_unit='ms', ), help=( 'The minimum transmit interval between BFD control packets. The ' 'default is 1000 milliseconds. See $ gcloud topic datetimes for ' 'information on duration formats.' ), ) bfd_group.add_argument( '--bfd-min-receive-interval', type=arg_parsers.Duration( default_unit='ms', lower_bound='1000ms', upper_bound='30000ms', parsed_unit='ms', ), help=( 'The minimum receive interval between BFD control packets. The ' 'default is 1000 milliseconds. See $ gcloud topic datetimes for ' 'information on duration formats.' ), ) bfd_group.add_argument( '--bfd-multiplier', type=int, help=( 'The number of consecutive BFD control packets that must be ' 'missed before BFD declares that a peer is unavailable.' ), ) enabled_display_help = ( 'If enabled, the peer connection can be established with routing ' 'information. If disabled, any active session with the peer is ' 'terminated and all associated routing information is removed.' ) if not is_update: enabled_display_help += ' Enabled by default.' parser.add_argument( '--enabled', action=arg_parsers.StoreTrueFalseAction, help=enabled_display_help, ) enable_ipv6_display_help = ( 'If IPv6 is enabled, the peer connection can be established with ' 'IPv6 route exchange. If disabled, no IPv6 route exchange is allowed ' 'on any active session.' ) if not is_update: enable_ipv6_display_help += ' Disabled by default.' parser.add_argument( '--enable-ipv6', action=arg_parsers.StoreTrueFalseAction, help=enable_ipv6_display_help, ) parser.add_argument( '--ipv6-nexthop-address', type=utils.IPV6Argument, help=( 'If IPv6 route exchange is enabled for IPv4-based BGP, the IPv6 next' ' hop address of the Cloud Router interface for this BGP peer.' ' Ignored otherwise. Must be a Google owned global unicast IPv6' ' address belonging to the range 2600:2d00:0:2:0:0:0:0/64 or' ' 2600:2d00:0:3:0:0:0:0/64 and must belong to same subnet as the' ' interface address of the peer router.' ), ) parser.add_argument( '--peer-ipv6-nexthop-address', type=utils.IPV6Argument, help=( 'If IPv6 route exchange is enabled for IPv4-based BGP, the IPv6 next' ' hop address of the peer router. Ignored otherwise. Must be a Google' ' owned global unicast IPv6 address belonging to the range' ' 2600:2d00:0:2:0:0:0:0/64 or 2600:2d00:0:3:0:0:0:0/64.' ), ) enable_ipv4_display_help = ( 'If IPv4 is enabled, the peer connection can be established with ' 'IPv4 route exchange. If disabled, no IPv4 route exchange is allowed ' 'on any active session.' ) if not is_update: enable_ipv4_display_help += ( ' By default enabled for IPv4-based BGP sessions.' ) parser.add_argument( '--enable-ipv4', action=arg_parsers.StoreTrueFalseAction, help=enable_ipv4_display_help, ) parser.add_argument( '--ipv4-nexthop-address', type=utils.IPV4Argument, help=( 'If IPv4 route exchange is enabled for IPv6-based BGP, the IPv4' ' next hop address of the Cloud Router interface for this BGP peer.' ' Ignored otherwise. Must be a Google owned link-local IPv4 address' ' in the range 169.254.0.0/16 and must belong to the same subnet as' ' the interface address of the peer router.' ), ) parser.add_argument( '--peer-ipv4-nexthop-address', type=utils.IPV4Argument, help=( 'If IPv4 route exchange is enabled for IPv6-based BGP, the IPv4' ' next hop address of the peer router. Ignored otherwise. Must be a' ' Google owned link-local IPv4 address in the range 169.254.0.0/16.' ), ) parser.add_argument( '--md5-authentication-key', type=str, help=( 'The MD5 authentication key for this BGP peer. Maximum length is ' '80 characters. Can contain only printable ASCII characters.' ), ) if is_update: parser.add_argument( '--clear-md5-authentication-key', action='store_true', default=None, help='If specified, remove MD5 authentication from the BGP peer.', ) parser.add_argument( '--export-policies', metavar='EXPORT_POLICY', type=arg_parsers.ArgList(), help=( 'Comma-separated list of export policies. Passing an empty string' ' removes all export policies.' ), ) parser.add_argument( '--import-policies', type=arg_parsers.ArgList(), metavar='IMPORT_POLICY', help=( 'Comma-separated list of import policies. Passing an empty string' ' removes all import policies.' ), ) def AddUpdateCustomAdvertisementArgs(parser, resource_str): """Adds common arguments for setting/updating custom advertisements.""" AddReplaceCustomAdvertisementArgs(parser, resource_str) AddIncrementalCustomAdvertisementArgs(parser, resource_str) def AddReplaceCustomAdvertisementArgs(parser, resource_str): """Adds common arguments for replacing custom advertisements.""" parser.add_argument( '--advertisement-mode', choices=_MODE_CHOICES, type=lambda mode: mode.upper(), metavar='MODE', help="""The new advertisement mode for this {0}.""".format(resource_str), ) parser.add_argument( '--set-advertisement-groups', type=arg_parsers.ArgList( choices=_GROUP_CHOICES, element_type=lambda group: group.upper() ), metavar='GROUP', help="""The list of pre-defined groups of IP ranges to dynamically advertise on this {0}. This list can only be specified in custom advertisement mode.""".format(resource_str), ) parser.add_argument( '--set-advertisement-ranges', type=arg_parsers.ArgDict(allow_key_only=True), metavar='CIDR_RANGE=DESC', help="""The list of individual IP ranges, in CIDR format, to dynamically advertise on this {0}. Each IP range can (optionally) be given a text description DESC. For example, to advertise a specific range, use `--set-advertisement-ranges=192.168.10.0/24`. To store a description with the range, use `--set-advertisement-ranges=192.168.10.0/24=my-networks`. This list can only be specified in custom advertisement mode.""".format( resource_str ), ) def AddIncrementalCustomAdvertisementArgs(parser, resource_str): """Adds common arguments for incrementally updating custom advertisements.""" incremental_args = parser.add_mutually_exclusive_group(required=False) incremental_args.add_argument( '--add-advertisement-groups', type=arg_parsers.ArgList( choices=_GROUP_CHOICES, element_type=lambda group: group.upper() ), metavar='GROUP', help="""A list of pre-defined groups of IP ranges to dynamically advertise on this {0}. This list is appended to any existing advertisements. This field can only be specified in custom advertisement mode.""".format( resource_str ), ) incremental_args.add_argument( '--remove-advertisement-groups', type=arg_parsers.ArgList( choices=_GROUP_CHOICES, element_type=lambda group: group.upper() ), metavar='GROUP', help="""A list of pre-defined groups of IP ranges to remove from dynamic advertisement on this {0}. Each group in the list must exist in the current set of custom advertisements. This field can only be specified in custom advertisement mode.""".format(resource_str), ) incremental_args.add_argument( '--add-advertisement-ranges', type=arg_parsers.ArgDict(allow_key_only=True), metavar='CIDR_RANGE=DESC', help="""A list of individual IP ranges, in CIDR format, to dynamically advertise on this {0}. This list is appended to any existing advertisements. Each IP range can (optionally) be given a text description DESC. For example, to advertise a specific range, use `--advertisement-ranges=192.168.10.0/24`. To store a description with the range, use `--advertisement-ranges=192.168.10.0/24=my-networks`. This list can only be specified in custom advertisement mode.""".format( resource_str ), ) incremental_args.add_argument( '--remove-advertisement-ranges', type=arg_parsers.ArgList(), metavar='CIDR_RANGE', help="""A list of individual IP ranges, in CIDR format, to remove from dynamic advertisement on this {0}. Each IP range in the list must exist in the current set of custom advertisements. This field can only be specified in custom advertisement mode.""".format( resource_str ), ) def AddUpdateCustomLearnedRoutesArgs(parser): """Adds common arguments for setting/updating custom learned routes. Args: parser: The parser to parse arguments. """ AddReplaceCustomLearnedRoutesArgs(parser) AddIncrementalCustomLearnedRoutesArgs(parser) def AddReplaceCustomLearnedRoutesArgs(parser): """Adds common arguments for replacing custom learned routes. Args: parser: The parser to parse arguments. """ parser.add_argument( '--custom-learned-route-priority', type=arg_parsers.BoundedInt(lower_bound=0, upper_bound=65535), metavar='PRIORITY', help="""An integral value `0` <= priority <= `65535`, to be applied to all custom learned route IP address ranges for this peer. If not specified, a Google-managed priority value of 100 is used. The routes with the lowest priority value win.""", ) parser.add_argument( '--set-custom-learned-route-ranges', type=arg_parsers.ArgList(), metavar='CIDR_RANGE', help="""The list of user-defined custom learned route IP address ranges for this peer. This list is a comma separated IP address ranges such as `1.2.3.4`,`6.7.0.0/16`,`2001:db8:abcd:12::/64` where each IP address range must be a valid CIDR-formatted prefix. If an IP address is provided without a subnet mask, it is interpreted as a /32 singular IP address range for IPv4, and /128 for IPv6.""", ) def AddIncrementalCustomLearnedRoutesArgs(parser): """Adds common arguments for incrementally updating custom learned routes. Args: parser: The parser to parse arguments. """ incremental_args = parser.add_mutually_exclusive_group(required=False) incremental_args.add_argument( '--add-custom-learned-route-ranges', type=arg_parsers.ArgList(), metavar='CIDR_RANGE', help="""A list of user-defined custom learned route IP address ranges to be added to this peer. This list is a comma separated IP address ranges such as `1.2.3.4`,`6.7.0.0/16`,`2001:db8:abcd:12::/64` where each IP address range must be a valid CIDR-formatted prefix. If an IP address is provided without a subnet mask, it is interpreted as a /32 singular IP address range for IPv4, and /128 for IPv6.""", ) incremental_args.add_argument( '--remove-custom-learned-route-ranges', type=arg_parsers.ArgList(), metavar='CIDR_RANGE', help="""A list of user-defined custom learned route IP address ranges to be removed from this peer. This list is a comma separated IP address ranges such as `1.2.3.4`,`6.7.0.0/16`,`2001:db8:abcd:12::/64` where each IP address range must be a valid CIDR-formatted prefix. If an IP address is provided without a subnet mask, it is interpreted as a /32 singular IP address range for IPv4, and /128 for IPv6.""", ) def AddGetNatMappingInfoArgs(parser): """Adds common arguments for get-nat-mapping-info.""" parser.add_argument( '--nat-name', required=False, help='The NAT name to filter out NAT mapping information', ) def AddGetNatIpInfoArgs(parser): """Adds common arguments for get-ip-mapping-info.""" parser.add_argument( '--nat-name', required=False, help='The NAT name to filter out NAT IP information', ) def AddEncryptedInterconnectRouter(parser): """Adds encrypted interconnect router flag.""" parser.add_argument( '--encrypted-interconnect-router', required=False, action='store_true', default=None, help=( 'Indicates if a router is dedicated for use with encrypted ' 'interconnect attachments (VLAN attachments).' ), )