feat: Add new gcloud commands, API clients, and third-party libraries across various services.

This commit is contained in:
2026-01-01 20:26:35 +01:00
parent 5e23cbece0
commit a19e592eb7
25221 changed files with 8324611 additions and 0 deletions

View File

@@ -0,0 +1,42 @@
# -*- 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.
"""Commands for reading and manipulating routers."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
@base.UniverseCompatible
@base.ReleaseTracks(
base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA, base.ReleaseTrack.GA)
class Routers(base.Group):
"""List, create, and delete Compute Engine routers."""
Routers.category = base.NETWORKING_CATEGORY
Routers.detailed_help = {
'DESCRIPTION': """
List, create, and delete Cloud Routers.
For more information about Cloud Routers, see the
[Cloud Router documentation](https://cloud.google.com//network-connectivity/docs/router/concepts/overview).
See also: [Routers API](https://cloud.google.com/compute/docs/reference/rest/v1/routers).
""",
}

View File

@@ -0,0 +1,328 @@
# -*- 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.
"""Command for adding a BGP peer to a Compute Engine router."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.compute import routers_utils
from googlecloudsdk.api_lib.compute.operations import poller
from googlecloudsdk.api_lib.util import waiter
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute.instances import flags as instance_flags
from googlecloudsdk.command_lib.compute.routers import flags
from googlecloudsdk.command_lib.compute.routers import router_utils
from googlecloudsdk.core import log
from googlecloudsdk.core import resources
import six
@base.ReleaseTracks(base.ReleaseTrack.GA)
@base.UniverseCompatible
class AddBgpPeer(base.UpdateCommand):
"""Add a BGP peer to a Compute Engine router."""
ROUTER_ARG = None
INSTANCE_ARG = None
@classmethod
def _Args(cls, parser):
cls.ROUTER_ARG = flags.RouterArgument()
cls.ROUTER_ARG.AddArgument(parser)
cls.INSTANCE_ARG = instance_flags.InstanceArgumentForRouter()
cls.INSTANCE_ARG.AddArgument(parser)
base.ASYNC_FLAG.AddToParser(parser)
flags.AddBgpPeerArgs(parser, for_add_bgp_peer=True)
flags.AddReplaceCustomAdvertisementArgs(parser, 'peer')
flags.AddReplaceCustomLearnedRoutesArgs(parser)
@classmethod
def Args(cls, parser):
cls._Args(parser)
def _Run(
self,
args,
support_bfd_mode=False,
):
"""Runs the command.
Args:
args: contains arguments passed to the command
support_bfd_mode: The flag to indicate whether bfd mode is supported.
Returns:
The result of patching the router adding the bgp peer with the
information provided in the arguments.
"""
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
messages = holder.client.messages
service = holder.client.apitools_client.routers
router_ref = self.ROUTER_ARG.ResolveAsResource(args, holder.resources)
request_type = messages.ComputeRoutersGetRequest
replacement = service.Get(request_type(**router_ref.AsDict()))
instance_ref = None
if args.instance is not None:
instance_ref = self.INSTANCE_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=instance_flags.GetInstanceZoneScopeLister(holder.client),
)
md5_authentication_key_name = router_utils.GenerateMd5AuthenticationKeyName(
replacement, args
)
peer = _CreateBgpPeerMessage(
messages,
args,
md5_authentication_key_name=md5_authentication_key_name,
support_bfd_mode=support_bfd_mode,
instance_ref=instance_ref,
)
if router_utils.HasReplaceAdvertisementFlags(args):
mode, groups, ranges = router_utils.ParseAdvertisements(
messages=messages, resource_class=messages.RouterBgpPeer, args=args
)
attrs = {
'advertiseMode': mode,
'advertisedGroups': groups,
'advertisedIpRanges': ranges,
}
for attr, value in six.iteritems(attrs):
if value is not None:
setattr(peer, attr, value)
if args.set_custom_learned_route_ranges is not None:
peer.customLearnedIpRanges = routers_utils.ParseCustomLearnedIpRanges(
messages=messages, ip_ranges=args.set_custom_learned_route_ranges
)
replacement.bgpPeers.append(peer)
if args.md5_authentication_key is not None:
md5_authentication_key = messages.RouterMd5AuthenticationKey(
name=md5_authentication_key_name, key=args.md5_authentication_key
)
replacement.md5AuthenticationKeys.append(md5_authentication_key)
result = service.Patch(
messages.ComputeRoutersPatchRequest(
project=router_ref.project,
region=router_ref.region,
router=router_ref.Name(),
routerResource=replacement,
)
)
operation_ref = resources.REGISTRY.Parse(
result.name,
collection='compute.regionOperations',
params={
'project': router_ref.project,
'region': router_ref.region,
},
)
if args.async_:
log.UpdatedResource(
operation_ref,
kind='router [{0}] to add peer [{1}]'.format(
router_ref.Name(), peer.name
),
is_async=True,
details=(
'Run the [gcloud compute operations describe] command '
'to check the status of this operation.'
),
)
return result
target_router_ref = holder.resources.Parse(
router_ref.Name(),
collection='compute.routers',
params={
'project': router_ref.project,
'region': router_ref.region,
},
)
operation_poller = poller.Poller(service, target_router_ref)
return waiter.WaitFor(
operation_poller,
operation_ref,
'Creating peer [{0}] in router [{1}]'.format(
peer.name, router_ref.Name()
),
)
def Run(self, args):
"""See base.UpdateCommand."""
return self._Run(args)
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class AddBgpPeerBeta(AddBgpPeer):
"""Add a BGP peer to a Compute Engine router."""
ROUTER_ARG = None
INSTANCE_ARG = None
@classmethod
def Args(cls, parser):
cls._Args(parser)
def Run(self, args):
"""See base.UpdateCommand."""
return self._Run(args, support_bfd_mode=False)
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class AddBgpPeerAlpha(AddBgpPeerBeta):
"""Add a BGP peer to a Compute Engine router."""
ROUTER_ARG = None
INSTANCE_ARG = None
@classmethod
def Args(cls, parser):
cls._Args(parser)
def Run(self, args):
"""See base.UpdateCommand."""
return self._Run(
args,
support_bfd_mode=True,
)
def _CreateBgpPeerMessage(
messages,
args,
md5_authentication_key_name,
support_bfd_mode=False,
instance_ref=None,
):
"""Creates a BGP peer with base attributes based on flag arguments.
Args:
messages: API messages holder.
args: contains arguments passed to the command.
md5_authentication_key_name: The md5 authentication key name.
support_bfd_mode: The flag to indicate whether bfd mode is supported.
instance_ref: An instance reference.
Returns:
the RouterBgpPeer
"""
if support_bfd_mode:
bfd = _CreateBgpPeerBfdMessageMode(messages, args)
else:
bfd = _CreateBgpPeerBfdMessage(messages, args)
enable = None
if args.enabled is not None:
if args.enabled:
enable = messages.RouterBgpPeer.EnableValueValuesEnum.TRUE
else:
enable = messages.RouterBgpPeer.EnableValueValuesEnum.FALSE
result = messages.RouterBgpPeer(
name=args.peer_name,
interfaceName=args.interface,
peerIpAddress=args.peer_ip_address,
peerAsn=args.peer_asn,
advertisedRoutePriority=args.advertised_route_priority,
enable=enable,
bfd=bfd,
enableIpv6=args.enable_ipv6,
ipv6NexthopAddress=args.ipv6_nexthop_address,
peerIpv6NexthopAddress=args.peer_ipv6_nexthop_address,
enableIpv4=args.enable_ipv4,
ipv4NexthopAddress=args.ipv4_nexthop_address,
peerIpv4NexthopAddress=args.peer_ipv4_nexthop_address,
)
result.customLearnedRoutePriority = args.custom_learned_route_priority
if instance_ref is not None:
result.routerApplianceInstance = instance_ref.SelfLink()
if args.md5_authentication_key is not None:
result.md5AuthenticationKeyName = md5_authentication_key_name
if args.export_policies is not None:
result.exportPolicies = args.export_policies
if args.import_policies is not None:
result.importPolicies = args.import_policies
return result
def _CreateBgpPeerBfdMessage(messages, args):
"""Creates a BGP peer with base attributes based on flag arguments."""
if not (
args.IsSpecified('bfd_min_receive_interval')
or args.IsSpecified('bfd_min_transmit_interval')
or args.IsSpecified('bfd_session_initialization_mode')
or args.IsSpecified('bfd_multiplier')
):
return None
bfd_session_initialization_mode = None
if args.bfd_session_initialization_mode is not None:
bfd_session_initialization_mode = (
messages.RouterBgpPeerBfd.SessionInitializationModeValueValuesEnum(
args.bfd_session_initialization_mode
)
)
return messages.RouterBgpPeerBfd(
minReceiveInterval=args.bfd_min_receive_interval,
minTransmitInterval=args.bfd_min_transmit_interval,
sessionInitializationMode=bfd_session_initialization_mode,
multiplier=args.bfd_multiplier,
)
def _CreateBgpPeerBfdMessageMode(messages, args):
"""Creates a BGP peer with base attributes based on flag arguments."""
if not (
args.IsSpecified('bfd_min_receive_interval')
or args.IsSpecified('bfd_min_transmit_interval')
or args.IsSpecified('bfd_session_initialization_mode')
or args.IsSpecified('bfd_multiplier')
):
return None
mode = None
bfd_session_initialization_mode = None
if args.bfd_session_initialization_mode is not None:
mode = messages.RouterBgpPeerBfd.ModeValueValuesEnum(
args.bfd_session_initialization_mode
)
bfd_session_initialization_mode = (
messages.RouterBgpPeerBfd.SessionInitializationModeValueValuesEnum(
args.bfd_session_initialization_mode
)
)
return messages.RouterBgpPeerBfd(
minReceiveInterval=args.bfd_min_receive_interval,
minTransmitInterval=args.bfd_min_transmit_interval,
mode=mode,
sessionInitializationMode=bfd_session_initialization_mode,
multiplier=args.bfd_multiplier,
)

View File

@@ -0,0 +1,211 @@
# -*- 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.
"""Command for adding an interface to a Compute Engine router."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import encoding
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import parser_errors
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.interconnects.attachments import (
flags as attachment_flags,
)
from googlecloudsdk.command_lib.compute.networks.subnets import flags as subnet_flags
from googlecloudsdk.command_lib.compute.routers import flags as router_flags
from googlecloudsdk.command_lib.compute.vpn_tunnels import (
flags as vpn_tunnel_flags,
)
@base.ReleaseTracks(base.ReleaseTrack.GA)
class AddInterface(base.UpdateCommand):
"""Add an interface to a Compute Engine router.
*{command}* is used to add an interface to a Compute Engine
router.
"""
ROUTER_ARG = None
VPN_TUNNEL_ARG = None
INTERCONNECT_ATTACHMENT_ARG = None
SUBNETWORK_ARG = None
@classmethod
def _Args(cls, parser):
cls.ROUTER_ARG = router_flags.RouterArgument()
cls.ROUTER_ARG.AddArgument(parser, operation_type='update')
link_parser = parser.add_mutually_exclusive_group(required=True)
vpn_tunnel_group = link_parser.add_argument_group('VPN Tunnel')
cls.VPN_TUNNEL_ARG = vpn_tunnel_flags.VpnTunnelArgumentForRouter(
required=False
)
cls.VPN_TUNNEL_ARG.AddArgument(vpn_tunnel_group)
interconnect_attachment_group = link_parser.add_argument_group(
'Interconnect Attachment'
)
cls.INTERCONNECT_ATTACHMENT_ARG = (
attachment_flags.InterconnectAttachmentArgumentForRouter()
)
cls.INTERCONNECT_ATTACHMENT_ARG.AddArgument(interconnect_attachment_group)
subnetwork_group = link_parser.add_argument_group('Subnetwork')
cls.SUBNETWORK_ARG = subnet_flags.SubnetworkArgumentForRouter()
cls.SUBNETWORK_ARG.AddArgument(subnetwork_group)
router_flags.AddInterfaceArgs(parser)
@classmethod
def Args(cls, parser):
cls._Args(parser)
def _GetGetRequest(self, client, router_ref):
return (
client.apitools_client.routers,
'Get',
client.messages.ComputeRoutersGetRequest(
router=router_ref.Name(),
region=router_ref.region,
project=router_ref.project,
),
)
def _GetSetRequest(self, client, router_ref, replacement):
return (
client.apitools_client.routers,
'Patch',
client.messages.ComputeRoutersPatchRequest(
router=router_ref.Name(),
routerResource=replacement,
region=router_ref.region,
project=router_ref.project,
),
)
def Modify(self, client, resources, args, existing):
replacement = encoding.CopyProtoMessage(existing)
mask = None
interface_name = args.interface_name
if args.ip_address is not None:
if args.subnetwork is None and args.mask_length is not None:
mask = '{0}/{1}'.format(args.ip_address, args.mask_length)
elif args.subnetwork is None:
raise parser_errors.ArgumentException(
'--mask-length must be set if --ip-address is set'
)
elif args.mask_length is not None:
raise parser_errors.ArgumentException(
'--mask-length cannot be set if --subnetwork is set'
)
ip_version = None
if args.ip_version is not None:
ip_version = client.messages.RouterInterface.IpVersionValueValuesEnum(
args.ip_version
)
if not args.vpn_tunnel_region:
args.vpn_tunnel_region = replacement.region
vpn_ref = None
if args.vpn_tunnel is not None:
vpn_ref = self.VPN_TUNNEL_ARG.ResolveAsResource(
args,
resources,
scope_lister=compute_flags.GetDefaultScopeLister(client),
)
if not args.interconnect_attachment_region:
args.interconnect_attachment_region = replacement.region
attachment_ref = None
if args.interconnect_attachment is not None:
attachment_ref = self.INTERCONNECT_ATTACHMENT_ARG.ResolveAsResource(
args, resources
)
subnetwork_ref = None
private_ip_address = None
redundant_interface = None
if args.subnetwork is not None:
subnetwork_ref = self.SUBNETWORK_ARG.ResolveAsResource(args, resources)
private_ip_address = args.ip_address
redundant_interface = args.redundant_interface
interface = client.messages.RouterInterface(
name=interface_name,
linkedVpnTunnel=(vpn_ref.SelfLink() if vpn_ref else None),
linkedInterconnectAttachment=(
attachment_ref.SelfLink() if attachment_ref else None
),
subnetwork=(subnetwork_ref.SelfLink() if subnetwork_ref else None),
ipRange=mask,
privateIpAddress=private_ip_address,
redundantInterface=redundant_interface,
)
if ip_version is not None:
interface.ipVersion = ip_version
replacement.interfaces.append(interface)
return replacement
def _Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
router_ref = self.ROUTER_ARG.ResolveAsResource(args, holder.resources)
get_request = self._GetGetRequest(client, router_ref)
objects = client.MakeRequests([get_request])
new_object = self.Modify(
client,
holder.resources,
args,
objects[0],
)
return client.MakeRequests(
[self._GetSetRequest(client, router_ref, new_object)]
)
def Run(self, args):
return self._Run(args)
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class AddInterfaceBeta(AddInterface):
"""Add an interface to a Compute Engine router.
*{command}* is used to add an interface to a Compute Engine
router.
"""
pass
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class AddInterfaceAlpha(AddInterfaceBeta):
"""Add an interface to a Compute Engine router.
*{command}* is used to add an interface to a Compute Engine
router.
"""
pass

View File

@@ -0,0 +1,127 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 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.
"""Command for adding an empty named set to a Compute Engine router."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags
from googlecloudsdk.command_lib.util.apis import arg_utils
@base.Hidden
@base.UniverseCompatible
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class AddNamedSet(base.CreateCommand):
"""Add an empty named set to a Compute Engine router.
*{command}* adds an empty named set to a Compute Engine router.
"""
ROUTER_ARG = None
@classmethod
def Args(cls, parser):
AddNamedSet.ROUTER_ARG = flags.RouterArgument()
AddNamedSet.ROUTER_ARG.AddArgument(parser, operation_type='insert')
parser.add_argument(
'--set-name',
help="""Name of the named set to add.""",
required=True,
)
parser.add_argument(
'--set-type',
type=arg_utils.ChoiceToEnumName,
choices={
'PREFIX': 'The Named Set is a Prefix Named Set.',
'COMMUNITY': 'The Named Set is a Community Named Set.',
},
help="""Type of the set's elements.""",
required=True,
)
def Run(self, args):
"""Issues the requests needed for adding an empty named set to a Router.
Args:
args: contains arguments passed to the command.
Returns:
The result of patching the router adding the empty named set.
"""
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
router_ref = AddNamedSet.ROUTER_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client),
)
named_set = client.messages.NamedSet(
name=args.set_name,
type=client.messages.NamedSet.TypeValueValuesEnum(
self.ConvertSetType(args.set_type)
),
)
self.RequireNamedSetDoesNotExist(client, router_ref, args.set_name)
request = (
client.apitools_client.routers,
'UpdateNamedSet',
client.messages.ComputeRoutersUpdateNamedSetRequest(
**router_ref.AsDict(), namedSet=named_set
),
)
return client.MakeRequests([request])[0]
def RequireNamedSetDoesNotExist(self, client, router_ref, set_name):
request = (
client.apitools_client.routers,
'GetNamedSet',
client.messages.ComputeRoutersGetNamedSetRequest(
**router_ref.AsDict(), namedSet=set_name
),
)
try:
client.MakeRequests([request])
except Exception as exception:
if (
"The named set '{set_name}' was not found.".format(set_name=set_name)
in exception.__str__()
# TODO(b/437296415): Remove this legacy error message once the new
# error message is fully rolled out.
or "Could not fetch resource:\n - Invalid value for field 'namedSet': "
in exception.__str__()
):
return
raise
raise exceptions.BadArgumentException(
'set-name', "A named set named '{0}' already exists".format(set_name)
)
def ConvertSetType(self, set_type):
if set_type == 'PREFIX':
return 'NAMED_SET_TYPE_PREFIX'
elif set_type == 'COMMUNITY':
return 'NAMED_SET_TYPE_COMMUNITY'
else:
return set_type

View File

@@ -0,0 +1,77 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 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.
"""Command for adding an element to an existing named set of a Compute Engine router."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags
@base.Hidden
@base.UniverseCompatible
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class AddNamedSetElement(base.UpdateCommand):
"""Adds an element to an existing named set of a Compute Engine router."""
ROUTER_ARG = None
@classmethod
def Args(cls, parser):
AddNamedSetElement.ROUTER_ARG = flags.RouterArgument()
AddNamedSetElement.ROUTER_ARG.AddArgument(parser, operation_type='update')
parser.add_argument(
'--set-name',
help="""Name of the set.""",
required=True,
)
parser.add_argument(
'--new-set-element',
help="""CEL expression for the element.""",
required=True,
)
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
messages = holder.client.messages
service = holder.client.apitools_client.routers
router_ref = AddNamedSetElement.ROUTER_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client),
)
named_set = service.GetNamedSet(
messages.ComputeRoutersGetNamedSetRequest(
**router_ref.AsDict(), namedSet=args.set_name
)
).resource
new_element = messages.Expr(expression=args.new_set_element)
named_set.elements.append(new_element)
request = (
service,
'PatchNamedSet',
messages.ComputeRoutersPatchNamedSetRequest(
**router_ref.AsDict(),
namedSet=named_set,
),
)
return client.MakeRequests([request])[0]

View File

@@ -0,0 +1,131 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 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.
"""Command for adding an empty route policy to a Compute Engine router."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags
from googlecloudsdk.command_lib.util.apis import arg_utils
@base.UniverseCompatible
class AddRoutePolicy(base.UpdateCommand):
"""Add an empty route policy to a Compute Engine router."""
ROUTER_ARG = None
@classmethod
def Args(cls, parser):
AddRoutePolicy.ROUTER_ARG = flags.RouterArgument()
AddRoutePolicy.ROUTER_ARG.AddArgument(parser, operation_type='update')
parser.add_argument(
'--policy-name',
help="""Name of the route policy to add.""",
required=True,
)
parser.add_argument(
'--policy-type',
type=arg_utils.ChoiceToEnumName,
choices={
'IMPORT': 'The route policy is an import policy.',
'EXPORT': 'The route policy is an export policy.',
},
help="""Type of the route policy to add.""",
required=True,
)
def Run(self, args):
"""Issues the requests needed for adding an empty route policy to a Router.
Args:
args: contains arguments passed to the command.
Returns:
The result of patching the router adding the empty route policy.
"""
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
router_ref = AddRoutePolicy.ROUTER_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client),
)
route_policy = client.messages.RoutePolicy(
name=args.policy_name,
type=client.messages.RoutePolicy.TypeValueValuesEnum(
self.ConvertRouteType(args.policy_type)
),
)
self.RequireRoutePolicyDoesNotExist(client, router_ref, args.policy_name)
request = (
client.apitools_client.routers,
'UpdateRoutePolicy',
client.messages.ComputeRoutersUpdateRoutePolicyRequest(
**router_ref.AsDict(), routePolicy=route_policy
),
)
return client.MakeRequests([request])[0]
def RequireRoutePolicyDoesNotExist(self, client, router_ref, policy_name):
request = (
client.apitools_client.routers,
'GetRoutePolicy',
client.messages.ComputeRoutersGetRoutePolicyRequest(
**router_ref.AsDict(), policy=policy_name
),
)
try:
client.MakeRequests([request])
except Exception as exception:
if (
"Could not fetch resource:\n - Invalid value for field 'policy': "
in exception.__str__()
):
return
raise
raise exceptions.BadArgumentException(
'policy-name', "A policy named '{0}' already exists".format(policy_name)
)
def ConvertRouteType(self, route_type):
if route_type == 'IMPORT':
return 'ROUTE_POLICY_TYPE_IMPORT'
elif route_type == 'EXPORT':
return 'ROUTE_POLICY_TYPE_EXPORT'
else:
return route_type
AddRoutePolicy.detailed_help = {
'DESCRIPTION': """\
*{command}* adds an empty route policy to a Compute Engine router.
""",
'EXAMPLES': """\
To add an import route policy `my-policy` to a router `my-router` in region `us-central1`, run:
$ {command} my-router --region=us-central1 --policy-name=my-policy --policy-type=IMPORT
""",
}

View File

@@ -0,0 +1,110 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 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.
"""Command for adding a term to an existing Route Policy of a Compute Engine router."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags
@base.UniverseCompatible
class AddRoutePolicyTerm(base.UpdateCommand):
"""Adds a new term to an existing route policy of a Comute Engine router."""
ROUTER_ARG = None
@classmethod
def Args(cls, parser):
AddRoutePolicyTerm.ROUTER_ARG = flags.RouterArgument()
AddRoutePolicyTerm.ROUTER_ARG.AddArgument(parser, operation_type='update')
parser.add_argument(
'--policy-name',
help="""Name of the route policy to which to add the term.""",
required=True,
)
parser.add_argument(
'--priority',
help="""Order of the term within the policy.""",
required=True,
type=arg_parsers.BoundedInt(lower_bound=0, upper_bound=2147483647),
)
parser.add_argument(
'--match',
help="""CEL expression for matching a route.""",
required=True,
)
parser.add_argument(
'--actions',
help="""Semicolon separated CEL expressions for the actions to take when the rule matches.""",
required=True,
type=arg_parsers.ArgList(custom_delim_char=';'),
metavar='ACTION',
)
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
messages = holder.client.messages
service = holder.client.apitools_client.routers
router_ref = AddRoutePolicyTerm.ROUTER_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client),
)
route_policy = service.GetRoutePolicy(
messages.ComputeRoutersGetRoutePolicyRequest(
**router_ref.AsDict(), policy=args.policy_name
)
).resource
new_term = messages.RoutePolicyPolicyTerm(
priority=args.priority,
match=messages.Expr(expression=args.match),
actions=[
messages.Expr(expression=cel_expression)
for cel_expression in args.actions
],
)
route_policy.terms.append(new_term)
request = (
service,
'PatchRoutePolicy',
messages.ComputeRoutersPatchRoutePolicyRequest(
**router_ref.AsDict(),
routePolicy=route_policy,
),
)
return client.MakeRequests([request])[0]
AddRoutePolicyTerm.detailed_help = {
'DESCRIPTION': """\
*{command}* adds a term to a route policy.
""",
'EXAMPLES': """\
To add a term with priority 0 with match `destination == '192.168.0.0/16'` and actions `drop()` to a route policy `my-policy` of a router `my-router` in region `us-central1`, run:
$ {command} my-router --region=us-central1 --policy-name=my-policy --priority=0 --match="destination == '192.168.0.0/16'" --actions="drop()"
""",
}

View File

@@ -0,0 +1,248 @@
# -*- 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.
"""Command for creating Compute Engine routers."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.compute.operations import poller
from googlecloudsdk.api_lib.util import waiter
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import resource_manager_tags_utils
from googlecloudsdk.command_lib.compute.networks import flags as network_flags
from googlecloudsdk.command_lib.compute.routers import flags
from googlecloudsdk.command_lib.compute.routers import router_utils
from googlecloudsdk.core import log
from googlecloudsdk.core import resources
import six
@base.UniverseCompatible
@base.ReleaseTracks(base.ReleaseTrack.GA)
class Create(base.CreateCommand):
"""Create a Compute Engine router.
*{command}* is used to create a router to provide dynamic routing to VPN
tunnels and interconnects.
"""
ROUTER_ARG = None
_support_ncc_gateway = False
_support_tagging_at_creation = True
@classmethod
def _Args(cls, parser):
parser.display_info.AddFormat(flags.DEFAULT_CREATE_FORMAT)
if not cls._support_ncc_gateway:
cls.add_network_arg(parser)
else:
cls.add_ncc_gateway_and_network_arg(parser)
if cls._support_tagging_at_creation:
parser.add_argument(
'--resource-manager-tags',
type=arg_parsers.ArgDict(),
metavar='KEY=VALUE',
help="""\
Comma-separated list of Resource Manager tags to attach to the router. Key-value pairs must be provided in the form tagKeys/{TagKey_Numeric_ID}=tagValues/{TagValue_Numeric_ID}.
See [Listing tag keys](https://cloud.google.com/resource-manager/docs/tags/tags-creating-and-managing#listing_keys).
""",
)
cls.ROUTER_ARG = flags.RouterArgument()
cls.ROUTER_ARG.AddArgument(parser, operation_type='create')
base.ASYNC_FLAG.AddToParser(parser)
flags.AddCreateRouterArgs(parser)
flags.AddKeepaliveIntervalArg(parser)
flags.AddBgpIdentifierRangeArg(parser)
flags.AddEncryptedInterconnectRouter(parser)
flags.AddReplaceCustomAdvertisementArgs(parser, 'router')
parser.display_info.AddCacheUpdater(flags.RoutersCompleter)
@classmethod
def add_ncc_gateway_and_network_arg(cls, parser):
link_parser = parser.add_mutually_exclusive_group(required=True)
flags.AddNccGatewayArg(link_parser)
cls.NETWORK_ARG = network_flags.NetworkArgumentForOtherResource(
required=False,
short_help='The network for this router'
)
cls.NETWORK_ARG.AddArgument(link_parser)
@classmethod
def add_network_arg(cls, parser):
cls.NETWORK_ARG = network_flags.NetworkArgumentForOtherResource(
'The network for this router'
)
cls.NETWORK_ARG.AddArgument(parser)
@classmethod
def Args(cls, parser):
"""See base.CreateCommand."""
cls._Args(parser)
def _Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
messages = holder.client.messages
service = holder.client.apitools_client.routers
router_ref = self.ROUTER_ARG.ResolveAsResource(args, holder.resources)
router_resource = messages.Router(
name=router_ref.Name(),
description=args.description,
)
if self._support_ncc_gateway:
if args.ncc_gateway is not None:
router_resource.nccGateway = args.ncc_gateway
if self._support_tagging_at_creation:
if args.resource_manager_tags is not None:
router_resource.params = self._CreateRouterParams(
messages, args.resource_manager_tags
)
if args.network is not None:
network_ref = self.NETWORK_ARG.ResolveAsResource(args, holder.resources)
router_resource.network = network_ref.SelfLink()
# Add bgp field with the assigned asn and/or keepalive_interval
if args.asn is not None or args.keepalive_interval is not None:
router_resource.bgp = messages.RouterBgp(
asn=args.asn, keepaliveInterval=args.keepalive_interval
)
if args.IsSpecified('encrypted_interconnect_router'):
router_resource.encryptedInterconnectRouter = (
args.encrypted_interconnect_router
)
if router_utils.HasReplaceAdvertisementFlags(args):
mode, groups, ranges = router_utils.ParseAdvertisements(
messages=messages, resource_class=messages.RouterBgp, args=args
)
attrs = {
'advertiseMode': mode,
'advertisedGroups': groups,
'advertisedIpRanges': ranges,
}
# Create an empty bgp field if not generated yet.
if args.asn is None:
router_resource.bgp = messages.RouterBgp()
for attr, value in six.iteritems(attrs):
if value is not None:
setattr(router_resource.bgp, attr, value)
if args.bgp_identifier_range is not None:
if not hasattr(router_resource.bgp, 'identifierRange'):
router_resource.bgp = messages.RouterBgp()
router_resource.bgp.identifierRange = args.bgp_identifier_range
result = service.Insert(
messages.ComputeRoutersInsertRequest(
router=router_resource,
region=router_ref.region,
project=router_ref.project,
)
)
operation_ref = resources.REGISTRY.Parse(
result.name,
collection='compute.regionOperations',
params={
'project': router_ref.project,
'region': router_ref.region,
},
)
if args.async_:
# Override the networks list format with the default operations format
if not args.IsSpecified('format'):
args.format = 'none'
log.CreatedResource(
operation_ref,
kind='router [{0}]'.format(router_ref.Name()),
is_async=True,
details=(
'Run the [gcloud compute operations describe] command '
'to check the status of this operation.'
),
)
return result
target_router_ref = holder.resources.Parse(
router_ref.Name(),
collection='compute.routers',
params={
'project': router_ref.project,
'region': router_ref.region,
},
)
operation_poller = poller.Poller(service, target_router_ref)
return waiter.WaitFor(
operation_poller,
operation_ref,
'Creating router [{0}]'.format(router_ref.Name()),
)
def Run(self, args):
"""See base.UpdateCommand."""
return self._Run(args)
def _CreateRouterParams(self, messages, resource_manager_tags):
resource_manager_tags_map = (
resource_manager_tags_utils.GetResourceManagerTags(
resource_manager_tags
)
)
params = messages.RouterParams
additional_properties = [
params.ResourceManagerTagsValue.AdditionalProperty(key=key, value=value)
for key, value in sorted(six.iteritems(resource_manager_tags_map))
]
return params(
resourceManagerTags=params.ResourceManagerTagsValue(
additionalProperties=additional_properties
)
)
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class CreateBeta(Create):
"""Create a Compute Engine router.
*{command}* is used to create a router to provide dynamic routing to VPN
tunnels and interconnects.
"""
_support_ncc_gateway = True
_support_tagging_at_creation = True
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class CreateAlpha(CreateBeta):
"""Create a Compute Engine router.
*{command}* is used to create a router to provide dynamic routing to VPN
tunnels and interconnects.
"""
_support_ncc_gateway = True
_support_tagging_at_creation = True

View File

@@ -0,0 +1,63 @@
# -*- 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.
"""Command for deleting Compute Engine routers."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.compute import utils
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags
class Delete(base.DeleteCommand):
"""Delete Compute Engine routers.
*{command}* deletes one or more Compute Engine
routers. Routers can only be deleted when no other resources
(e.g., virtual machine instances) refer to them.
"""
ROUTER_ARG = None
@classmethod
def Args(cls, parser):
Delete.ROUTER_ARG = flags.RouterArgument(plural=True)
Delete.ROUTER_ARG.AddArgument(parser, operation_type='delete')
parser.display_info.AddCacheUpdater(flags.RoutersCompleter)
def Run(self, args):
"""Issues requests necessary to delete Routers."""
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
router_refs = Delete.ROUTER_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client))
utils.PromptForDeletion(router_refs, 'region')
requests = []
for router_ref in router_refs:
requests.append((client.apitools_client.routers, 'Delete',
client.messages.ComputeRoutersDeleteRequest(
**router_ref.AsDict())))
return client.MakeRequests(requests)

View File

@@ -0,0 +1,56 @@
# -*- 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.
"""Command for describing Compute Engine routers."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags
class Describe(base.DescribeCommand):
"""Describe a Compute Engine router.
*{command}* displays all data associated with a Compute Engine
router.
"""
ROUTERS_ARG = None
@classmethod
def Args(cls, parser):
Describe.ROUTERS_ARG = flags.RouterArgument()
Describe.ROUTERS_ARG.AddArgument(parser, operation_type='describe')
def Run(self, args):
"""Issues the request necessary for describing a router."""
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
router_ref = self.ROUTERS_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client))
request = client.messages.ComputeRoutersGetRequest(
**router_ref.AsDict())
return client.MakeRequests([(client.apitools_client.routers,
'Get', request)])[0]

View File

@@ -0,0 +1,99 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 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.
"""Command for downloading a named set from a Compute Engine router."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import os
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags
from googlecloudsdk.core.resource import resource_printer
from googlecloudsdk.core.util import files
@base.Hidden
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class DownloadNamedSet(base.DescribeCommand):
"""Download a named set from a Compute Engine router.
*{command}* downloads a named set from a Compute Engine router.
"""
ROUTER_ARG = None
@classmethod
def Args(cls, parser):
DownloadNamedSet.ROUTER_ARG = flags.RouterArgument()
DownloadNamedSet.ROUTER_ARG.AddArgument(parser, operation_type='export')
parser.add_argument(
'--set-name',
required=True,
help='Name of the named set to download.',
)
parser.add_argument(
'--file-name',
required=True,
help='The name of the file to download the named set config to.',
)
parser.add_argument(
'--file-format',
choices=['json', 'yaml'],
help='Format of the file passed to --file-name',
)
def Run(self, args):
"""Downloads a named set from a Router into the specified file."""
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
router_ref = DownloadNamedSet.ROUTER_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client),
)
if os.path.isdir(args.file_name):
raise exceptions.BadFileException(
'[{0}] is a directory'.format(args.file_name)
)
named_set = self.GetNamedSet(client, router_ref, args.set_name)
self.WriteToFile(named_set, args.file_name, args.file_format)
def GetNamedSet(self, client, router_ref, set_name):
request = (
client.apitools_client.routers,
'GetNamedSet',
client.messages.ComputeRoutersGetNamedSetRequest(
**router_ref.AsDict(), namedSet=set_name
),
)
return client.MakeRequests([request])[0]
def WriteToFile(self, message, file_name, file_format):
if file_format is None:
file_format = 'yaml'
with files.FileWriter(file_name) as export_file:
resource_printer.Print(
resources=message,
print_format=file_format,
out=export_file,
)

View File

@@ -0,0 +1,107 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 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.
"""Command for downloading a route policy from a Compute Engine router."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import os
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags
from googlecloudsdk.core.resource import resource_printer
from googlecloudsdk.core.util import files
@base.UniverseCompatible
class DownloadRoutePolicy(base.DescribeCommand):
"""Download a route policy from a Compute Engine router."""
ROUTER_ARG = None
@classmethod
def Args(cls, parser):
DownloadRoutePolicy.ROUTER_ARG = flags.RouterArgument()
DownloadRoutePolicy.ROUTER_ARG.AddArgument(parser, operation_type='export')
parser.add_argument(
'--policy-name',
required=True,
help='Name of the route policy to download.',
)
parser.add_argument(
'--file-name',
required=True,
help='The name of the file to download the route policy config to.',
)
parser.add_argument(
'--file-format',
choices=['json', 'yaml'],
help='Format of the file passed to --file-name',
)
def Run(self, args):
"""Downloads a route policy from a Router into the specified file."""
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
router_ref = DownloadRoutePolicy.ROUTER_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client),
)
if os.path.isdir(args.file_name):
raise exceptions.BadFileException(
'[{0}] is a directory'.format(args.file_name)
)
route_policy = self.GetRoutePolicy(client, router_ref, args.policy_name)
self.WriteToFile(route_policy, args.file_name, args.file_format)
def GetRoutePolicy(self, client, router_ref, policy_name):
request = (
client.apitools_client.routers,
'GetRoutePolicy',
client.messages.ComputeRoutersGetRoutePolicyRequest(
**router_ref.AsDict(), policy=policy_name
),
)
return client.MakeRequests([request])[0]
def WriteToFile(self, message, file_name, file_format):
if file_format is None:
file_format = 'yaml'
with files.FileWriter(file_name) as export_file:
resource_printer.Print(
resources=message,
print_format=file_format,
out=export_file,
)
DownloadRoutePolicy.detailed_help = {
'DESCRIPTION': """\
*{command}* downloads a route policy from a Compute Engine router.
""",
'EXAMPLES': """\
To download a route policy `my-export-policy` to a file `my-export-policy.yaml` from a router `my-router` in region `us-central1`, run:
$ {command} my-router --region=us-central1 --policy-name=my-export-policy --file-name=my-export-policy.yaml"
""",
}

View File

@@ -0,0 +1,67 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 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.
"""Command for getting a named set from a Compute Engine router."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags
@base.Hidden
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class GetNamedSet(base.DescribeCommand):
"""Get a named set from a Compute Engine router.
*{command}* gets a named set from a Compute Engine router.
"""
ROUTER_ARG = None
@classmethod
def Args(cls, parser):
GetNamedSet.ROUTER_ARG = flags.RouterArgument()
GetNamedSet.ROUTER_ARG.AddArgument(parser, operation_type='get')
parser.add_argument(
'--set-name',
help="""Name of the named set to get.""",
required=True,
)
def Run(self, args):
"""Issues the request necessary for getting a named set from a Router."""
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
router_ref = GetNamedSet.ROUTER_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client),
)
request = (
client.apitools_client.routers,
'GetNamedSet',
client.messages.ComputeRoutersGetNamedSetRequest(
**router_ref.AsDict(), namedSet=args.set_name
),
)
return client.MakeRequests([request])[0]

View File

@@ -0,0 +1,75 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 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.
"""Command for getting NAT IP information from Compute Engine routers."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags as routers_flags
class GetNatIpInfo(base.DescribeCommand):
"""Display NAT IP information in a router."""
ROUTER_ARG = None
@classmethod
def Args(cls, parser):
cls.ROUTER_ARG = routers_flags.RouterArgument()
cls.ROUTER_ARG.AddArgument(parser, operation_type='get NAT IP info')
routers_flags.AddGetNatIpInfoArgs(parser)
base.URI_FLAG.RemoveFromParser(parser)
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
router_ref = self.ROUTER_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client),
)
params = router_ref.AsDict()
if args.nat_name:
params['natName'] = args.nat_name
request = client.messages.ComputeRoutersGetNatIpInfoRequest(**params)
return client.MakeRequests([(client.apitools_client.routers,
'GetNatIpInfo', request)])[0]
GetNatIpInfo.detailed_help = {
'DESCRIPTION': """
$ {command}
shows a mapping of IP:[usage, mode]
allocated to each NAT via the specified router.""",
'EXAMPLES': """\
To show NAT IP information from all NATs in router 'r1' in region
'us-central1', run:
$ {command} r1 --region=us-central1
To show NAT IP information for a specific NAT 'nat1' in router 'r1' in
region 'us-central1', run:
$ {command} r1 --region=us-central1 --nat-name="nat1"
"""
}

View File

@@ -0,0 +1,84 @@
# -*- coding: utf-8 -*- #
# Copyright 2018 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.
"""Command for listing Compute Engine routers."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import list_pager
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags as routers_flags
class GetNatMappingInfo(base.ListCommand):
"""Display NAT Mapping information in a router."""
ROUTER_ARG = None
@classmethod
def Args(cls, parser):
cls.ROUTER_ARG = routers_flags.RouterArgument()
cls.ROUTER_ARG.AddArgument(parser, operation_type='get NAT mapping info')
routers_flags.AddGetNatMappingInfoArgs(parser)
base.URI_FLAG.RemoveFromParser(parser)
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
router_ref = self.ROUTER_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client))
params = router_ref.AsDict()
if args.nat_name:
params['natName'] = args.nat_name
request = client.messages.ComputeRoutersGetNatMappingInfoRequest(**params)
return list_pager.YieldFromList(
client.apitools_client.routers,
request,
limit=args.limit,
batch_size=args.page_size,
method='GetNatMappingInfo',
field='result',
current_token_attribute='pageToken',
next_token_attribute='nextPageToken',
batch_size_attribute='maxResults',
)
GetNatMappingInfo.detailed_help = {
'DESCRIPTION':
"""
$ {command}
shows a mapping of IP:port-ranges
allocated to each VM's interface that is configured to use NAT via the
specified router.""",
'EXAMPLES':
"""\
To show NAT mappings from all NATs in router 'r1' in region
'us-central1', run:
$ {command} r1 --region=us-central1
"""
}

View File

@@ -0,0 +1,76 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 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.
"""Command for getting a route policy from a Compute Engine router."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags
@base.UniverseCompatible
class GetRoutePolicy(base.DescribeCommand):
"""Get a route policy from a Compute Engine router."""
ROUTER_ARG = None
@classmethod
def Args(cls, parser):
GetRoutePolicy.ROUTER_ARG = flags.RouterArgument()
GetRoutePolicy.ROUTER_ARG.AddArgument(parser, operation_type='get')
parser.add_argument(
'--policy-name',
help="""Name of the route policy to get.""",
required=True,
)
def Run(self, args):
"""Issues the request necessary for getting a route policy from a Router."""
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
router_ref = GetRoutePolicy.ROUTER_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client),
)
request = (
client.apitools_client.routers,
'GetRoutePolicy',
client.messages.ComputeRoutersGetRoutePolicyRequest(
**router_ref.AsDict(), policy=args.policy_name
),
)
return client.MakeRequests([request])[0]
GetRoutePolicy.detailed_help = {
'DESCRIPTION': """\
*{command}* gets a route policy from a Compute Engine router.
""",
'EXAMPLES': """\
To get a route policy `my-policy` from a router `my-router` in region `us-central1`, run:
$ {command} my-router --region=us-central1 --policy-name=my-policy
""",
}

View File

@@ -0,0 +1,55 @@
# -*- 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.
"""Command for getting the status of Compute Engine routers."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags
class Describe(base.DescribeCommand):
"""Get status of a Compute Engine router.
*{command}* displays all runtime data associated with a Compute Engine
router.
"""
ROUTER_ARG = None
@classmethod
def Args(cls, parser):
Describe.ROUTER_ARG = flags.RouterArgument()
Describe.ROUTER_ARG.AddArgument(parser, operation_type='describe')
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
router_ref = Describe.ROUTER_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client))
request = client.messages.ComputeRoutersGetRouterStatusRequest(
**router_ref.AsDict())
return client.MakeRequests([(client.apitools_client.routers,
'GetRouterStatus', request)])[0]

View File

@@ -0,0 +1,49 @@
# -*- 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.
"""Command for listing Compute Engine routers."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.compute import lister
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute.routers import flags
class List(base.ListCommand):
"""List Compute Engine routers."""
@classmethod
def Args(cls, parser):
parser.display_info.AddFormat(flags.DEFAULT_LIST_FORMAT)
lister.AddRegionsArg(parser)
parser.display_info.AddCacheUpdater(flags.RoutersCompleter)
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
request_data = lister.ParseRegionalFlags(args, holder.resources)
list_implementation = lister.RegionalLister(
client, client.apitools_client.routers)
return lister.Invoke(request_data, list_implementation)
List.detailed_help = base_classes.GetRegionalListerHelp('routers')

View File

@@ -0,0 +1,133 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 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.
"""Command for listing bgp routes from a Compute Engine router."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import list_pager
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags
from googlecloudsdk.command_lib.util.apis import arg_utils
@base.UniverseCompatible
class ListBgpRoutes(base.ListCommand):
"""List routes advertised and learned on individual BGP sessions, both pre- and post-policy evaluation."""
ROUTER_ARG = None
@classmethod
def Args(cls, parser):
ListBgpRoutes.ROUTER_ARG = flags.RouterArgument()
ListBgpRoutes.ROUTER_ARG.AddArgument(parser, operation_type='list')
parser.display_info.AddCacheUpdater(flags.RoutersCompleter)
parser.add_argument(
'--peer',
help="""Limit results to routes learned from this peer (name).""",
required=True,
)
parser.add_argument(
'--address-family',
type=arg_utils.ChoiceToEnumName,
choices={
'IPV4': 'Interface with IPv4-based BGP.',
'IPV6': 'Interface with IPv6-based BGP.',
},
help="""Limit results to routes learned for this Address Family Identifier.""",
required=True,
)
parser.add_argument(
'--route-direction',
type=arg_utils.ChoiceToEnumName,
choices={
'INBOUND': 'Learned routes.',
'OUTBOUND': 'Advertised routes.',
},
help="""Limit results to routes in this direction.""",
required=True,
)
parser.add_argument(
'--policy-applied',
action='store_true',
default=True,
help="""Routes returned are post-policy evaluation.""",
)
parser.add_argument(
'--destination-range',
help="""Limit results to prefixes.""",
metavar='CIDR_RANGE',
)
def Run(self, args):
"""Issues a request necessary for listing bgp routes from a Router."""
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
router_ref = ListBgpRoutes.ROUTER_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client),
)
address_family = client.messages.ComputeRoutersListBgpRoutesRequest.AddressFamilyValueValuesEnum(
args.address_family
)
route_type = client.messages.ComputeRoutersListBgpRoutesRequest.RouteTypeValueValuesEnum(
self.ConvertRouteDirection(args.route_direction)
)
request = client.messages.ComputeRoutersListBgpRoutesRequest(
**router_ref.AsDict(),
peer=args.peer,
addressFamily=address_family,
routeType=route_type,
policyApplied=args.policy_applied,
destinationPrefix=args.destination_range,
)
return list_pager.YieldFromList(
client.apitools_client.routers,
request,
limit=args.limit,
batch_size=args.page_size,
method='ListBgpRoutes',
field='result',
current_token_attribute='pageToken',
next_token_attribute='nextPageToken',
batch_size_attribute='maxResults',
)
def ConvertRouteDirection(self, route_direction):
if route_direction == 'INBOUND':
return 'LEARNED'
elif route_direction == 'OUTBOUND':
return 'ADVERTISED'
else:
return route_direction
ListBgpRoutes.detailed_help = {
'DESCRIPTION': """\
*{command}* lists routes advertised and learned on individual BGP sessions, both pre- and post-policy evaluation.
""",
'EXAMPLES': """\
To list inbound BGP routes limited to IPv4 addess family from a router `my-router` BGP peer `my-bgp-peer` in region `us-central1`, run:
$ {command} my-router --region=us-central1 --address-family=IPV4 --peer=my-bgp-peer --route-direction=INBOUND"
""",
}

View File

@@ -0,0 +1,70 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 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.
"""Command for listing named sets from a Compute Engine router."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import list_pager
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags
@base.Hidden
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class ListNamedSets(base.ListCommand):
"""List named sets from a Compute Engine router.
*{command}* lists all named sets from a Compute Engine router.
"""
ROUTER_ARG = None
@classmethod
def Args(cls, parser):
ListNamedSets.ROUTER_ARG = flags.RouterArgument()
ListNamedSets.ROUTER_ARG.AddArgument(parser, operation_type='list')
parser.display_info.AddCacheUpdater(flags.RoutersCompleter)
parser.display_info.AddFormat('table(name, type)')
def Run(self, args):
"""Issues a request necessary for listing named sets from a Router."""
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
router_ref = ListNamedSets.ROUTER_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client),
)
request = client.messages.ComputeRoutersListNamedSetsRequest(
**router_ref.AsDict()
)
return list_pager.YieldFromList(
client.apitools_client.routers,
request,
limit=args.limit,
batch_size=args.page_size,
method='ListNamedSets',
field='result',
current_token_attribute='pageToken',
next_token_attribute='nextPageToken',
batch_size_attribute='maxResults',
)

View File

@@ -0,0 +1,77 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 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.
"""Command for listing route policies from a Compute Engine router."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import list_pager
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags
@base.UniverseCompatible
class ListRoutePolicies(base.ListCommand):
"""List route policies from a Compute Engine router."""
ROUTER_ARG = None
@classmethod
def Args(cls, parser):
ListRoutePolicies.ROUTER_ARG = flags.RouterArgument()
ListRoutePolicies.ROUTER_ARG.AddArgument(parser, operation_type='list')
parser.display_info.AddCacheUpdater(flags.RoutersCompleter)
parser.display_info.AddFormat('table(name, type)')
def Run(self, args):
"""Issues a request necessary for listing route policies from a Router."""
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
router_ref = ListRoutePolicies.ROUTER_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client),
)
request = client.messages.ComputeRoutersListRoutePoliciesRequest(
**router_ref.AsDict()
)
return list_pager.YieldFromList(
client.apitools_client.routers,
request,
limit=args.limit,
batch_size=args.page_size,
method='ListRoutePolicies',
field='result',
current_token_attribute='pageToken',
next_token_attribute='nextPageToken',
batch_size_attribute='maxResults',
)
ListRoutePolicies.detailed_help = {
'DESCRIPTION': """\
*{command}* lists all route policies from a Compute Engine router.
""",
'EXAMPLES': """\
To list route policies from a router `my-router` in region `us-central1`, run:
$ {command} my-router --region=us-central1
""",
}

View File

@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*- #
# Copyright 2018 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.
"""Commands for reading and manipulating NATs."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
class Nats(base.Group):
"""List, create, describe, and delete Cloud NAT."""
Nats.detailed_help = {
'DESCRIPTION': """
List, create, describe, and delete Cloud NAT.
For more information about Cloud NAT, see the
[Cloud NAT documentation](https://cloud.google.com/nat/docs/using-nat).
See also: [Routers API](https://cloud.google.com/compute/docs/reference/rest/v1/routers).
""",
}

View File

@@ -0,0 +1,149 @@
# -*- coding: utf-8 -*- #
# Copyright 2018 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.
"""Command for adding a NAT to a Compute Engine router."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.compute.operations import poller
from googlecloudsdk.api_lib.util import waiter
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags as routers_flags
from googlecloudsdk.command_lib.compute.routers.nats import flags as nats_flags
from googlecloudsdk.command_lib.compute.routers.nats import nats_utils
from googlecloudsdk.core import log
from googlecloudsdk.core import resources
@base.UniverseCompatible
@base.ReleaseTracks(
base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA, base.ReleaseTrack.GA
)
class Create(base.CreateCommand):
"""Add a NAT to a Compute Engine router."""
@classmethod
def Args(cls, parser):
cls.ROUTER_ARG = routers_flags.RouterArgumentForNat()
cls.ROUTER_ARG.AddArgument(parser)
base.ASYNC_FLAG.AddToParser(parser)
compute_flags.AddRegionFlag(parser, 'NAT', operation_type='create')
nats_flags.AddNatNameArg(parser, operation_type='create')
nats_flags.AddTypeArg(parser)
nats_flags.AddEndpointTypesArg(parser)
nats_flags.AddCommonNatArgs(parser, for_create=True)
def Run(self, args):
"""See base.CreateCommand."""
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
messages = holder.client.messages
service = holder.client.apitools_client.routers
router_ref = self.ROUTER_ARG.ResolveAsResource(args, holder.resources)
request_type = messages.ComputeRoutersGetRequest
replacement = service.Get(request_type(**router_ref.AsDict()))
nat = nats_utils.CreateNatMessage(args, holder)
replacement.nats.append(nat)
result = service.Patch(
messages.ComputeRoutersPatchRequest(
project=router_ref.project,
region=router_ref.region,
router=router_ref.Name(),
routerResource=replacement))
operation_ref = resources.REGISTRY.Parse(
result.name,
collection='compute.regionOperations',
params={
'project': router_ref.project,
'region': router_ref.region,
})
if args.async_:
log.CreatedResource(
operation_ref,
kind='nat [{0}] in router [{1}]'.format(nat.name, router_ref.Name()),
is_async=True,
details='Run the [gcloud compute operations describe] command '
'to check the status of this operation.')
return result
target_router_ref = holder.resources.Parse(
router_ref.Name(),
collection='compute.routers',
params={
'project': router_ref.project,
'region': router_ref.region,
})
operation_poller = poller.Poller(service, target_router_ref)
return waiter.WaitFor(
operation_poller, operation_ref,
'Creating NAT [{0}] in router [{1}]'.format(nat.name,
router_ref.Name()))
Create.detailed_help = {
'DESCRIPTION':
"""
*{command}* is used to create a NAT on a Compute Engine router.
""",
'EXAMPLES':
"""\
Auto-allocate NAT for all IP addresses of all subnets in the region:
$ {command} nat1 --router=my-router
--auto-allocate-nat-external-ips --nat-all-subnet-ip-ranges
Specify IP addresses for NAT:
Each IP address is the name of a reserved static IP address resource in
the same region.
$ {command} nat1 --router=my-router
--nat-external-ip-pool=ip-address1,ip-address2
Specify subnet ranges for NAT:
By default, NAT works for all primary and secondary IP ranges for all
subnets in the region for the given VPC network. You can restrict which
subnet primary and secondary ranges can use NAT.
$ {command} nat1 --router=my-router
--auto-allocate-nat-external-ips
--nat-custom-subnet-ip-ranges=subnet-1,subnet-3:secondary-range-1
""",
'API REFERENCE':
"""\
This command, when specified without alpha or beta, uses the compute/v1/routers API. The full documentation
for this API can be found at: https://cloud.google.com/compute/docs/reference/rest/v1/routers/
The beta command uses the compute/beta/routers API. The full documentation
for this API can be found at: https://cloud.google.com/compute/docs/reference/rest/beta/routers/
The alpha command uses the compute/alpha/routers API. Full documentation is not available for the alpha API.
"""
}

View File

@@ -0,0 +1,117 @@
# -*- coding: utf-8 -*- #
# Copyright 2020 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.
"""Command for removing a NAT from a Compute Engine router."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from apitools.base.py import encoding
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.compute import utils
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags as routers_flags
from googlecloudsdk.command_lib.compute.routers.nats import flags as nats_flags
from googlecloudsdk.command_lib.compute.routers.nats import nats_utils
class AlphaDelete(base.DeleteCommand):
"""Remove a NAT from a Compute Engine router.
*{command}* removes a NAT from a Compute Engine router.
"""
ROUTER_ARG = None
@classmethod
def Args(cls, parser):
cls.ROUTER_ARG = routers_flags.RouterArgumentForNat()
cls.ROUTER_ARG.AddArgument(parser)
compute_flags.AddRegionFlag(
parser, 'NAT', operation_type='delete', plural=True)
nats_flags.AddNatNameArg(parser, operation_type='delete', plural=True)
def _GetPatchRequest(self, client, router_ref, replacement):
return (client.apitools_client.routers, 'Patch',
client.messages.ComputeRoutersPatchRequest(
router=router_ref.Name(),
routerResource=replacement,
region=router_ref.region,
project=router_ref.project))
def Modify(self, args, existing, cleared_fields):
"""Mutate the router and record any cleared_fields for Patch request."""
replacement = encoding.CopyProtoMessage(existing)
for name in args.name:
nat = nats_utils.FindNatOrRaise(replacement, name)
replacement.nats.remove(nat)
# If all NATs have been removed, add this field to cleared_fields.
if not replacement.nats:
cleared_fields.append('nats')
return replacement
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
router_ref = self.ROUTER_ARG.ResolveAsResource(args, holder.resources)
objects = client.MakeRequests(
[(client.apitools_client.routers, 'Get',
client.messages.ComputeRoutersGetRequest(**router_ref.AsDict()))])
# Cleared list fields need to be explicitly identified for Patch API.
cleared_fields = []
new_object = self.Modify(args, objects[0], cleared_fields)
utils.PromptForDeletionHelper(
'NAT', ['{} in router {}'.format(args.name, router_ref.Name())])
with client.apitools_client.IncludeFields(cleared_fields):
# There is only one response because one request is made above
result = client.MakeRequests(
[self._GetPatchRequest(client, router_ref, new_object)])
return result
AlphaDelete.detailed_help = {
'DESCRIPTION':
textwrap.dedent("""\
*{command}* is used to delete a NAT on a Compute Engine router.
"""),
'EXAMPLES':
"""\
To delete NAT 'n1' in router 'r1', run:
$ {command} n1 --router=r1 --region=us-central1
""",
'API REFERENCE':
"""\
This command, when specified without alpha or beta, uses the compute/v1/routers API. The full documentation
for this API can be found at: https://cloud.google.com/compute/docs/reference/rest/v1/routers/
The beta command uses the compute/beta/routers API. The full documentation
for this API can be found at: https://cloud.google.com/compute/docs/reference/rest/beta/routers/
The alpha command uses the compute/alpha/routers API. Full documentation is not available for the alpha API.
"""
}

View File

@@ -0,0 +1,78 @@
# -*- coding: utf-8 -*- #
# Copyright 2018 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.
"""Command for describing a NAT in a Compute Engine router."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags as routers_flags
from googlecloudsdk.command_lib.compute.routers.nats import flags as nats_flags
from googlecloudsdk.command_lib.compute.routers.nats import nats_utils
class Describe(base.DescribeCommand):
"""Describe a NAT in a Compute Engine router."""
ROUTER_ARG = None
@classmethod
def Args(cls, parser):
cls.ROUTER_ARG = routers_flags.RouterArgumentForNat()
cls.ROUTER_ARG.AddArgument(parser)
compute_flags.AddRegionFlag(parser, 'NAT', operation_type='describe')
nats_flags.AddNatNameArg(parser, operation_type='describe')
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
router_ref = self.ROUTER_ARG.ResolveAsResource(args, holder.resources)
request = client.messages.ComputeRoutersGetRequest(**router_ref.AsDict())
router = client.MakeRequests([(client.apitools_client.routers, 'Get',
request)])[0]
return nats_utils.FindNatOrRaise(router, args.name)
Describe.detailed_help = {
'DESCRIPTION':
textwrap.dedent("""
*{command}* is used to describe a NAT in a Compute Engine router.
"""),
'EXAMPLES':
"""\
To describe NAT 'n1' in router 'r1', run:
$ {command} n1 --router=r1 --region=us-central1
""",
'API REFERENCE':
"""\
This command, when specified without alpha or beta, uses the compute/v1/routers API. The full documentation
for this API can be found at: https://cloud.google.com/compute/docs/reference/rest/v1/routers/
The beta command uses the compute/beta/routers API. The full documentation
for this API can be found at: https://cloud.google.com/compute/docs/reference/rest/beta/routers/
The alpha command uses the compute/alpha/routers API. Full documentation is not available for the alpha API.
"""
}

View File

@@ -0,0 +1,74 @@
# -*- coding: utf-8 -*- #
# Copyright 2018 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.
"""Command to list NATs on a Compute Engine router."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags as routers_flags
from googlecloudsdk.command_lib.compute.routers.nats import flags as nats_flags
class List(base.DescribeCommand):
"""Lists the NATs on a Compute Engine router."""
@classmethod
def Args(cls, parser):
cls.ROUTER_ARG = routers_flags.RouterArgumentForNat()
cls.ROUTER_ARG.AddArgument(parser)
parser.display_info.AddFormat(nats_flags.DEFAULT_LIST_FORMAT)
compute_flags.AddRegionFlag(parser, 'NATs', operation_type='list')
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
messages = holder.client.messages
service = holder.client.apitools_client.routers
router_ref = self.ROUTER_ARG.ResolveAsResource(args, holder.resources)
request_type = messages.ComputeRoutersGetRequest
router = service.Get(request_type(**router_ref.AsDict()))
return router.nats
List.detailed_help = {
'DESCRIPTION':
textwrap.dedent("""\
*{command}* is used to list the NATs on a Compute Engine router.
"""),
'EXAMPLES':
"""\
To list all NATs in router ``r1'' in region ``us-central1'', run:
$ {command} --router=r1 --region=us-central1.
""",
'API REFERENCE':
"""\
This command, when specified without alpha or beta, uses the compute/v1/routers API. The full documentation
for this API can be found at: https://cloud.google.com/compute/docs/reference/rest/v1/routers/
The beta command uses the compute/beta/routers API. The full documentation
for this API can be found at: https://cloud.google.com/compute/docs/reference/rest/beta/routers/
The alpha command uses the compute/alpha/routers API. Full documentation is not available for the alpha API.
"""
}

View File

@@ -0,0 +1,36 @@
# -*- coding: utf-8 -*- #
# Copyright 2020 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.
"""Commands for reading and manipulating rules in NATs."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
class Rules(base.Group):
"""List, create, update, describe, and delete Cloud NAT Rules."""
Rules.detailed_help = {
'DESCRIPTION':
"""
List, create, update, describe, and delete Cloud NAT Rules.
For more information about Cloud NAT, see the
[Cloud NAT documentation](https://cloud.google.com/nat/docs/using-nat).
""",
}

View File

@@ -0,0 +1,123 @@
# -*- coding: utf-8 -*- #
# Copyright 2020 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.
"""Command for adding a Rule to a Compute Engine NAT."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.compute.operations import poller
from googlecloudsdk.api_lib.util import waiter
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags as routers_flags
from googlecloudsdk.command_lib.compute.routers.nats import nats_utils
from googlecloudsdk.command_lib.compute.routers.nats.rules import flags as rules_flags
from googlecloudsdk.command_lib.compute.routers.nats.rules import rules_utils
from googlecloudsdk.core import log
from googlecloudsdk.core import resources
@base.ReleaseTracks(
base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA, base.ReleaseTrack.GA
)
class Create(base.CreateCommand):
"""Add a Rule to a Compute Engine NAT."""
@classmethod
def Args(cls, parser):
cls.ROUTER_ARG = routers_flags.RouterArgumentForNat()
cls.ROUTER_ARG.AddArgument(parser)
rules_flags.AddRuleNumberArg(parser, operation_type='create', plural=False)
rules_flags.AddNatNameArg(parser)
compute_flags.AddRegionFlag(parser, 'NAT', operation_type='create')
rules_flags.AddMatchArg(parser, required=True)
rules_flags.AddIpAndRangeArgsForCreate(parser)
base.ASYNC_FLAG.AddToParser(parser)
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
messages = holder.client.messages
service = holder.client.apitools_client.routers
router_ref = self.ROUTER_ARG.ResolveAsResource(args, holder.resources)
request_type = messages.ComputeRoutersGetRequest
router = service.Get(request_type(**router_ref.AsDict()))
rule_number = args.rule_number
nat_name = args.nat
existing_nat = nats_utils.FindNatOrRaise(router, nat_name)
rule = rules_utils.CreateRuleMessage(args, holder, existing_nat)
existing_nat.rules.append(rule)
result = service.Patch(
messages.ComputeRoutersPatchRequest(
project=router_ref.project,
region=router_ref.region,
router=router_ref.Name(),
routerResource=router))
operation_ref = resources.REGISTRY.Parse(
result.name,
collection='compute.regionOperations',
params={
'project': router_ref.project,
'region': router_ref.region,
})
if args.async_:
log.CreatedResource(
operation_ref,
kind='Rule [{0}] in NAT [{1}]'.format(rule_number, nat_name),
is_async=True,
details='Run the [gcloud compute operations describe] command '
'to check the status of this operation.')
return result
target_router_ref = holder.resources.Parse(
router_ref.Name(),
collection='compute.routers',
params={
'project': router_ref.project,
'region': router_ref.region,
})
operation_poller = poller.Poller(service, target_router_ref)
return waiter.WaitFor(
operation_poller, operation_ref,
'Creating Rule [{0}] in NAT [{1}]'.format(rule_number, nat_name))
Create.detailed_help = {
'DESCRIPTION':
"""
*{command}* is used to create a Rule on a Compute Engine NAT.
""",
'EXAMPLES':
"""\
Create a rule to use the IP Address address-1 to talk to destination IPs
in the CIDR Range "203.0.113.0/24".
$ {command} 1 --nat=my-nat --router=my-router --region=us-central1
--match='inIpRange(destination.ip, "203.0.113.0/24")'
--source-nat-active-ips=a1
"""
}

View File

@@ -0,0 +1,93 @@
# -*- coding: utf-8 -*- #
# Copyright 2020 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.
"""Command for removing a Rule from a Compute Engine NAT."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.compute import utils
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags as routers_flags
from googlecloudsdk.command_lib.compute.routers.nats import nats_utils
from googlecloudsdk.command_lib.compute.routers.nats.rules import flags as rules_flags
from googlecloudsdk.command_lib.compute.routers.nats.rules import rules_utils
class Delete(base.DeleteCommand):
"""Delete a Rule in a Compute Engine NAT."""
ROUTER_ARG = None
@classmethod
def Args(cls, parser):
cls.ROUTER_ARG = routers_flags.RouterArgumentForNat()
cls.ROUTER_ARG.AddArgument(parser)
rules_flags.AddRuleNumberArg(parser, plural=True)
rules_flags.AddNatNameArg(parser)
compute_flags.AddRegionFlag(
parser, 'NAT containing the Rule', operation_type='delete', plural=True)
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
router_ref = self.ROUTER_ARG.ResolveAsResource(args, holder.resources)
objects = client.MakeRequests([
(client.apitools_client.routers, 'Get',
client.messages.ComputeRoutersGetRequest(**router_ref.AsDict()))
])
router = objects[0]
nat_name = args.nat
rule_numbers = args.rule_number
nat = nats_utils.FindNatOrRaise(router, nat_name)
for rule_number in rule_numbers:
rule = rules_utils.FindRuleOrRaise(nat, rule_number)
nat.rules.remove(rule)
utils.PromptForDeletionHelper(
'Rule', ['{} in NAT {}'.format(args.rule_number, nat_name)])
return client.MakeRequests(
[self._GetPatchRequest(client, router_ref, router)])
def _GetPatchRequest(self, client, router_ref, router):
return (client.apitools_client.routers, 'Patch',
client.messages.ComputeRoutersPatchRequest(
router=router_ref.Name(),
routerResource=router,
region=router_ref.region,
project=router_ref.project))
Delete.detailed_help = {
'DESCRIPTION':
textwrap.dedent("""\
*{command}* is used to delete a Rule on a Compute Engine NAT.
"""),
'EXAMPLES':
"""\
To delete Rule 1 in NAT 'n1' in router 'r1', run:
$ {command} 1 --nat=n1 --router=r1 --region=us-central1
"""
}

View File

@@ -0,0 +1,76 @@
# -*- coding: utf-8 -*- #
# Copyright 2021 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.
"""Command for describing a Rule from a Compute Engine NAT."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags as routers_flags
from googlecloudsdk.command_lib.compute.routers.nats import nats_utils
from googlecloudsdk.command_lib.compute.routers.nats.rules import flags as rules_flags
from googlecloudsdk.command_lib.compute.routers.nats.rules import rules_utils
class Describe(base.DescribeCommand):
"""Describe a Rule in a Compute Engine NAT."""
ROUTER_ARG = None
@classmethod
def Args(cls, parser):
cls.ROUTER_ARG = routers_flags.RouterArgumentForNat()
cls.ROUTER_ARG.AddArgument(parser)
rules_flags.AddRuleNumberArg(parser)
rules_flags.AddNatNameArg(parser)
compute_flags.AddRegionFlag(
parser, 'NAT containing the Rule', operation_type='describe')
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
router_ref = self.ROUTER_ARG.ResolveAsResource(args, holder.resources)
router = client.MakeRequests([
(client.apitools_client.routers, 'Get',
client.messages.ComputeRoutersGetRequest(**router_ref.AsDict()))
])[0]
nat_name = args.nat
rule_number = args.rule_number
nat = nats_utils.FindNatOrRaise(router, nat_name)
return rules_utils.FindRuleOrRaise(nat, rule_number)
Describe.detailed_help = {
'DESCRIPTION':
textwrap.dedent("""\
*{command}* is used to describe a Rule on a Compute Engine NAT.
"""),
'EXAMPLES':
"""\
To describe Rule 1 in NAT 'n1' in router 'r1', run:
$ {command} 1 --nat=n1 --router=r1 --region=us-central1
"""
}

View File

@@ -0,0 +1,75 @@
# -*- coding: utf-8 -*- #
# Copyright 2020 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.
"""Command to list NATs on a Compute Engine router."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags as routers_flags
from googlecloudsdk.command_lib.compute.routers.nats import nats_utils
from googlecloudsdk.command_lib.compute.routers.nats.rules import flags as rules_flags
class List(base.DescribeCommand):
"""Lists the NATs on a Compute Engine router."""
@classmethod
def Args(cls, parser):
cls.ROUTER_ARG = routers_flags.RouterArgumentForNat()
cls.ROUTER_ARG.AddArgument(parser)
rules_flags.AddNatNameArg(parser)
parser.display_info.AddFormat(rules_flags.DEFAULT_LIST_FORMAT)
compute_flags.AddRegionFlag(
parser, 'NAT containing the Rules', operation_type='list')
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
messages = holder.client.messages
service = holder.client.apitools_client.routers
router_ref = self.ROUTER_ARG.ResolveAsResource(args, holder.resources)
request_type = messages.ComputeRoutersGetRequest
router = service.Get(request_type(**router_ref.AsDict()))
nat_name = args.nat
nat = nats_utils.FindNatOrRaise(router, nat_name)
return nat.rules
List.detailed_help = {
'DESCRIPTION':
textwrap.dedent("""\
*{command}* is used to list the Rule on a Compute Engine NAT.
"""),
'EXAMPLES':
"""\
To list all Rules in Nat ``n1'' in router ``r1'' in region ``us-central1'',
run:
$ {command} --nat=n1 --router=r1 --region=us-central1.
"""
}

View File

@@ -0,0 +1,125 @@
# -*- coding: utf-8 -*- #
# Copyright 2020 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.
"""Command for updating a Rule in a Compute Engine NAT."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.compute.operations import poller
from googlecloudsdk.api_lib.util import waiter
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags as routers_flags
from googlecloudsdk.command_lib.compute.routers.nats import nats_utils
from googlecloudsdk.command_lib.compute.routers.nats.rules import flags as rules_flags
from googlecloudsdk.command_lib.compute.routers.nats.rules import rules_utils
from googlecloudsdk.core import log
from googlecloudsdk.core import resources
@base.ReleaseTracks(
base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA, base.ReleaseTrack.GA
)
class Update(base.UpdateCommand):
"""Update a Rule in a Compute Engine NAT."""
@classmethod
def Args(cls, parser):
cls.ROUTER_ARG = routers_flags.RouterArgumentForNat()
cls.ROUTER_ARG.AddArgument(parser)
rules_flags.AddRuleNumberArg(parser, operation_type='update', plural=False)
rules_flags.AddNatNameArg(parser)
compute_flags.AddRegionFlag(
parser, 'NAT containing the Rule', operation_type='update'
)
rules_flags.AddMatchArg(parser, required=False)
rules_flags.AddIpAndRangeArgsForUpdate(parser)
base.ASYNC_FLAG.AddToParser(parser)
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
messages = holder.client.messages
service = holder.client.apitools_client.routers
router_ref = self.ROUTER_ARG.ResolveAsResource(args, holder.resources)
request_type = messages.ComputeRoutersGetRequest
router = service.Get(request_type(**router_ref.AsDict()))
rule_number = args.rule_number
nat_name = args.nat
nat = nats_utils.FindNatOrRaise(router, nat_name)
rule = rules_utils.FindRuleOrRaise(nat, rule_number)
rules_utils.UpdateRuleMessage(rule, args, holder, nat)
result = service.Patch(
messages.ComputeRoutersPatchRequest(
project=router_ref.project,
region=router_ref.region,
router=router_ref.Name(),
routerResource=router))
operation_ref = resources.REGISTRY.Parse(
result.name,
collection='compute.regionOperations',
params={
'project': router_ref.project,
'region': router_ref.region,
})
if args.async_:
log.UpdatedResource(
operation_ref,
kind='Rule [{0}] in NAT [{1}]'.format(rule_number, nat_name),
is_async=True,
details='Run the [gcloud compute operations describe] command '
'to check the status of this operation.')
return result
target_router_ref = holder.resources.Parse(
router_ref.Name(),
collection='compute.routers',
params={
'project': router_ref.project,
'region': router_ref.region,
})
operation_poller = poller.Poller(service, target_router_ref)
return waiter.WaitFor(
operation_poller, operation_ref,
'Updating Rule [{0}] in NAT [{1}]'.format(rule_number, nat_name))
Update.detailed_help = {
'DESCRIPTION':
"""
*{command}* is used to update a Rule in a Compute Engine NAT.
""",
'EXAMPLES':
"""\
To drain connections established using address-1 and use address-2 for
all new connections matching Rule 1 in NAT nat-1, run:
$ {command} 1 --nat=nat1 --router=my-router --region=us-central1
--source-nat-drain-ips=address-1
--source-nat-active-ips=address-2
"""
}

View File

@@ -0,0 +1,145 @@
# -*- coding: utf-8 -*- #
# Copyright 2018 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.
"""Command for updating a NAT on a Compute Engine router."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.compute.operations import poller
from googlecloudsdk.api_lib.util import waiter
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags as routers_flags
from googlecloudsdk.command_lib.compute.routers.nats import flags as nats_flags
from googlecloudsdk.command_lib.compute.routers.nats import nats_utils
from googlecloudsdk.core import log
from googlecloudsdk.core import resources
@base.UniverseCompatible
@base.ReleaseTracks(
base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA, base.ReleaseTrack.GA
)
class Update(base.UpdateCommand):
"""Update a NAT on a Compute Engine router."""
@classmethod
def Args(cls, parser):
cls.ROUTER_ARG = routers_flags.RouterArgumentForNat()
cls.ROUTER_ARG.AddArgument(parser)
base.ASYNC_FLAG.AddToParser(parser)
compute_flags.AddRegionFlag(parser, 'NAT', operation_type='create')
nats_flags.AddNatNameArg(parser, operation_type='create')
nats_flags.AddCommonNatArgs(parser, for_create=False)
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
messages = holder.client.messages
service = holder.client.apitools_client.routers
router_ref = self.ROUTER_ARG.ResolveAsResource(args, holder.resources)
request_type = messages.ComputeRoutersGetRequest
replacement = service.Get(request_type(**router_ref.AsDict()))
# Retrieve specified NAT and update base fields.
existing_nat = nats_utils.FindNatOrRaise(replacement, args.name)
nat = nats_utils.UpdateNatMessage(existing_nat, args, holder)
request_type = messages.ComputeRoutersPatchRequest
result = service.Patch(
request_type(
project=router_ref.project,
region=router_ref.region,
router=router_ref.Name(),
routerResource=replacement))
operation_ref = resources.REGISTRY.Parse(
result.name,
collection='compute.regionOperations',
params={
'project': router_ref.project,
'region': router_ref.region,
})
if args.async_:
log.UpdatedResource(
operation_ref,
kind='nat [{0}] in router [{1}]'.format(nat.name, router_ref.Name()),
is_async=True,
details='Run the [gcloud compute operations describe] command '
'to check the status of this operation.')
return result
target_router_ref = holder.resources.Parse(
router_ref.Name(),
collection='compute.routers',
params={
'project': router_ref.project,
'region': router_ref.region,
})
operation_poller = poller.Poller(service, target_router_ref)
return waiter.WaitFor(
operation_poller, operation_ref,
'Updating nat [{0}] in router [{1}]'.format(nat.name,
router_ref.Name()))
Update.detailed_help = {
'DESCRIPTION': """
*{command}* is used to update a NAT in a Compute Engine router.
""",
'EXAMPLES': """\
Change subnetworks and IP address resources associated with NAT:
$ {command} nat1 --router=my-router
--nat-external-ip-pool=ip-address2,ip-address3
--nat-custom-subnet-ip-ranges=subnet-2,subnet-3:secondary-range-2
Change minimum default ports allocated per VM associated with NAT:
$ {command} nat1 --router=my-router --min-ports-per-vm=128
Change connection timeouts associated with NAT:
$ {command} nat1 --router=my-router
--udp-idle-timeout=60s
--icmp-idle-timeout=60s
--tcp-established-idle-timeout=60s
--tcp-transitory-idle-timeout=60s
Reset connection timeouts associated NAT to default values:
$ {command} nat1 --router=my-router
--clear-udp-idle-timeout --clear-icmp-idle-timeout
--clear-tcp-established-idle-timeout
--clear-tcp-transitory-idle-timeout
""",
'API REFERENCE': """\
This command, when specified without alpha or beta, uses the compute/v1/routers API. The full documentation
for this API can be found at: https://cloud.google.com/compute/docs/reference/rest/v1/routers/
The beta command uses the compute/beta/routers API. The full documentation
for this API can be found at: https://cloud.google.com/compute/docs/reference/rest/beta/routers/
The alpha command uses the compute/alpha/routers API. Full documentation is not available for the alpha API.
""",
}

View File

@@ -0,0 +1,177 @@
# -*- 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.
"""Command for removing a BGP peer from a Compute Engine router."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import encoding
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute.routers import flags
from googlecloudsdk.core import exceptions
class PeerNotFoundError(exceptions.Error):
"""Raised when a peer is not found."""
def __init__(self, name_list):
error_msg = ('peer ' + ', '.join(
['%s'] * len(name_list))) % tuple(name_list) + ' not found'
super(PeerNotFoundError, self).__init__(error_msg)
@base.ReleaseTracks(base.ReleaseTrack.GA)
class RemoveBgpPeer(base.UpdateCommand):
"""Remove a BGP peer from a Compute Engine router.
*{command}* removes a BGP peer from a Compute Engine router.
"""
ROUTER_ARG = None
@classmethod
def _Args(cls, parser):
cls.ROUTER_ARG = flags.RouterArgument()
cls.ROUTER_ARG.AddArgument(parser, operation_type='update')
bgp_peer_parser = parser.add_mutually_exclusive_group(required=True)
# TODO(b/170227243): deprecate --peer-name after --peer-names hit GA
bgp_peer_parser.add_argument(
'--peer-name', help='The name of the peer being removed.')
bgp_peer_parser.add_argument(
'--peer-names',
type=arg_parsers.ArgList(),
metavar='PEER_NAME',
help='The list of names for peers being removed.')
@classmethod
def Args(cls, parser):
cls._Args(parser)
def GetGetRequest(self, client, router_ref):
return (client.apitools_client.routers, 'Get',
client.messages.ComputeRoutersGetRequest(
router=router_ref.Name(),
region=router_ref.region,
project=router_ref.project))
def GetSetRequest(self, client, router_ref, replacement):
return (client.apitools_client.routers, 'Patch',
client.messages.ComputeRoutersPatchRequest(
router=router_ref.Name(),
routerResource=replacement,
region=router_ref.region,
project=router_ref.project))
def Modify(self, args, existing, cleared_fields):
"""Mutate the router and record any cleared_fields for Patch request."""
replacement = encoding.CopyProtoMessage(existing)
input_remove_list = args.peer_names if args.peer_names else []
input_remove_list = input_remove_list + ([args.peer_name]
if args.peer_name else [])
actual_remove_list = []
replacement = encoding.CopyProtoMessage(existing)
existing_router = encoding.CopyProtoMessage(existing)
# remove peer if exists
md5_authentication_keys_to_remove = set()
for p in existing_router.bgpPeers:
if p.name in input_remove_list:
peer = p
if peer.md5AuthenticationKeyName is not None:
md5_authentication_keys_to_remove.add(peer.md5AuthenticationKeyName)
replacement.bgpPeers.remove(peer)
if not replacement.bgpPeers:
cleared_fields.append('bgpPeers')
actual_remove_list.append(peer.name)
if replacement.md5AuthenticationKeys:
replacement.md5AuthenticationKeys = [
md5_key for md5_key in replacement.md5AuthenticationKeys
if md5_key.name not in md5_authentication_keys_to_remove
]
if not replacement.md5AuthenticationKeys:
cleared_fields.append('md5AuthenticationKeys')
not_found_peers = list(set(input_remove_list) - set(actual_remove_list))
if not_found_peers:
raise PeerNotFoundError(not_found_peers)
return replacement
def _Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
router_ref = self.ROUTER_ARG.ResolveAsResource(args, holder.resources)
get_request = self.GetGetRequest(client, router_ref)
objects = client.MakeRequests([get_request])
# Cleared list fields need to be explicitly identified for Patch API.
cleared_fields = []
new_object = self.Modify(args, objects[0], cleared_fields)
with client.apitools_client.IncludeFields(cleared_fields):
# There is only one response because one request is made above
result = client.MakeRequests(
[self.GetSetRequest(client, router_ref, new_object)])
return result
def Run(self, args):
return self._Run(args)
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class RemoveBgpPeerBeta(RemoveBgpPeer):
"""Remove a BGP peer from a Compute Engine router."""
ROUTER_ARG = None
@classmethod
def Args(cls, parser):
cls._Args(parser)
def Run(self, args):
return self._Run(args)
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class RemoveBgpPeerAlpha(RemoveBgpPeerBeta):
"""Remove a BGP peer from a Compute Engine router."""
ROUTER_ARG = None
@classmethod
def Args(cls, parser):
cls._Args(parser)
def Run(self, args):
return self._Run(args)
RemoveBgpPeer.detailed_help = {
'DESCRIPTION':
"""
*{command}* removes a BGP peer from a Compute Engine router.
""",
}

View File

@@ -0,0 +1,132 @@
# -*- 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.
"""Command for removing an interface from a Compute Engine router."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import encoding
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute.routers import flags
from googlecloudsdk.core import exceptions
class InterfaceNotFoundError(exceptions.Error):
"""Raised when an interface is not found."""
def __init__(self, name_list):
error_msg = ('interface ' + ', '.join(
['%s'] * len(name_list))) % tuple(name_list) + ' not found'
super(InterfaceNotFoundError, self
).__init__(error_msg)
class RemoveInterface(base.UpdateCommand):
"""Remove an interface from a Compute Engine router.
*{command}* removes an interface from a Compute Engine router.
"""
ROUTER_ARG = None
@classmethod
def _Args(cls, parser):
cls.ROUTER_ARG = flags.RouterArgument()
cls.ROUTER_ARG.AddArgument(parser, operation_type='update')
interface_parser = parser.add_mutually_exclusive_group(required=True)
# TODO(b/170227243): deprecate --peer-name after --peer-names hit GA
interface_parser.add_argument(
'--interface-name', help='The name of the interface being removed.')
interface_parser.add_argument(
'--interface-names',
type=arg_parsers.ArgList(),
metavar='INTERFACE_NAME',
help='The list of names for interfaces being removed.')
@classmethod
def Args(cls, parser):
cls._Args(parser)
def GetGetRequest(self, client, router_ref):
return (client.apitools_client.routers,
'Get',
client.messages.ComputeRoutersGetRequest(
router=router_ref.Name(),
region=router_ref.region,
project=router_ref.project))
def GetSetRequest(self, client, router_ref, replacement):
return (client.apitools_client.routers,
'Patch',
client.messages.ComputeRoutersPatchRequest(
router=router_ref.Name(),
routerResource=replacement,
region=router_ref.region,
project=router_ref.project))
def Modify(self, args, existing, cleared_fields):
"""Mutate the router and record any cleared_fields for Patch request."""
input_remove_list = args.interface_names if args.interface_names else []
input_remove_list = input_remove_list + ([args.interface_name]
if args.interface_name else [])
# remove interface if exists
interface = None
acutal_remove_list = []
replacement = encoding.CopyProtoMessage(existing)
existing_router = encoding.CopyProtoMessage(existing)
for i in existing_router.interfaces:
if i.name in input_remove_list:
interface = i
replacement.interfaces.remove(interface)
if not replacement.interfaces:
cleared_fields.append('interfaces')
acutal_remove_list.append(interface.name)
not_found_interface = list(set(input_remove_list) - set(acutal_remove_list))
if not_found_interface:
raise InterfaceNotFoundError(not_found_interface)
return replacement
def _Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
router_ref = self.ROUTER_ARG.ResolveAsResource(args, holder.resources)
get_request = self.GetGetRequest(client, router_ref)
objects = client.MakeRequests([get_request])
# Cleared list fields need to be explicitly identified for Patch API.
cleared_fields = []
new_object = self.Modify(args, objects[0], cleared_fields)
with client.apitools_client.IncludeFields(cleared_fields):
# There is only one response because one request is made above
result = client.MakeRequests(
[self.GetSetRequest(client, router_ref, new_object)])
return result
def Run(self, args):
return self._Run(args)

View File

@@ -0,0 +1,67 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 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.
"""Command for removing a named set from a Compute Engine router."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags
@base.Hidden
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class RemoveNamedSet(base.DeleteCommand):
"""Remove a named set from a Compute Engine router.
*{command}* removes a named set from a Compute Engine router.
"""
ROUTER_ARG = None
@classmethod
def Args(cls, parser):
RemoveNamedSet.ROUTER_ARG = flags.RouterArgument()
RemoveNamedSet.ROUTER_ARG.AddArgument(parser, operation_type='delete')
parser.add_argument(
'--set-name',
help="""Name of the named set to be removed.""",
required=True,
)
def Run(self, args):
"""Issues the request necessary for removing a named set from a Router."""
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
router_ref = RemoveNamedSet.ROUTER_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client),
)
request = (
client.apitools_client.routers,
'DeleteNamedSet',
client.messages.ComputeRoutersDeleteNamedSetRequest(
**router_ref.AsDict(), namedSet=args.set_name
),
)
return client.MakeRequests([request])[0]

View File

@@ -0,0 +1,87 @@
# -*- coding: utf-8 -*- #
# Copyright 2025 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.
"""Command for adding an element to an existing named set of a Compute Engine router."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags
from googlecloudsdk.command_lib.compute.routers import route_policy_utils
@base.Hidden
@base.UniverseCompatible
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class RemoveNamedSetElement(base.UpdateCommand):
"""Remove an element from a named set of a Compute Engine router."""
ROUTER_ARG = None
@classmethod
def Args(cls, parser):
RemoveNamedSetElement.ROUTER_ARG = flags.RouterArgument()
RemoveNamedSetElement.ROUTER_ARG.AddArgument(
parser, operation_type='update'
)
parser.add_argument(
'--set-name',
help="""Name of the match set.""",
required=True,
)
parser.add_argument(
'--set-element',
help="""CEL expression for the element.""",
required=True,
)
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
messages = holder.client.messages
service = holder.client.apitools_client.routers
router_ref = RemoveNamedSetElement.ROUTER_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client),
)
named_set = service.GetNamedSet(
messages.ComputeRoutersGetNamedSetRequest(
**router_ref.AsDict(), namedSet=args.set_name
)
).resource
element = route_policy_utils.FindNamedSetElementOrRise(
resource=named_set, element_cel=args.set_element
)
named_set.elements.remove(element)
# Cleared list fields need to be explicitly identified for Patch API.
cleared_fields = [] if named_set.elements else ['elements']
request = (
service,
'PatchNamedSet',
messages.ComputeRoutersPatchNamedSetRequest(
**router_ref.AsDict(),
namedSet=named_set,
),
)
with client.apitools_client.IncludeFields(cleared_fields):
result = client.MakeRequests([request])
return result

View File

@@ -0,0 +1,81 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 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.
"""Command for removing a route policy from a Compute Engine router."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags
@base.UniverseCompatible
class RemoveRoutePolicy(base.DeleteCommand):
"""Remove a route policy from a Compute Engine router."""
ROUTER_ARG = None
@classmethod
def Args(cls, parser):
RemoveRoutePolicy.ROUTER_ARG = flags.RouterArgument()
RemoveRoutePolicy.ROUTER_ARG.AddArgument(parser, operation_type='delete')
parser.add_argument(
'--policy-name',
help="""Name of the route policy to be removed.""",
required=True,
)
def Run(self, args):
"""Issues the request necessary for removing a route policy from a Router.
Args:
args: contains arguments passed to the command.
Returns:
The result of patching the router removing the route policy.
"""
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
router_ref = RemoveRoutePolicy.ROUTER_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client),
)
request = (
client.apitools_client.routers,
'DeleteRoutePolicy',
client.messages.ComputeRoutersDeleteRoutePolicyRequest(
**router_ref.AsDict(), policy=args.policy_name
),
)
return client.MakeRequests([request])[0]
RemoveRoutePolicy.detailed_help = {
'DESCRIPTION': """\
*{command}* removes a route policy from a Compute Engine router.
""",
'EXAMPLES': """\
To remove a route policy `my-policy` from a router `my-router` in region `us-central1`, run:
$ {command} my-router --region=us-central1 --policy-name=my-policy
""",
}

View File

@@ -0,0 +1,98 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 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.
"""Command for removing a route policy term of a Compute Engine router."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags
from googlecloudsdk.command_lib.compute.routers import route_policy_utils
@base.UniverseCompatible
class RemoveRoutePolicyTerm(base.DeleteCommand):
"""Remove a route policy term of a Compute Engine router."""
ROUTER_ARG = None
@classmethod
def Args(cls, parser):
RemoveRoutePolicyTerm.ROUTER_ARG = flags.RouterArgument()
RemoveRoutePolicyTerm.ROUTER_ARG.AddArgument(
parser, operation_type='remove a route policy term from'
)
parser.add_argument(
'--policy-name',
help="""Name of the route policy from which the term should be removed.""",
required=True,
)
parser.add_argument(
'--priority',
help="""Order of the term within the policy.""",
required=True,
type=arg_parsers.BoundedInt(lower_bound=0, upper_bound=2147483647),
)
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
messages = holder.client.messages
service = holder.client.apitools_client.routers
router_ref = RemoveRoutePolicyTerm.ROUTER_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client),
)
route_policy = service.GetRoutePolicy(
messages.ComputeRoutersGetRoutePolicyRequest(
**router_ref.AsDict(), policy=args.policy_name
)
).resource
term = route_policy_utils.FindPolicyTermOrRaise(route_policy, args.priority)
route_policy.terms.remove(term)
# Cleared list fields need to be explicitly identified for Patch API.
cleared_fields = []
if not route_policy.terms:
cleared_fields.append('terms')
request = (
service,
'PatchRoutePolicy',
messages.ComputeRoutersPatchRoutePolicyRequest(
**router_ref.AsDict(),
routePolicy=route_policy,
),
)
with client.apitools_client.IncludeFields(cleared_fields):
result = client.MakeRequests([request])
return result
RemoveRoutePolicyTerm.detailed_help = {
'DESCRIPTION': """\
*{command}* removes a term of a route policy.
""",
'EXAMPLES': """\
To remove a route policy term with priority 0 from a route policy `my-policy` from a router `my-router` in region `us-central1`, run:
$ {command} my-router --region=us-central1 --policy-name=my-policy --priority=0
""",
}

View File

@@ -0,0 +1,233 @@
# -*- coding: utf-8 -*- #
# Copyright 2017 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.
"""Command for updating Compute Engine routers."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.compute import routers_utils
from googlecloudsdk.api_lib.compute.operations import poller
from googlecloudsdk.api_lib.util import waiter
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute.routers import flags
from googlecloudsdk.command_lib.compute.routers import router_utils
from googlecloudsdk.core import log
from googlecloudsdk.core import resources
from googlecloudsdk.core.console import console_io
@base.ReleaseTracks(base.ReleaseTrack.GA)
@base.UniverseCompatible
class Update(base.UpdateCommand):
"""Update a Compute Engine router."""
ROUTER_ARG = None
@classmethod
def _Args(cls, parser):
cls.ROUTER_ARG = flags.RouterArgument()
cls.ROUTER_ARG.AddArgument(parser, operation_type='update')
base.ASYNC_FLAG.AddToParser(parser)
flags.AddKeepaliveIntervalArg(parser)
flags.AddBgpIdentifierRangeArg(parser)
flags.AddAsnArg(parser)
flags.AddUpdateCustomAdvertisementArgs(parser, 'router')
@classmethod
def Args(cls, parser):
cls._Args(parser)
def _Run(self, args):
# Manually ensure replace/incremental flags are mutually exclusive.
router_utils.CheckIncompatibleFlagsOrRaise(args)
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
messages = holder.client.messages
service = holder.client.apitools_client.routers
router_ref = self.ROUTER_ARG.ResolveAsResource(args, holder.resources)
request_type = messages.ComputeRoutersGetRequest
replacement = service.Get(request_type(**router_ref.AsDict()))
replacement.bgp = replacement.bgp or messages.RouterBgp()
existing_mode = replacement.bgp.advertiseMode
if args.keepalive_interval is not None:
setattr(replacement.bgp, 'keepaliveInterval', args.keepalive_interval)
if args.bgp_identifier_range is not None:
setattr(replacement.bgp, 'identifierRange', args.bgp_identifier_range)
if args.asn is not None:
setattr(replacement.bgp, 'asn', args.asn)
if router_utils.HasReplaceAdvertisementFlags(args):
mode, groups, ranges = router_utils.ParseAdvertisements(
messages=messages, resource_class=messages.RouterBgp, args=args
)
router_utils.PromptIfSwitchToDefaultMode(
messages=messages,
resource_class=messages.RouterBgp,
existing_mode=existing_mode,
new_mode=mode,
)
attrs = {
'advertiseMode': mode,
'advertisedGroups': groups,
'advertisedIpRanges': ranges,
}
for attr, value in attrs.items():
if value is not None:
setattr(replacement.bgp, attr, value)
if router_utils.HasIncrementalAdvertisementFlags(args):
# This operation should only be run on custom mode routers.
router_utils.ValidateCustomMode(
messages=messages,
resource_class=messages.RouterBgp,
resource=replacement.bgp,
)
# These arguments are guaranteed to be mutually exclusive in args.
if args.add_advertisement_groups:
groups_to_add = routers_utils.ParseGroups(
resource_class=messages.RouterBgp,
groups=args.add_advertisement_groups,
)
replacement.bgp.advertisedGroups.extend(groups_to_add)
if args.remove_advertisement_groups:
groups_to_remove = routers_utils.ParseGroups(
resource_class=messages.RouterBgp,
groups=args.remove_advertisement_groups,
)
router_utils.RemoveGroupsFromAdvertisements(
messages=messages,
resource_class=messages.RouterBgp,
resource=replacement.bgp,
groups=groups_to_remove,
)
if args.add_advertisement_ranges:
ip_ranges_to_add = routers_utils.ParseIpRanges(
messages=messages, ip_ranges=args.add_advertisement_ranges
)
replacement.bgp.advertisedIpRanges.extend(ip_ranges_to_add)
if args.remove_advertisement_ranges:
router_utils.RemoveIpRangesFromAdvertisements(
messages=messages,
resource_class=messages.RouterBgp,
resource=replacement.bgp,
ip_ranges=args.remove_advertisement_ranges,
)
if router_utils.HasUpdateAdvertisementRangesFlags(args):
console_io.PromptContinue(
message=(
'Caution: You can specify custom advertised routes on a Cloud'
' Router and on a BGP session. Custom advertised routes on the'
' Cloud Router apply to all of its BGP sessions. However, if you'
' specify any custom advertised route on a BGP session, all of'
" the Cloud Router's advertised routes are ignored and overridden"
" by the BGP session's custom advertised route."
),
cancel_on_no=True,
)
# Cleared list fields need to be explicitly identified for Patch API.
cleared_fields = []
if not replacement.bgp.advertisedGroups:
cleared_fields.append('bgp.advertisedGroups')
if not replacement.bgp.advertisedIpRanges:
cleared_fields.append('bgp.advertisedIpRanges')
with holder.client.apitools_client.IncludeFields(cleared_fields):
request_type = messages.ComputeRoutersPatchRequest
result = service.Patch(
request_type(
project=router_ref.project,
region=router_ref.region,
router=router_ref.Name(),
routerResource=replacement,
)
)
operation_ref = resources.REGISTRY.Parse(
result.name,
collection='compute.regionOperations',
params={
'project': router_ref.project,
'region': router_ref.region,
},
)
if args.async_:
log.UpdatedResource(
operation_ref,
kind='router [{0}]'.format(router_ref.Name()),
is_async=True,
details=(
'Run the [gcloud compute operations describe] command '
'to check the status of this operation.'
),
)
return result
target_router_ref = holder.resources.Parse(
router_ref.Name(),
collection='compute.routers',
params={
'project': router_ref.project,
'region': router_ref.region,
},
)
operation_poller = poller.Poller(service, target_router_ref)
return waiter.WaitFor(
operation_poller,
operation_ref,
'Updating router [{0}]'.format(router_ref.Name()),
)
def Run(self, args):
"""See base.UpdateCommand."""
return self._Run(args)
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class UpdateBeta(Update):
"""Update a Compute Engine router."""
pass
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class UpdateAlpha(UpdateBeta):
"""Update a Compute Engine router."""
pass
Update.detailed_help = {
'DESCRIPTION': """
*{command}* is used to update a Compute Engine router.
""",
}

View File

@@ -0,0 +1,425 @@
# -*- 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.
"""Command for updating a BGP peer on a Compute Engine router."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.compute import routers_utils
from googlecloudsdk.api_lib.compute.operations import poller
from googlecloudsdk.api_lib.util import waiter
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute.routers import flags
from googlecloudsdk.command_lib.compute.routers import router_utils
from googlecloudsdk.core import log
from googlecloudsdk.core import resources
@base.ReleaseTracks(base.ReleaseTrack.GA)
@base.UniverseCompatible
class UpdateBgpPeer(base.UpdateCommand):
"""Update a BGP peer on a Compute Engine router."""
ROUTER_ARG = None
@classmethod
def _Args(cls, parser):
cls.ROUTER_ARG = flags.RouterArgument()
cls.ROUTER_ARG.AddArgument(parser)
base.ASYNC_FLAG.AddToParser(parser)
flags.AddBgpPeerArgs(
parser,
for_add_bgp_peer=False,
is_update=True,
)
flags.AddUpdateCustomAdvertisementArgs(parser, 'peer')
flags.AddUpdateCustomLearnedRoutesArgs(parser)
@classmethod
def Args(cls, parser):
cls._Args(parser)
def _Run(self, args, support_bfd_mode=False):
"""Runs the command.
Args:
args: contains arguments passed to the command.
support_bfd_mode: The flag to indicate whether bfd mode is supported.
Returns:
The result of patching the router updating the bgp peer with the
information provided in the arguments.
"""
# Manually ensure replace/incremental flags are mutually exclusive.
router_utils.CheckIncompatibleFlagsOrRaise(args)
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
messages = holder.client.messages
service = holder.client.apitools_client.routers
router_ref = self.ROUTER_ARG.ResolveAsResource(args, holder.resources)
request_type = messages.ComputeRoutersGetRequest
replacement = service.Get(request_type(**router_ref.AsDict()))
# Retrieve specified peer and update base fields.
peer = router_utils.FindBgpPeerOrRaise(replacement, args.peer_name)
md5_authentication_key_name = None
cleared_fields = []
if (
args.clear_md5_authentication_key
and peer.md5AuthenticationKeyName is not None
):
replacement.md5AuthenticationKeys = [
md5_authentication_key
for md5_authentication_key in replacement.md5AuthenticationKeys
if md5_authentication_key.name != peer.md5AuthenticationKeyName
]
if not replacement.md5AuthenticationKeys:
cleared_fields.append('md5AuthenticationKeys')
elif args.md5_authentication_key is not None:
if peer.md5AuthenticationKeyName is not None:
md5_authentication_key_name = peer.md5AuthenticationKeyName
for md5_authentication_key in replacement.md5AuthenticationKeys:
if md5_authentication_key.name == md5_authentication_key_name:
md5_authentication_key.key = args.md5_authentication_key
break
else:
md5_authentication_key_name = (
router_utils.GenerateMd5AuthenticationKeyName(replacement, args)
)
md5_authentication_key = messages.RouterMd5AuthenticationKey(
name=md5_authentication_key_name, key=args.md5_authentication_key
)
replacement.md5AuthenticationKeys.append(md5_authentication_key)
_UpdateBgpPeerMessage(
peer,
messages,
args,
md5_authentication_key_name=md5_authentication_key_name,
support_bfd_mode=support_bfd_mode,
)
if router_utils.HasReplaceAdvertisementFlags(args):
mode, groups, ranges = router_utils.ParseAdvertisements(
messages=messages, resource_class=messages.RouterBgpPeer, args=args
)
router_utils.PromptIfSwitchToDefaultMode(
messages=messages,
resource_class=messages.RouterBgpPeer,
existing_mode=peer.advertiseMode,
new_mode=mode,
)
attrs = {
'advertiseMode': mode,
'advertisedGroups': groups,
'advertisedIpRanges': ranges,
}
for attr, value in attrs.items():
if value is not None:
setattr(peer, attr, value)
if router_utils.HasIncrementalAdvertisementFlags(args):
# This operation should only be run on custom mode peers.
router_utils.ValidateCustomMode(
messages=messages,
resource_class=messages.RouterBgpPeer,
resource=peer,
)
# These arguments are guaranteed to be mutually exclusive in args.
if args.add_advertisement_groups:
groups_to_add = routers_utils.ParseGroups(
resource_class=messages.RouterBgpPeer,
groups=args.add_advertisement_groups,
)
peer.advertisedGroups.extend(groups_to_add)
if args.remove_advertisement_groups:
groups_to_remove = routers_utils.ParseGroups(
resource_class=messages.RouterBgpPeer,
groups=args.remove_advertisement_groups,
)
router_utils.RemoveGroupsFromAdvertisements(
messages=messages,
resource_class=messages.RouterBgpPeer,
resource=peer,
groups=groups_to_remove,
)
if args.add_advertisement_ranges:
ip_ranges_to_add = routers_utils.ParseIpRanges(
messages=messages, ip_ranges=args.add_advertisement_ranges
)
peer.advertisedIpRanges.extend(ip_ranges_to_add)
if args.remove_advertisement_ranges:
router_utils.RemoveIpRangesFromAdvertisements(
messages=messages,
resource_class=messages.RouterBgpPeer,
resource=peer,
ip_ranges=args.remove_advertisement_ranges,
)
if args.set_custom_learned_route_ranges is not None:
peer.customLearnedIpRanges = routers_utils.ParseCustomLearnedIpRanges(
messages=messages, ip_ranges=args.set_custom_learned_route_ranges
)
# These arguments are guaranteed to be mutually exclusive in args.
if args.add_custom_learned_route_ranges:
ip_ranges_to_add = routers_utils.ParseCustomLearnedIpRanges(
messages=messages, ip_ranges=args.add_custom_learned_route_ranges
)
peer.customLearnedIpRanges.extend(ip_ranges_to_add)
if args.remove_custom_learned_route_ranges:
router_utils.RemoveIpRangesFromCustomLearnedRoutes(
messages=messages,
peer=peer,
ip_ranges=args.remove_custom_learned_route_ranges,
)
request_type = messages.ComputeRoutersPatchRequest
with holder.client.apitools_client.IncludeFields(cleared_fields):
result = service.Patch(
request_type(
project=router_ref.project,
region=router_ref.region,
router=router_ref.Name(),
routerResource=replacement,
)
)
operation_ref = resources.REGISTRY.Parse(
result.name,
collection='compute.regionOperations',
params={
'project': router_ref.project,
'region': router_ref.region,
},
)
if args.async_:
log.UpdatedResource(
operation_ref,
kind='peer [{0}] in router [{1}]'.format(
peer.name, router_ref.Name()
),
is_async=True,
details=(
'Run the [gcloud compute operations describe] command '
'to check the status of this operation.'
),
)
return result
target_router_ref = holder.resources.Parse(
router_ref.Name(),
collection='compute.routers',
params={
'project': router_ref.project,
'region': router_ref.region,
},
)
operation_poller = poller.Poller(service, target_router_ref)
return waiter.WaitFor(
operation_poller,
operation_ref,
'Updating peer [{0}] in router [{1}]'.format(
peer.name, router_ref.Name()
),
)
def Run(self, args):
"""See base.UpdateCommand."""
return self._Run(args)
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class UpdateBgpPeerBeta(UpdateBgpPeer):
"""Update a BGP peer on a Compute Engine router."""
ROUTER_ARG = None
@classmethod
def Args(cls, parser):
cls._Args(parser)
def Run(self, args):
"""Runs the command.
Args:
args: contains arguments passed to the command.
Returns:
The result of patching the router updating the bgp peer with the
information provided in the arguments.
"""
return self._Run(args, support_bfd_mode=False)
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class UpdateBgpPeerAlpha(UpdateBgpPeerBeta):
"""Update a BGP peer on a Compute Engine router."""
ROUTER_ARG = None
@classmethod
def Args(cls, parser):
cls._Args(parser)
def Run(self, args):
"""Runs the command.
Args:
args: contains arguments passed to the command.
Returns:
The result of patching the router updating the bgp peer with the
information provided in the arguments.
"""
return self._Run(args, support_bfd_mode=True)
def _UpdateBgpPeerMessage(
peer,
messages,
args,
md5_authentication_key_name,
support_bfd_mode=False,
):
"""Updates base attributes of a BGP peer based on flag arguments."""
attrs = {
'interfaceName': args.interface,
'ipAddress': args.ip_address,
'peerIpAddress': args.peer_ip_address,
'peerAsn': args.peer_asn,
'advertisedRoutePriority': args.advertised_route_priority,
}
if args.enabled is not None:
if args.enabled:
attrs['enable'] = messages.RouterBgpPeer.EnableValueValuesEnum.TRUE
else:
attrs['enable'] = messages.RouterBgpPeer.EnableValueValuesEnum.FALSE
if args.enable_ipv6 is not None:
attrs['enableIpv6'] = args.enable_ipv6
if args.ipv6_nexthop_address is not None:
attrs['ipv6NexthopAddress'] = args.ipv6_nexthop_address
if args.peer_ipv6_nexthop_address is not None:
attrs['peerIpv6NexthopAddress'] = args.peer_ipv6_nexthop_address
if args.enable_ipv4 is not None:
attrs['enableIpv4'] = args.enable_ipv4
if args.ipv4_nexthop_address is not None:
attrs['ipv4NexthopAddress'] = args.ipv4_nexthop_address
if args.peer_ipv4_nexthop_address is not None:
attrs['peerIpv4NexthopAddress'] = args.peer_ipv4_nexthop_address
if args.custom_learned_route_priority is not None:
attrs['customLearnedRoutePriority'] = args.custom_learned_route_priority
if args.md5_authentication_key is not None:
attrs['md5AuthenticationKeyName'] = md5_authentication_key_name
attrs['exportPolicies'] = args.export_policies
attrs['importPolicies'] = args.import_policies
for attr, value in attrs.items():
if value is not None:
setattr(peer, attr, value)
if args.clear_md5_authentication_key:
peer.md5AuthenticationKeyName = None
if support_bfd_mode:
bfd = _UpdateBgpPeerBfdMessageMode(messages, peer, args)
else:
bfd = _UpdateBgpPeerBfdMessage(messages, peer, args)
if bfd is not None:
setattr(peer, 'bfd', bfd)
def _UpdateBgpPeerBfdMessage(messages, peer, args):
"""Updates BGP peer BFD messages based on flag arguments."""
if not (
args.IsSpecified('bfd_min_receive_interval')
or args.IsSpecified('bfd_min_transmit_interval')
or args.IsSpecified('bfd_session_initialization_mode')
or args.IsSpecified('bfd_multiplier')
):
return None
if peer.bfd is not None:
bfd = peer.bfd
else:
bfd = messages.RouterBgpPeerBfd()
attrs = {}
if args.bfd_session_initialization_mode is not None:
attrs['sessionInitializationMode'] = (
messages.RouterBgpPeerBfd.SessionInitializationModeValueValuesEnum(
args.bfd_session_initialization_mode
)
)
attrs['minReceiveInterval'] = args.bfd_min_receive_interval
attrs['minTransmitInterval'] = args.bfd_min_transmit_interval
attrs['multiplier'] = args.bfd_multiplier
for attr, value in attrs.items():
if value is not None:
setattr(bfd, attr, value)
return bfd
def _UpdateBgpPeerBfdMessageMode(messages, peer, args):
"""Updates BGP peer BFD messages based on flag arguments."""
if not (
args.IsSpecified('bfd_min_receive_interval')
or args.IsSpecified('bfd_min_transmit_interval')
or args.IsSpecified('bfd_session_initialization_mode')
or args.IsSpecified('bfd_multiplier')
):
return None
if peer.bfd is not None:
bfd = peer.bfd
else:
bfd = messages.RouterBgpPeerBfd()
attrs = {}
if args.bfd_session_initialization_mode is not None:
attrs['mode'] = messages.RouterBgpPeerBfd.ModeValueValuesEnum(
args.bfd_session_initialization_mode
)
attrs['sessionInitializationMode'] = (
messages.RouterBgpPeerBfd.SessionInitializationModeValueValuesEnum(
args.bfd_session_initialization_mode
)
)
attrs['minReceiveInterval'] = args.bfd_min_receive_interval
attrs['minTransmitInterval'] = args.bfd_min_transmit_interval
attrs['multiplier'] = args.bfd_multiplier
for attr, value in attrs.items():
if value is not None:
setattr(bfd, attr, value)
return bfd
UpdateBgpPeer.detailed_help = {
'DESCRIPTION': """
*{command}* is used to update a BGP peer on a Compute Engine
router.
""",
}

View File

@@ -0,0 +1,214 @@
# -*- 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.
"""Command for updating an interface on a Compute Engine router."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import encoding
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import parser_errors
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.interconnects.attachments import (
flags as attachment_flags,
)
from googlecloudsdk.command_lib.compute.routers import flags as router_flags
from googlecloudsdk.command_lib.compute.routers import router_utils
from googlecloudsdk.command_lib.compute.vpn_tunnels import (
flags as vpn_tunnel_flags,
)
from googlecloudsdk.core import log
@base.ReleaseTracks(base.ReleaseTrack.GA)
class UpdateInterface(base.UpdateCommand):
"""Update an interface on a Compute Engine router.
*{command}* is used to update an interface on a Compute Engine
router.
"""
ROUTER_ARG = None
VPN_TUNNEL_ARG = None
INTERCONNECT_ATTACHMENT_ARG = None
@classmethod
def _Args(cls, parser):
cls.ROUTER_ARG = router_flags.RouterArgument()
cls.ROUTER_ARG.AddArgument(parser, operation_type='update')
link_parser = parser.add_mutually_exclusive_group(required=False)
cls.VPN_TUNNEL_ARG = vpn_tunnel_flags.VpnTunnelArgumentForRouter(
required=False, operation_type='updated'
)
cls.VPN_TUNNEL_ARG.AddArgument(link_parser)
cls.INTERCONNECT_ATTACHMENT_ARG = (
attachment_flags.InterconnectAttachmentArgumentForRouter(
required=False, operation_type='updated'
)
)
cls.INTERCONNECT_ATTACHMENT_ARG.AddArgument(link_parser)
router_flags.AddInterfaceArgs(parser, for_update=True)
@classmethod
def Args(cls, parser):
return cls._Args(parser)
def GetGetRequest(self, client, router_ref):
return (
client.apitools_client.routers,
'Get',
client.messages.ComputeRoutersGetRequest(
router=router_ref.Name(),
region=router_ref.region,
project=router_ref.project,
),
)
def GetSetRequest(self, client, router_ref, replacement):
return (
client.apitools_client.routers,
'Patch',
client.messages.ComputeRoutersPatchRequest(
router=router_ref.Name(),
routerResource=replacement,
region=router_ref.region,
project=router_ref.project,
),
)
def Modify(self, client, resources, args, existing):
replacement = encoding.CopyProtoMessage(existing)
iface = None
for i in replacement.interfaces:
if i.name == args.interface_name:
iface = i
break
if iface is None:
raise router_utils.InterfaceNotFoundError(args.interface_name)
# Flags --ip-address and --mask-length must be specified together.
# TODO(b/65850105): Use an argument group for these flags.
if (args.ip_address is not None) and (args.mask_length is not None):
iface.ipRange = '{0}/{1}'.format(args.ip_address, args.mask_length)
elif (args.ip_address is not None) or (args.mask_length is not None):
raise router_utils.RequireIpAddressAndMaskLengthError()
if args.ip_version is not None:
iface.ipVersion = (
client.messages.RouterInterface.IpVersionValueValuesEnum(
args.ip_version
)
)
if not args.vpn_tunnel_region:
args.vpn_tunnel_region = replacement.region
if args.vpn_tunnel is not None:
vpn_ref = self.VPN_TUNNEL_ARG.ResolveAsResource(
args,
resources,
scope_lister=compute_flags.GetDefaultScopeLister(client),
)
iface.linkedVpnTunnel = vpn_ref.SelfLink()
if not args.interconnect_attachment_region:
args.interconnect_attachment_region = replacement.region
if args.interconnect_attachment is not None:
attachment_ref = self.INTERCONNECT_ATTACHMENT_ARG.ResolveAsResource(
args, resources
)
iface.linkedInterconnectAttachment = attachment_ref.SelfLink()
if (
iface.linkedVpnTunnel is not None
and iface.linkedInterconnectAttachment is not None
):
raise parser_errors.ArgumentException(
'cannot have both vpn-tunnel and interconnect-attachment for the '
'interface.'
)
return replacement
def _Run(self, args):
"""Issues requests necessary to update interfaces of the Router."""
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
router_ref = self.ROUTER_ARG.ResolveAsResource(args, holder.resources)
get_request = self.GetGetRequest(client, router_ref)
objects = client.MakeRequests([get_request])
new_object = self.Modify(
client,
holder.resources,
args,
objects[0],
)
# If existing object is equal to the proposed object or if
# Modify() returns None, then there is no work to be done, so we
# print the resource and return.
if objects[0] == new_object:
log.status.Print(
'No change requested; skipping update for [{0}].'.format(
objects[0].name
)
)
return objects
return client.MakeRequests(
[self.GetSetRequest(client, router_ref, new_object)]
)
def Run(self, args):
return self._Run(args)
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class UpdateInterfaceBeta(UpdateInterface):
"""Update an interface on a Compute Engine router.
*{command}* is used to update an interface on a Compute Engine
router.
"""
def Run(self, args):
return self._Run(args)
@classmethod
def Args(cls, parser):
return cls._Args(parser)
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class UpdateInterfaceAlpha(UpdateInterfaceBeta):
"""Update an interface on a Compute Engine router.
*{command}* is used to update an interface on a Compute Engine
router.
"""
pass

View File

@@ -0,0 +1,115 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 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.
"""Command for updating a route policy term of a Compute Engine router."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags
from googlecloudsdk.command_lib.compute.routers import route_policy_utils
@base.UniverseCompatible
class UpdateRoutePolicyTerm(base.UpdateCommand):
"""Updates a term of an existing route policy of a Comute Engine router."""
ROUTER_ARG = None
@classmethod
def Args(cls, parser):
UpdateRoutePolicyTerm.ROUTER_ARG = flags.RouterArgument()
UpdateRoutePolicyTerm.ROUTER_ARG.AddArgument(
parser, operation_type='update'
)
parser.add_argument(
'--policy-name',
help="""Name of the route policy to which the term should be updated.""",
required=True,
)
parser.add_argument(
'--priority',
help="""Order of the term within the policy.""",
required=True,
type=arg_parsers.BoundedInt(lower_bound=0, upper_bound=2147483647),
)
parser.add_argument(
'--match',
help="""CEL expression for matching a route.""",
required=True,
)
parser.add_argument(
'--actions',
help="""Semicolon separated CEL expressions for the actions to take when the rule matches.""",
required=True,
type=arg_parsers.ArgList(custom_delim_char=';'),
metavar='ACTION',
)
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
messages = holder.client.messages
service = holder.client.apitools_client.routers
router_ref = UpdateRoutePolicyTerm.ROUTER_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client),
)
route_policy = service.GetRoutePolicy(
messages.ComputeRoutersGetRoutePolicyRequest(
**router_ref.AsDict(), policy=args.policy_name
)
).resource
term = route_policy_utils.FindPolicyTermOrRaise(route_policy, args.priority)
_UpdatePolicyTermMessage(term, messages, args)
request = (
service,
'PatchRoutePolicy',
messages.ComputeRoutersPatchRoutePolicyRequest(
**router_ref.AsDict(),
routePolicy=route_policy,
),
)
return client.MakeRequests([request])[0]
def _UpdatePolicyTermMessage(term, messages, args):
term.match = messages.Expr(expression=args.match)
term.actions = [
messages.Expr(expression=cel_expression)
for cel_expression in args.actions
]
UpdateRoutePolicyTerm.detailed_help = {
'DESCRIPTION': """\
*{command}* updates a term of a route policy.
""",
# pylint: disable=line-too-long
'EXAMPLES': """\
To update a term with priority 128 with match `destination == '192.168.0.0/24'` and actions `med.set(12345);asPath.prependSequence([1, 2])` of a route policy `example-policy-name` of a router `example-router` in region `router-region`, run:
$ {command} example-router --region=router-region --policy-name=example-policy-name --priority=128 --match="destination == '192.168.0.0/24'" --actions="med.set(12345);asPath.prependSequence([1, 2])"
""",
# pylint: enable=line-too-long
}

View File

@@ -0,0 +1,137 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 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.
"""Command for uploading a named set into a Compute Engine router."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import base64
import json
import os
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.util import messages as messages_util
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags
from googlecloudsdk.core import yaml
from googlecloudsdk.core.util import files
@base.Hidden
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class UploadNamedSet(base.SilentCommand):
"""Upload a named set into a Compute Engine router.
*{command}* uploads a named set into a Compute Engine router.
"""
ROUTER_ARG = None
@classmethod
def Args(cls, parser):
UploadNamedSet.ROUTER_ARG = flags.RouterArgument()
UploadNamedSet.ROUTER_ARG.AddArgument(parser, operation_type='upload')
parser.add_argument(
'--set-name', help='Name of the named set to add/replace.'
)
parser.add_argument(
'--file-name',
required=True,
help='Local path to the file defining the named set',
)
parser.add_argument(
'--file-format',
choices=['json', 'yaml'],
help='Format of the file passed to --file-name',
)
def Run(self, args):
"""Issues the request necessary for uploading a named set into a Router.
Args:
args: contains arguments passed to the command.
Returns:
The result of patching the router uploading the named set.
"""
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
router_ref = UploadNamedSet.ROUTER_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client),
)
named_set = self.ParseNamedSetFromFile(
args.file_name, args.file_format, client.messages
)
self.EnsureSameSetName(args.set_name, named_set)
request = (
client.apitools_client.routers,
'UpdateNamedSet',
client.messages.ComputeRoutersUpdateNamedSetRequest(
**router_ref.AsDict(), namedSet=named_set
),
)
return client.MakeRequests([request])[0]
def EnsureSameSetName(self, set_name, named_set):
if set_name is not None and hasattr(named_set, 'name'):
if set_name != named_set.name:
raise exceptions.BadArgumentException(
'set-name',
'The set name provided [{0}] does not match the one from the'
' file [{1}]'.format(set_name, named_set.name),
)
if not hasattr(named_set, 'name') and set_name is not None:
named_set.name = set_name
def ParseNamedSetFromFile(self, input_file, file_format, messages):
# Get the imported named set config.
resource = self.ParseFile(input_file, file_format)
if 'resource' in resource:
resource = resource['resource']
named_set = messages_util.DictToMessageWithErrorCheck(
resource, messages.NamedSet
)
if 'fingerprint' in resource:
named_set.fingerprint = base64.b64decode(resource['fingerprint'])
return named_set
def ParseFile(self, input_file, file_format):
if os.path.isdir(input_file):
raise exceptions.BadFileException(
'[{0}] is a directory'.format(input_file)
)
if not os.path.isfile(input_file):
raise exceptions.BadFileException('No such file [{0}]'.format(input_file))
try:
with files.FileReader(input_file) as import_file:
if file_format == 'json':
return json.load(import_file)
return yaml.load(import_file)
except Exception as exp:
msg = (
'Unable to read named set config from specified file [{0}]'
' because {1}'.format(input_file, exp)
)
raise exceptions.BadFileException(msg)

View File

@@ -0,0 +1,145 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 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.
"""Command for uploading a route policy into a Compute Engine router."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import base64
import json
import os
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.util import messages as messages_util
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.routers import flags
from googlecloudsdk.core import yaml
from googlecloudsdk.core.util import files
@base.UniverseCompatible
class UploadRoutePolicy(base.SilentCommand):
"""Upload a route policy into a Compute Engine router."""
ROUTER_ARG = None
@classmethod
def Args(cls, parser):
UploadRoutePolicy.ROUTER_ARG = flags.RouterArgument()
UploadRoutePolicy.ROUTER_ARG.AddArgument(parser, operation_type='upload')
parser.add_argument(
'--policy-name', help='Name of the route policy to add/replace.'
)
parser.add_argument(
'--file-name',
required=True,
help='Local path to the file defining the policy',
)
parser.add_argument(
'--file-format',
choices=['json', 'yaml'],
help='Format of the file passed to --file-name',
)
def Run(self, args):
"""Issues the request necessary for uploading a route policy into a Router.
Args:
args: contains arguments passed to the command.
Returns:
The result of patching the router uploading the route policy.
"""
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
router_ref = UploadRoutePolicy.ROUTER_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client),
)
route_policy = self.ParseRoutePolicyFromFile(
args.file_name, args.file_format, client.messages
)
self.EnsureSamePolicyName(args.policy_name, route_policy)
request = (
client.apitools_client.routers,
'UpdateRoutePolicy',
client.messages.ComputeRoutersUpdateRoutePolicyRequest(
**router_ref.AsDict(), routePolicy=route_policy
),
)
return client.MakeRequests([request])[0]
def EnsureSamePolicyName(self, policy_name, route_policy):
if policy_name is not None and hasattr(route_policy, 'name'):
if policy_name != route_policy.name:
raise exceptions.BadArgumentException(
'policy-name',
'The policy name provided [{0}] does not match the one from the'
' file [{1}]'.format(policy_name, route_policy.name),
)
if not hasattr(route_policy, 'name') and policy_name is not None:
route_policy.name = policy_name
def ParseRoutePolicyFromFile(self, input_file, file_format, messages):
# Get the imported route policy config.
resource = self.ParseFile(input_file, file_format)
if 'resource' in resource:
resource = resource['resource']
route_policy = messages_util.DictToMessageWithErrorCheck(
resource, messages.RoutePolicy
)
if 'fingerprint' in resource:
route_policy.fingerprint = base64.b64decode(resource['fingerprint'])
return route_policy
def ParseFile(self, input_file, file_format):
if os.path.isdir(input_file):
raise exceptions.BadFileException(
'[{0}] is a directory'.format(input_file)
)
if not os.path.isfile(input_file):
raise exceptions.BadFileException('No such file [{0}]'.format(input_file))
try:
with files.FileReader(input_file) as import_file:
if file_format == 'json':
return json.load(import_file)
return yaml.load(import_file)
except Exception as exp:
msg = (
'Unable to read route policy config from specified file [{0}]'
' because {1}'.format(input_file, exp)
)
raise exceptions.BadFileException(msg)
UploadRoutePolicy.detailed_help = {
'DESCRIPTION': """\
*{command}* uploads a route policy into a Compute Engine router.
""",
'EXAMPLES': """\
To upload a route policy `my-import-policy` from a file `my-import-policy.yaml` into a router `my-router` in region `us-central1`, run:
$ {command} my-router --region=us-central1 --policy-name=my-import-policy --file-name=my-import-policy.yaml"
""",
}