def _GetSSHOptions(self, name, ssh_helper, instance, host_keys): options = ssh_helper.GetConfig(ssh_utils.HostKeyAlias(instance), strict_host_key_checking='no', host_keys_to_add=host_keys) os.environ['TPU_NAME'] = name options['SendEnv'] = 'TPU_NAME' return options
def RunSSHCommandToInstance(command_list, instance, user, args, ssh_helper, explicit_output_file=None, explicit_error_file=None, dry_run=False): """Runs a SSH command to a Google Compute Engine VM. Args: command_list: List with the ssh command to run. instance: The GCE VM object. user: The user to be used for the SSH command. args: The args used to call the gcloud instance. ssh_helper: ssh_utils.BaseSSHCLIHelper instance initialized for the command. explicit_output_file: Use this file for piping stdout of the SSH command, instead of using stdout. This is useful for capturing the command and analyzing it. explicit_error_file: Use this file for piping stdout of the SSH command, instead of using stdout. This is useful for capturing the command and analyzing it. dry_run: Whether or not this is a dry-run (only print, not run). Returns: The exit code of the SSH command Raises: ssh.CommandError: there was an error running a SSH command """ external_ip_address = ssh_utils.GetExternalIPAddress(instance) remote = ssh.Remote(external_ip_address, user) identity_file = None options = None if not args.plain: identity_file = ssh_helper.keys.key_file options = ssh_helper.GetConfig(ssh_utils.HostKeyAlias(instance), args.strict_host_key_checking) extra_flags = ssh.ParseAndSubstituteSSHFlags(args, remote, user) remainder = [] remote_command = containers.GetRemoteCommand(None, command_list) cmd = ssh.SSHCommand(remote, identity_file=identity_file, options=options, extra_flags=extra_flags, remote_command=remote_command, remainder=remainder) if dry_run: DryRunLog(' '.join(cmd.Build(ssh_helper.env))) return 0 return_code = cmd.Run(ssh_helper.env, force_connect=True, explicit_output_file=explicit_output_file, explicit_error_file=explicit_error_file) log.out.flush() return return_code
def Run(self, args): super(SshGA, self).Run(args) user, instance_name = ssh_utils.GetUserAndInstance( args.user_host, self._use_accounts_service, self.http) instance_ref = instance_flags.SSH_INSTANCE_RESOLVER.ResolveResources( [instance_name], compute_scope.ScopeEnum.ZONE, args.zone, self.resources, scope_lister=flags.GetDefaultScopeLister(self.compute_client))[0] instance = self.GetInstance(instance_ref) external_ip_address = ssh_utils.GetExternalIPAddress(instance) ssh_args = [self.env.ssh] if not args.plain: ssh_args.extend(ssh.GetDefaultFlags(self.keys.key_file)) # Allocates a tty if no command was provided and a container was provided. if args.container and not args.command: ssh_args.append('-t') if args.ssh_flag: for flag in args.ssh_flag: for flag_part in flag.split(): # We want grouping here dereferenced_flag = (flag_part.replace( '%USER%', user).replace('%INSTANCE%', external_ip_address)) ssh_args.append(dereferenced_flag) host_key_alias = ssh_utils.HostKeyAlias(instance) ssh_args.extend( ssh.GetHostKeyArgs(host_key_alias, args.plain, args.strict_host_key_checking)) ssh_args.append(ssh.UserHost(user, external_ip_address)) if args.ssh_args: ssh_args.extend(args.ssh_args) if args.container: ssh_args.append('--') ssh_args.append('container_exec') ssh_args.append(args.container) # Runs the given command inside the given container if --command was # specified, otherwise runs /bin/sh. if args.command: ssh_args.append(args.command) else: ssh_args.append('/bin/sh') elif args.command: if not platforms.OperatingSystem.IsWindows(): ssh_args.append('--') ssh_args.append(args.command) # Don't use strict error checking for ssh: if the executed command fails, we # don't want to consider it an error. We do, however, want to propagate its # return code. return_code = self.ActuallyRun( args, ssh_args, user, instance, instance_ref.project, external_ip_address, strict_error_checking=False, use_account_service=self._use_accounts_service) if return_code: # Can't raise an exception because we don't want any "ERROR" message # printed; the output from `ssh` will be enough. sys.exit(return_code)
def Run(self, args): """See ssh_utils.BaseSSHCLICommand.Run.""" holder = base_classes.ComputeApiHolder(self.ReleaseTrack()) client = holder.client ssh_helper = ssh_utils.BaseSSHCLIHelper() ssh_helper.Run(args) user, instance_name = ssh_utils.GetUserAndInstance(args.user_host) instance_ref = instance_flags.SSH_INSTANCE_RESOLVER.ResolveResources( [instance_name], compute_scope.ScopeEnum.ZONE, args.zone, holder.resources, scope_lister=instance_flags.GetInstanceZoneScopeLister(client))[0] instance = ssh_helper.GetInstance(client, instance_ref) project = ssh_helper.GetProject(client, instance_ref.project) if args.plain: use_oslogin = False else: user, use_oslogin = ssh_helper.CheckForOsloginAndGetUser( instance, project, user, self.ReleaseTrack()) if self._use_internal_ip: ip_address = ssh_utils.GetInternalIPAddress(instance) else: ip_address = ssh_utils.GetExternalIPAddress(instance) remote = ssh.Remote(ip_address, user) identity_file = None options = None if not args.plain: identity_file = ssh_helper.keys.key_file options = ssh_helper.GetConfig(ssh_utils.HostKeyAlias(instance), args.strict_host_key_checking) extra_flags = ssh.ParseAndSubstituteSSHFlags(args, remote, ip_address) remainder = [] if args.ssh_args: remainder.extend(args.ssh_args) # Transform args.command into arg list or None if no command command_list = args.command.split(' ') if args.command else None tty = containers.GetTty(args.container, command_list) remote_command = containers.GetRemoteCommand(args.container, command_list) cmd = ssh.SSHCommand(remote, identity_file=identity_file, options=options, extra_flags=extra_flags, remote_command=remote_command, tty=tty, remainder=remainder) if args.dry_run: log.out.Print(' '.join(cmd.Build(ssh_helper.env))) return if args.plain or use_oslogin: keys_newly_added = False else: keys_newly_added = ssh_helper.EnsureSSHKeyExists( client, remote.user, instance, project) if keys_newly_added: poller = ssh.SSHPoller( remote, identity_file=identity_file, options=options, extra_flags=extra_flags, max_wait_ms=ssh_utils.SSH_KEY_PROPAGATION_TIMEOUT_SEC) log.status.Print('Waiting for SSH key to propagate.') # TODO(b/35355795): Don't force_connect try: poller.Poll(ssh_helper.env, force_connect=True) except retry.WaitException: raise ssh_utils.NetworkError() if self._use_internal_ip: ssh_helper.PreliminarilyVerifyInstance(instance.id, remote, identity_file, options) return_code = cmd.Run(ssh_helper.env, force_connect=True) if return_code: # Can't raise an exception because we don't want any "ERROR" message # printed; the output from `ssh` will be enough. sys.exit(return_code)
def Run(self, args): """See ssh_utils.BaseSSHCLICommand.Run.""" on_prem = ( args.IsKnownAndSpecified('network') and args.IsKnownAndSpecified('region')) if on_prem: args.plain = True # These two lines are needed to ensure reauth is performed as needed, even # for on-prem, which doesn't use the resulting variables. holder = base_classes.ComputeApiHolder(self.ReleaseTrack()) client = holder.client ssh_helper = ssh_utils.BaseSSHCLIHelper() ssh_helper.Run(args) oslogin_state = ssh.OsloginState() if on_prem: user, ip = ssh_utils.GetUserAndInstance(args.user_host) remote = ssh.Remote(ip, user) iap_tunnel_args = iap_tunnel.CreateOnPremSshTunnelArgs( args, self.ReleaseTrack(), ip) instance_address = ip internal_address = ip else: user, instance_name = ssh_utils.GetUserAndInstance(args.user_host) instance_ref = instance_flags.SSH_INSTANCE_RESOLVER.ResolveResources( [instance_name], compute_scope.ScopeEnum.ZONE, args.zone, holder.resources, scope_lister=instance_flags.GetInstanceZoneScopeLister(client))[0] instance = ssh_helper.GetInstance(client, instance_ref) project = ssh_helper.GetProject(client, instance_ref.project) host_keys = ssh_helper.GetHostKeysFromGuestAttributes( client, instance_ref, instance, project) iap_tunnel_args = iap_tunnel.CreateSshTunnelArgs( args, self.ReleaseTrack(), instance_ref, ssh_utils.GetExternalInterface(instance, no_raise=True)) internal_address = ssh_utils.GetInternalIPAddress(instance) if args.troubleshoot: log.status.Print(TROUBLESHOOT_HEADER.format( instance_ref, args.zone or instance_ref.zone, datetime.datetime.now() )) RunTroubleshooting(project, args.zone or instance_ref.zone, instance, iap_tunnel_args) return if not host_keys and host_keys is not None: log.debug('Unable to retrieve host keys from instance metadata. ' 'Continuing.') expiration, expiration_micros = ssh_utils.GetSSHKeyExpirationFromArgs( args) if args.plain: oslogin_state.oslogin_enabled = False else: public_key = ssh_helper.keys.GetPublicKey().ToEntry( include_comment=True) # If there is an '@' symbol in the user_host arg, the user is requesting # to connect as a specific user. This may get overridden by OS Login. username_requested = '@' in args.user_host oslogin_state = ssh.GetOsloginState( instance, project, user, public_key, expiration_micros, self.ReleaseTrack(), username_requested=username_requested) user = oslogin_state.user log.debug(oslogin_state) if iap_tunnel_args: # IAP Tunnel only uses instance_address for the purpose of --ssh-flag # substitution. In this case, dest_addr doesn't do much, it just matches # against entries in the user's ssh_config file. It's best to use # something unique to avoid false positive matches, thus we use # HostKeyAlias. instance_address = internal_address dest_addr = ssh_utils.HostKeyAlias(instance) elif args.internal_ip: instance_address = internal_address dest_addr = instance_address else: instance_address = ssh_utils.GetExternalIPAddress(instance) dest_addr = instance_address remote = ssh.Remote(dest_addr, user) # identity_file_list will be None if security keys are not enabled. identity_file_list = ssh.WriteSecurityKeys(oslogin_state) identity_file = None options = None if not args.plain: if not identity_file_list: identity_file = ssh_helper.keys.key_file options = ssh_helper.GetConfig(ssh_utils.HostKeyAlias(instance), args.strict_host_key_checking, host_keys_to_add=host_keys) extra_flags = ssh.ParseAndSubstituteSSHFlags(args, remote, instance_address, internal_address) remainder = [] if args.ssh_args: remainder.extend(args.ssh_args) # Transform args.command into arg list or None if no command command_list = args.command.split(' ') if args.command else None tty = containers.GetTty(args.container, command_list) remote_command = containers.GetRemoteCommand(args.container, command_list) # Do not include default port since that will prevent users from # specifying a custom port (b/121998342). ssh_cmd_args = {'remote': remote, 'identity_file': identity_file, 'options': options, 'extra_flags': extra_flags, 'remote_command': remote_command, 'tty': tty, 'iap_tunnel_args': iap_tunnel_args, 'remainder': remainder, 'identity_list': identity_file_list} cmd = ssh.SSHCommand(**ssh_cmd_args) if args.dry_run: # Add quotes around any arguments that contain spaces. log.out.Print(' '.join('"{0}"'.format(arg) if ' ' in arg else arg for arg in cmd.Build(ssh_helper.env))) return # Raise errors if instance requires a security key but the local # envionment doesn't support them. This is after the 'dry-run' because # we want to allow printing the command regardless. if self.enable_security_keys: ssh_utils.ConfirmSecurityKeyStatus(oslogin_state) if args.plain or oslogin_state.oslogin_enabled: keys_newly_added = False else: keys_newly_added = ssh_helper.EnsureSSHKeyExists( client, remote.user, instance, project, expiration=expiration) if keys_newly_added: poller = ssh_utils.CreateSSHPoller(remote, identity_file, options, iap_tunnel_args, extra_flags=extra_flags) log.status.Print('Waiting for SSH key to propagate.') # TODO(b/35355795): Don't force_connect try: poller.Poll( ssh_helper.env, force_connect=properties.VALUES.ssh.putty_force_connect.GetBool()) except retry.WaitException: raise ssh_utils.NetworkError() if args.internal_ip and not on_prem: ssh_helper.PreliminarilyVerifyInstance(instance.id, remote, identity_file, options) # Errors from SSH itself result in an ssh.CommandError being raised try: return_code = cmd.Run( ssh_helper.env, force_connect=properties.VALUES.ssh.putty_force_connect.GetBool()) except ssh.CommandError as e: if not on_prem: log.status.Print(self.createRecommendMessage(args, instance_name, instance_ref, project)) raise e if return_code: # This is the return code of the remote command. Problems with SSH itself # will result in ssh.CommandError being raised above. sys.exit(return_code)
def Run(self, args): """See ssh_utils.BaseSSHCLICommand.Run.""" holder = base_classes.ComputeApiHolder(self.ReleaseTrack()) client = holder.client ssh_helper = ssh_utils.BaseSSHCLIHelper() ssh_helper.Run(args) user, instance_name = ssh_utils.GetUserAndInstance(args.user_host) instance_ref = instance_flags.SSH_INSTANCE_RESOLVER.ResolveResources( [instance_name], compute_scope.ScopeEnum.ZONE, args.zone, holder.resources, scope_lister=instance_flags.GetInstanceZoneScopeLister(client))[0] instance = ssh_helper.GetInstance(client, instance_ref) project = ssh_helper.GetProject(client, instance_ref.project) if self.get_host_keys: host_keys = ssh_helper.GetHostKeysFromGuestAttributes( client, instance_ref) if not host_keys: log.warning('Unable to retrieve host keys from instance metadata. ' 'Continuing.') else: host_keys = {} expiration, expiration_micros = ssh_utils.GetSSHKeyExpirationFromArgs(args) if args.plain: use_oslogin = False else: public_key = ssh_helper.keys.GetPublicKey().ToEntry(include_comment=True) user, use_oslogin = ssh.CheckForOsloginAndGetUser( instance, project, user, public_key, expiration_micros, self.ReleaseTrack()) iap_tunnel_args = iap_tunnel.SshTunnelArgs.FromArgs( args, self.ReleaseTrack(), instance_ref, ssh_utils.GetExternalInterface(instance, no_raise=True)) internal_address = ssh_utils.GetInternalIPAddress(instance) if iap_tunnel_args: # IAP Tunnel only uses instance_address for the purpose of --ssh-flag # substitution. In this case, dest_addr doesn't do much, it just matches # against entries in the user's ssh_config file. It's best to use # something unique to avoid false positive matches, thus we use # HostKeyAlias. instance_address = internal_address dest_addr = ssh_utils.HostKeyAlias(instance) elif args.internal_ip: instance_address = internal_address dest_addr = instance_address else: instance_address = ssh_utils.GetExternalIPAddress(instance) dest_addr = instance_address remote = ssh.Remote(dest_addr, user) identity_file = None options = None if not args.plain: identity_file = ssh_helper.keys.key_file options = ssh_helper.GetConfig(ssh_utils.HostKeyAlias(instance), args.strict_host_key_checking, host_keys_to_add=host_keys) extra_flags = ssh.ParseAndSubstituteSSHFlags(args, remote, instance_address, internal_address) remainder = [] if args.ssh_args: remainder.extend(args.ssh_args) # Transform args.command into arg list or None if no command command_list = args.command.split(' ') if args.command else None tty = containers.GetTty(args.container, command_list) remote_command = containers.GetRemoteCommand(args.container, command_list) # Do not include default port since that will prevent users from # specifying a custom port (b/121998342). ssh_cmd_args = {'remote': remote, 'identity_file': identity_file, 'options': options, 'extra_flags': extra_flags, 'remote_command': remote_command, 'tty': tty, 'iap_tunnel_args': iap_tunnel_args, 'remainder': remainder} cmd = ssh.SSHCommand(**ssh_cmd_args) if args.dry_run: log.out.Print(' '.join(cmd.Build(ssh_helper.env))) return if args.plain or use_oslogin: keys_newly_added = False else: keys_newly_added = ssh_helper.EnsureSSHKeyExists( client, remote.user, instance, project, expiration=expiration) if keys_newly_added: poller = ssh_utils.CreateSSHPoller(remote, identity_file, options, iap_tunnel_args, extra_flags=extra_flags) log.status.Print('Waiting for SSH key to propagate.') # TODO(b/35355795): Don't force_connect try: poller.Poll(ssh_helper.env, force_connect=True) except retry.WaitException: raise ssh_utils.NetworkError() if args.internal_ip: ssh_helper.PreliminarilyVerifyInstance(instance.id, remote, identity_file, options) # Errors from SSH itself result in an ssh.CommandError being raised return_code = cmd.Run(ssh_helper.env, force_connect=True) if return_code: # This is the return code of the remote command. Problems with SSH itself # will result in ssh.CommandError being raised above. sys.exit(return_code)
def Run(self, args): """See ssh_utils.BaseSSHCLICommand.Run.""" holder = base_classes.ComputeApiHolder(self.ReleaseTrack()) client = holder.client ssh_helper = ssh_utils.BaseSSHCLIHelper() ssh_helper.Run(args) user, instance_name = ssh_utils.GetUserAndInstance(args.user_host) instance_ref = instance_flags.SSH_INSTANCE_RESOLVER.ResolveResources( [instance_name], compute_scope.ScopeEnum.ZONE, args.zone, holder.resources, scope_lister=instance_flags.GetInstanceZoneScopeLister(client))[0] instance = ssh_helper.GetInstance(client, instance_ref) project = ssh_helper.GetProject(client, instance_ref.project) if args.plain: use_oslogin = False else: public_key = ssh_helper.keys.GetPublicKey().ToEntry( include_comment=True) user, use_oslogin = ssh.CheckForOsloginAndGetUser( instance, project, user, public_key, self.ReleaseTrack()) if args.internal_ip: ip_address = ssh_utils.GetInternalIPAddress(instance) else: ip_address = ssh_utils.GetExternalIPAddress(instance) remote = ssh.Remote(ip_address, user) identity_file = None options = None if not args.plain: identity_file = ssh_helper.keys.key_file options = ssh_helper.GetConfig(ssh_utils.HostKeyAlias(instance), args.strict_host_key_checking) extra_flags = ssh.ParseAndSubstituteSSHFlags(args, remote, ip_address) remainder = [] if args.ssh_args: remainder.extend(args.ssh_args) # Transform args.command into arg list or None if no command command_list = args.command.split(' ') if args.command else None tty = containers.GetTty(args.container, command_list) remote_command = containers.GetRemoteCommand(args.container, command_list) target_remote = remote port = ssh_utils.DEFAULT_SSH_PORT ip_type = (ip.IpTypeEnum.INTERNAL if args.internal_ip else ip.IpTypeEnum.EXTERNAL) tunnel_helper = None interface = None if hasattr(args, 'tunnel_through_iap') and args.tunnel_through_iap: tunnel_helper, interface = ssh_utils.CreateIapTunnelHelper( args, instance_ref, instance, ip_type) tunnel_helper.StartListener() target_remote = ssh.Remote('localhost', user) port = tunnel_helper.GetLocalPort() cmd = ssh.SSHCommand(target_remote, port=str(port), identity_file=identity_file, options=options, extra_flags=extra_flags, remote_command=remote_command, tty=tty, remainder=remainder) if args.dry_run: log.out.Print(' '.join(cmd.Build(ssh_helper.env))) if tunnel_helper: tunnel_helper.StopListener() return if args.plain or use_oslogin: keys_newly_added = False else: keys_newly_added = ssh_helper.EnsureSSHKeyExists( client, remote.user, instance, project) if keys_newly_added: poller_tunnel_helper = None if tunnel_helper: poller_tunnel_helper, _ = ssh_utils.CreateIapTunnelHelper( args, instance_ref, instance, ip_type, interface=interface) poller_tunnel_helper.StartListener( accept_multiple_connections=True) poller = ssh_utils.CreateSSHPoller(remote, identity_file, options, poller_tunnel_helper, extra_flags=extra_flags) log.status.Print('Waiting for SSH key to propagate.') # TODO(b/35355795): Don't force_connect try: poller.Poll(ssh_helper.env, force_connect=True) except retry.WaitException: if tunnel_helper: tunnel_helper.StopListener() raise ssh_utils.NetworkError() finally: if poller_tunnel_helper: poller_tunnel_helper.StopListener() if args.internal_ip and not tunnel_helper: # The IAP Tunnel connection uses instance name and network interface name, # so do not need to additionally verify the instance. Also, the # SSHCommand used within the function does not support IAP Tunnels. ssh_helper.PreliminarilyVerifyInstance(instance.id, remote, identity_file, options) try: # Errors from SSH itself result in an ssh.CommandError being raised return_code = cmd.Run(ssh_helper.env, force_connect=True) finally: if tunnel_helper: tunnel_helper.StopListener() if return_code: # This is the return code of the remote command. Problems with SSH itself # will result in ssh.CommandError being raised above. sys.exit(return_code)
def Run(self, args, port=None, recursive=False, extra_flags=None): """SCP files between local and remote GCE instance. Run this method from subclasses' Run methods. Args: args: argparse.Namespace, the args the command was invoked with. port: str, int or None, Port number to use for SSH connection. recursive: bool, Whether to use recursive copying using -R flag. extra_flags: [str] or None, extra flags to add to command invocation. Raises: ssh_utils.NetworkError: Network issue which likely is due to failure of SSH key propagation. ssh.CommandError: The SSH command exited with SSH exit code, which usually implies that a connection problem occurred. """ super(BaseScpCommand, self).Run(args) dst = ssh.FileReference.FromPath(args.destination) srcs = [ssh.FileReference.FromPath(src) for src in args.sources] # Make sure we have a unique remote ssh.SCPCommand.Verify(srcs, dst, single_remote=True) remote = dst.remote or srcs[0].remote if not dst.remote: # Make sure all remotes point to the same ref for src in srcs: src.remote = remote instance_ref = instance_flags.SSH_INSTANCE_RESOLVER.ResolveResources( [remote.host], compute_scope.ScopeEnum.ZONE, args.zone, self.resources, scope_lister=flags.GetDefaultScopeLister(self.compute_client))[0] instance = self.GetInstance(instance_ref) # Now replace the instance name with the actual IP/hostname remote.host = ssh_utils.GetExternalIPAddress(instance) if not remote.user: remote.user = ssh.GetDefaultSshUsername(warn_on_account_user=True) identity_file = None options = None if not args.plain: identity_file = self.keys.key_file options = self.GetConfig(ssh_utils.HostKeyAlias(instance), args.strict_host_key_checking) cmd = ssh.SCPCommand( srcs, dst, identity_file=identity_file, options=options, recursive=recursive, port=port, extra_flags=extra_flags) if args.dry_run: log.out.Print(' '.join(cmd.Build(self.env))) return if args.plain: keys_newly_added = False else: keys_newly_added = self.EnsureSSHKeyExists( remote.user, instance, instance_ref.project, use_account_service=self._use_account_service) if keys_newly_added: poller = ssh.SSHPoller( remote, identity_file=identity_file, options=options, max_wait_ms=ssh_utils.SSH_KEY_PROPAGATION_TIMEOUT_SEC) log.status.Print('Waiting for SSH key to propagate.') # TODO(b/35355795): Don't force_connect try: poller.Poll(self.env, force_connect=True) except retry.WaitException: raise ssh_utils.NetworkError() return_code = cmd.Run(self.env, force_connect=True) if return_code: # Can't raise an exception because we don't want any "ERROR" message # printed; the output from `ssh` will be enough. sys.exit(return_code)
def Run(self, args): super(CopyFiles, self).Run(args) # TODO(b/33467618): Change this implementation to use ssh.SCPCommand and # ssh.FileReference objects. file_specs = [] # Parses the positional arguments. for arg in args.sources + [args.destination]: if ssh.IsScpLocalPath(arg): file_specs.append(LocalFile(arg)) else: user_host, file_path = arg.split(':', 1) user_host_parts = user_host.split('@', 1) if len(user_host_parts) == 1: user = ssh.GetDefaultSshUsername(warn_on_account_user=True) source_instance = user_host_parts[0] else: user, source_instance = user_host_parts file_specs.append(RemoteFile(user, source_instance, file_path)) log.debug('Normalized arguments: %s', file_specs) # Validates the positional arguments. # TODO(b/36057400): Look into relaxing these conditions. sources = file_specs[:-1] destination = file_specs[-1] if isinstance(destination, LocalFile): for source in sources: if isinstance(source, LocalFile): raise exceptions.ToolException( 'All sources must be remote files when the destination ' 'is local.') else: # RemoteFile for source in sources: if isinstance(source, RemoteFile): raise exceptions.ToolException( 'All sources must be local files when the destination ' 'is remote.') destination_instances = set() for file_spec in file_specs: if isinstance(file_spec, RemoteFile): destination_instances.add(file_spec.instance_name) if len(destination_instances) > 1: raise exceptions.ToolException( 'Copies must involve exactly one virtual machine instance; ' 'your invocation refers to [{0}] instances: [{1}].'.format( len(destination_instances), ', '.join(sorted(destination_instances)))) source_instance_ref = instance_flags.SSH_INSTANCE_RESOLVER.ResolveResources( [source_instance], compute_scope.ScopeEnum.ZONE, args.zone, self.resources, scope_lister=flags.GetDefaultScopeLister(self.compute_client))[0] source_instance = self.GetInstance(source_instance_ref) external_ip_address = ssh_utils.GetExternalIPAddress(source_instance) # Builds the scp command. scp_args = [self.env.scp] if not args.plain: scp_args.extend(ssh.GetDefaultFlags(self.keys.key_file)) host_key_alias = ssh_utils.HostKeyAlias(source_instance) scp_args.extend( ssh.GetHostKeyArgs(host_key_alias, args.plain, args.strict_host_key_checking)) scp_args.append('-r') for file_spec in file_specs: if isinstance(file_spec, LocalFile): scp_args.append(file_spec.file_path) else: scp_args.append('{0}:{1}'.format( ssh.UserHost(file_spec.user, external_ip_address), file_spec.file_path)) ip_address = ssh_utils.GetExternalIPAddress(source_instance) self.ActuallyRun(args, scp_args, user, source_instance, source_instance_ref.project, ip_address)
def RunScp(self, compute_holder, args, port=None, recursive=False, compress=False, extra_flags=None, release_track=None, ip_type=ip.IpTypeEnum.EXTERNAL): """SCP files between local and remote GCE instance. Run this method from subclasses' Run methods. Args: compute_holder: The ComputeApiHolder. args: argparse.Namespace, the args the command was invoked with. port: str or None, Port number to use for SSH connection. recursive: bool, Whether to use recursive copying using -R flag. compress: bool, Whether to use compression. extra_flags: [str] or None, extra flags to add to command invocation. release_track: obj, The current release track. ip_type: IpTypeEnum, Specify using internal ip or external ip address. Raises: ssh_utils.NetworkError: Network issue which likely is due to failure of SSH key propagation. ssh.CommandError: The SSH command exited with SSH exit code, which usually implies that a connection problem occurred. """ if release_track is None: release_track = base.ReleaseTrack.GA super(BaseScpHelper, self).Run(args) dst = ssh.FileReference.FromPath(args.destination) srcs = [ssh.FileReference.FromPath(src) for src in args.sources] # Make sure we have a unique remote ssh.SCPCommand.Verify(srcs, dst, single_remote=True) remote = dst.remote or srcs[0].remote if not dst.remote: # Make sure all remotes point to the same ref for src in srcs: src.remote = remote instance_ref = instance_flags.SSH_INSTANCE_RESOLVER.ResolveResources( [remote.host], compute_scope.ScopeEnum.ZONE, args.zone, compute_holder.resources, scope_lister=instance_flags.GetInstanceZoneScopeLister( compute_holder.client))[0] instance = self.GetInstance(compute_holder.client, instance_ref) project = self.GetProject(compute_holder.client, instance_ref.project) if not remote.user: remote.user = ssh.GetDefaultSshUsername(warn_on_account_user=True) if args.plain: use_oslogin = False else: public_key = self.keys.GetPublicKey().ToEntry(include_comment=True) remote.user, use_oslogin = ssh.CheckForOsloginAndGetUser( instance, project, remote.user, public_key, release_track) identity_file = None options = None if not args.plain: identity_file = self.keys.key_file options = self.GetConfig(ssh_utils.HostKeyAlias(instance), args.strict_host_key_checking) iap_tunnel_args = iap_tunnel.SshTunnelArgs.FromArgs( args, release_track, instance_ref, ssh_utils.GetInternalInterface(instance), ssh_utils.GetExternalInterface(instance, no_raise=True)) if iap_tunnel_args: remote.host = ssh_utils.HostKeyAlias(instance) elif ip_type is ip.IpTypeEnum.INTERNAL: remote.host = ssh_utils.GetInternalIPAddress(instance) else: remote.host = ssh_utils.GetExternalIPAddress(instance) cmd = ssh.SCPCommand(srcs, dst, identity_file=identity_file, options=options, recursive=recursive, compress=compress, port=port, extra_flags=extra_flags, iap_tunnel_args=iap_tunnel_args) if args.dry_run: log.out.Print(' '.join(cmd.Build(self.env))) return if args.plain or use_oslogin: keys_newly_added = False else: keys_newly_added = self.EnsureSSHKeyExists(compute_holder.client, remote.user, instance, project) if keys_newly_added: poller = ssh_utils.CreateSSHPoller(remote, identity_file, options, iap_tunnel_args, port=port) log.status.Print('Waiting for SSH key to propagate.') # TODO(b/35355795): Don't force_connect try: poller.Poll(self.env, force_connect=True) except retry.WaitException: raise ssh_utils.NetworkError() if ip_type is ip.IpTypeEnum.INTERNAL: # This will never happen when IAP Tunnel is enabled, because ip_type is # always EXTERNAL when IAP Tunnel is enabled, even if the instance has no # external IP. IAP Tunnel doesn't need verification because it uses # unambiguous identifiers for the instance. self.PreliminarilyVerifyInstance(instance.id, remote, identity_file, options) # Errors from the SCP command result in an ssh.CommandError being raised cmd.Run(self.env, force_connect=True)
def Run(self, args): """See ssh_utils.BaseSSHCLICommand.Run.""" super(SshGA, self).Run(args) user, instance_name = ssh_utils.GetUserAndInstance( args.user_host, self._use_account_service, self.http) instance_ref = instance_flags.SSH_INSTANCE_RESOLVER.ResolveResources( [instance_name], compute_scope.ScopeEnum.ZONE, args.zone, self.resources, scope_lister=flags.GetDefaultScopeLister(self.compute_client))[0] instance = self.GetInstance(instance_ref) if self._use_internal_ip: ip_address = ssh_utils.GetInternalIPAddress(instance) else: ip_address = ssh_utils.GetExternalIPAddress(instance) remote = ssh.Remote(ip_address, user) identity_file = None options = None if not args.plain: identity_file = self.keys.key_file options = self.GetConfig(ssh_utils.HostKeyAlias(instance), args.strict_host_key_checking) extra_flags = [] remainder = [] if args.ssh_flag: for flag in args.ssh_flag: for flag_part in flag.split(): # We want grouping here dereferenced_flag = (flag_part.replace( '%USER%', remote.user).replace('%INSTANCE%', ip_address)) extra_flags.append(dereferenced_flag) if args.ssh_args: remainder.extend(args.ssh_args) tty = ssh_utils.GetTty(args.container, args.command) remote_command = ssh_utils.GetRemoteCommand(args.container, args.command) cmd = ssh.SSHCommand(remote, identity_file=identity_file, options=options, extra_flags=extra_flags, remote_command=remote_command, tty=tty, remainder=remainder) if args.dry_run: log.out.Print(' '.join(cmd.Build(self.env))) return if args.plain: keys_newly_added = False else: keys_newly_added = self.EnsureSSHKeyExists( remote.user, instance, instance_ref.project, use_account_service=self._use_account_service) if keys_newly_added: poller = ssh.SSHPoller( remote, identity_file=identity_file, options=options, extra_flags=extra_flags, max_wait_ms=ssh_utils.SSH_KEY_PROPAGATION_TIMEOUT_SEC) log.status.Print('Waiting for SSH key to propagate.') # TODO(b/35355795): Don't force_connect try: poller.Poll(self.env, force_connect=True) except retry.WaitException: raise ssh_utils.NetworkError() if self._use_internal_ip: self._PreliminarylyVerifyInstance(instance.id, remote, identity_file, options, extra_flags) return_code = cmd.Run(self.env, force_connect=True) if return_code: # Can't raise an exception because we don't want any "ERROR" message # printed; the output from `ssh` will be enough. sys.exit(return_code)
def RunScp(self, compute_holder, args, port=None, recursive=False, compress=False, extra_flags=None, release_track=None, ip_type=ip.IpTypeEnum.EXTERNAL): """SCP files between local and remote GCE instance. Run this method from subclasses' Run methods. Args: compute_holder: The ComputeApiHolder. args: argparse.Namespace, the args the command was invoked with. port: str or None, Port number to use for SSH connection. recursive: bool, Whether to use recursive copying using -R flag. compress: bool, Whether to use compression. extra_flags: [str] or None, extra flags to add to command invocation. release_track: obj, The current release track. ip_type: IpTypeEnum, Specify using internal ip or external ip address. Raises: ssh_utils.NetworkError: Network issue which likely is due to failure of SSH key propagation. ssh.CommandError: The SSH command exited with SSH exit code, which usually implies that a connection problem occurred. """ if release_track is None: release_track = base.ReleaseTrack.GA super(BaseScpHelper, self).Run(args) dst = ssh.FileReference.FromPath(args.destination) srcs = [ssh.FileReference.FromPath(src) for src in args.sources] # Make sure we have a unique remote ssh.SCPCommand.Verify(srcs, dst, single_remote=True) remote = dst.remote or srcs[0].remote if not dst.remote: # Make sure all remotes point to the same ref for src in srcs: src.remote = remote instance_ref = instance_flags.SSH_INSTANCE_RESOLVER.ResolveResources( [remote.host], compute_scope.ScopeEnum.ZONE, args.zone, compute_holder.resources, scope_lister=instance_flags.GetInstanceZoneScopeLister( compute_holder.client))[0] instance = self.GetInstance(compute_holder.client, instance_ref) project = self.GetProject(compute_holder.client, instance_ref.project) # Now replace the instance name with the actual IP/hostname if ip_type is ip.IpTypeEnum.INTERNAL: remote.host = ssh_utils.GetInternalIPAddress(instance) else: remote.host = ssh_utils.GetExternalIPAddress(instance) if not remote.user: remote.user = ssh.GetDefaultSshUsername(warn_on_account_user=True) if args.plain: use_oslogin = False else: public_key = self.keys.GetPublicKey().ToEntry(include_comment=True) remote.user, use_oslogin = ssh.CheckForOsloginAndGetUser( instance, project, remote.user, public_key, release_track) identity_file = None options = None if not args.plain: identity_file = self.keys.key_file options = self.GetConfig(ssh_utils.HostKeyAlias(instance), args.strict_host_key_checking) tunnel_helper = None cmd_port = port interface = None if hasattr(args, 'tunnel_through_iap') and args.tunnel_through_iap: tunnel_helper, interface = ssh_utils.CreateIapTunnelHelper( args, instance_ref, instance, ip_type, port=port) tunnel_helper.StartListener() cmd_port = str(tunnel_helper.GetLocalPort()) if dst.remote: dst.remote.host = 'localhost' else: for src in srcs: src.remote.host = 'localhost' cmd = ssh.SCPCommand(srcs, dst, identity_file=identity_file, options=options, recursive=recursive, compress=compress, port=cmd_port, extra_flags=extra_flags) if args.dry_run: log.out.Print(' '.join(cmd.Build(self.env))) if tunnel_helper: tunnel_helper.StopListener() return if args.plain or use_oslogin: keys_newly_added = False else: keys_newly_added = self.EnsureSSHKeyExists(compute_holder.client, remote.user, instance, project) if keys_newly_added: poller_tunnel_helper = None if tunnel_helper: poller_tunnel_helper, _ = ssh_utils.CreateIapTunnelHelper( args, instance_ref, instance, ip_type, port=port, interface=interface) poller_tunnel_helper.StartListener( accept_multiple_connections=True) poller = ssh_utils.CreateSSHPoller(remote, identity_file, options, poller_tunnel_helper, port=port) log.status.Print('Waiting for SSH key to propagate.') # TODO(b/35355795): Don't force_connect try: poller.Poll(self.env, force_connect=True) except retry.WaitException: if tunnel_helper: tunnel_helper.StopListener() raise ssh_utils.NetworkError() finally: if poller_tunnel_helper: poller_tunnel_helper.StopListener() if ip_type is ip.IpTypeEnum.INTERNAL and not tunnel_helper: # The IAP Tunnel connection uses instance name and network interface name, # so do not need to additionally verify the instance. Also, the # SSHCommand used within the function does not support IAP Tunnels. self.PreliminarilyVerifyInstance(instance.id, remote, identity_file, options) try: # Errors from the SCP command result in an ssh.CommandError being raised cmd.Run(self.env, force_connect=True) finally: if tunnel_helper: tunnel_helper.StopListener()