# -*- 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. """Implements the command for copying files from and to virtual machines.""" 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 iap_tunnel from googlecloudsdk.command_lib.compute import scp_utils from googlecloudsdk.command_lib.compute import ssh_utils from googlecloudsdk.command_lib.util.ssh import ip def _DetailedHelp(): """Generate detailed help for each version.""" detailed_help = { 'brief': ('Copy files to and from Google Compute Engine ' 'virtual machines via scp.'), 'DESCRIPTION': """ *{command}* securely copies files between a virtual machine instance and your local machine using the scp command. This command works for Linux VMs and Windows Server 2019 and later VMs that have [SSH enabled](https://cloud.google.com/compute/docs/connect/windows-ssh). In order to set up a successful transfer, follow these guidelines: * Prefix remote file names with the virtual machine instance name (e.g., _example-instance_:~/_FILE_). * Local file names can be used as is (e.g., ~/_FILE_). * File names containing a colon (``:'') must be invoked by either their absolute path or a path that begins with ``./''. * When the destination of your transfer is local, all source files must be from the same virtual machine. * When the destination of your transfer is remote instead, all sources must be local. * When the destination is Windows Server, the source must be using a similar SSH version. Under the covers, *scp(1)* is used to facilitate the transfer. If the `--region` and `--network` flags are provided, then `--plain` and `--tunnel-through-iap` are implied and any remote file names must be prefixed with the remote IP address instead of the instance name. This is most useful for connecting to on-prem resources.""", 'EXAMPLES': """ To copy a remote directory, `~/narnia`, from ``example-instance'' to the `~/wardrobe` directory of your local host, run: $ {command} --recurse example-instance:~/narnia ~/wardrobe Conversely, files from your local computer can be copied to a virtual machine: $ {command} ~/localtest.txt ~/localtest2.txt example-instance:~/narnia Remote Windows-based virtual machines require you to provide a path using backslash notation: $ {command} ~/localtest.txt ~/localtest2.txt example-windows-instance:"C:\\Users\\Public" Paths for remote Windows-based virtual machines which contain spaces in directory name should be appropriately protected with a pair of nested single and double quotes: $ {command} ~/localtest.txt 'example-windows-instance:"C:\\Users\\Public\\Test Folder"' If the zone cannot be determined, you will be prompted for it. Use the `--zone` flag to avoid being prompted: $ {command} --recurse example-instance:~/narnia ~/wardrobe --zone=us-central1-a To specify the project, zone, and recurse all together, run: $ {command} --project="my-gcp-project" --zone="us-east1-b" --recurse ~/foo-folder/ gcp-instance-name:~/ You can limit the allowed time to ssh. For example, to allow a key to be used through 2019: $ {command} --recurse example-instance:~/narnia ~/wardrobe --ssh-key-expiration="2020-01-01T00:00:00:00Z" Or alternatively, allow access for the next two minutes: $ {command} --recurse example-instance:~/narnia ~/wardrobe --ssh-key-expire-after=2m To use the IP address of your remote VM (eg, for on-prem), you must also specify the `--region` and `--network` flags: $ {command} 10.1.2.3:~/narnia ~/wardrobe --region=us-central1 --network=default """, } return detailed_help @base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA, base.ReleaseTrack.GA) @base.DefaultUniverseOnly class Scp(base.Command): """Copy files to and from Google Compute Engine virtual machines via scp.""" category = base.TOOLS_CATEGORY @classmethod def Args(cls, parser): """Set up arguments for this command. Args: parser: An argparse.ArgumentParser. """ scp_utils.BaseScpHelper.Args(parser) parser.add_argument('--port', help='The port to connect to.') parser.add_argument( '--recurse', action='store_true', help='Upload directories recursively.') parser.add_argument( '--compress', action='store_true', help='Enable compression.') parser.add_argument( '--scp-flag', action='append', help='Extra flag to be sent to scp. This flag may be repeated.') ssh_utils.AddVerifyInternalIpArg(parser) routing_group = parser.add_mutually_exclusive_group() routing_group.add_argument( '--internal-ip', default=False, action='store_true', help="""\ Connect to instances using their internal IP addresses rather than their external IP addresses. Use this to connect from one instance to another on the same VPC network, over a VPN connection, or between two peered VPC networks. For this connection to work, you must configure your networks and firewall to allow SSH connections to the internal IP address of the instance to which you want to connect. To learn how to use this flag, see [](https://cloud.google.com/compute/docs/instances/connecting-advanced#sshbetweeninstances). """) iap_tunnel.AddSshTunnelArgs(parser, routing_group) iap_tunnel.AddHostBasedTunnelArgs(parser) def Run(self, args): """See scp_utils.BaseScpCommand.Run.""" on_prem = (args.IsSpecified('network') and args.IsSpecified('region')) if on_prem: args.plain = True if args.internal_ip: ip_type = ip.IpTypeEnum.INTERNAL else: ip_type = ip.IpTypeEnum.EXTERNAL holder = base_classes.ComputeApiHolder(self.ReleaseTrack()) scp_helper = scp_utils.BaseScpHelper() extra_flags = [] # TODO(b/33467618): Add -C to SCPCommand if args.scp_flag: extra_flags.extend(args.scp_flag) return scp_helper.RunScp( holder, args, on_prem=on_prem, port=args.port, recursive=args.recurse, compress=args.compress, extra_flags=extra_flags, release_track=self.ReleaseTrack(), ip_type=ip_type) Scp.detailed_help = _DetailedHelp()