def disconnect(self) -> None: # ADD DOCSTRING if not self.connected: raise SSHConnectionError( 'Not currently connected to a remote host') self.shell.close() self.connected = False self.port = None
def connect(self, hostname: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, use_key: bool = False, login: bool = False, port: Optional[int] = None, timeout: int = 60, retries: int = 0, retry_delay: int = 1) -> None: # ADD DOCSTRING # TODO: deal with prompting for required fields if not provided if self.connected: raise SSHConnectionError( 'already connected to a remote host. use `SshShell.disconnect` ' 'to disconnect from the current host before connecting to a new ' 'one, or `SshShell.reconnect` to reset the connection to the ' 'current host') port = port or self.port or 22 hostname = hostname or self.hostname or input("Hostname: ") username = username or self.username or input("Username: "******"Password: ") self._shell = spurplus.connect_with_retries( hostname=hostname, username=username, password=password, look_for_private_keys=(not use_key), port=port, connect_timeout=timeout, retries=retries, retry_period=retry_delay) # only update attrs if connection is successful self.hostname = hostname self.username = username self.port = port self.connected = True if self._environ is None: # read environment variables tmp_exe = self.executable or '/bin/bash' if login: printenv_command = [tmp_exe, '-lc', 'printenv'] else: printenv_command = ['printenv'] # TODO: a more robust solution for this in case BASH_FUNC_module isn't last initial_env = self.shell.check_output(printenv_command).split( '\nBASH_FUNC_module()')[0] initial_env = dict( map(lambda x: x.split('=', maxsplit=1), initial_env.splitlines())) self._environ = PseudoEnviron(initial_env=initial_env, custom_vars=self._env_additions) # initial validation of properties that depend on environment self.cwd = self._cwd self.executable = self._executable
def remote_path(self, path: PathLike): if self._remote_path is not None: raise AttributeError( "local/remote path mapping is read-only once set") elif not self._cluster.connected: raise SSHConnectionError( "Connection to remote host must be open to assign a remote " "path to a file mapping") path = self._cluster.resolve_path(PurePosixPath(path), strict=False) self._remote_path = path self._init_remote()
def load_project(self, name: str): # ADD DOCSTRING # loads a project form $HOME/.clustertools if not self.connected: raise SSHConnectionError( "SSH connection must be open to load a project") elif self.project is not None: raise ClusterToolsProjectError( f"Project '{self.project.name}' is already loaded. Use " "'cluster.unload_project()' to unload the current project " "before loading a new one") self.project = Project.load(name=name, cluster=self) self._mixin_project()
def create_project(self, name: str, **kwargs): # ADD DOCSTRING - creates a new project configuration & an entry # in $HOME/.clustertools # TODO: should take all (or most) arguments in Project constructor # TODO: would be possible to allow creating/partially loading # projects before connecting, but would require a lot of # additional coding around possible scenarios if not self.connected: raise SSHConnectionError( "SSH connection must be open to create a project") elif self.project is not None: raise ClusterToolsProjectError( "Cannot create a project when one is already loaded. Use " "'cluster.unload_project()' to unload the current project " "before creating a new one.") elif name in self.all_projects: raise ClusterToolsProjectError( f"Project '{name}' already exists. Use " f"cluster.load_project('{name}') to load its previous state.") self.project = Project(name=name, cluster=self, **kwargs) self._all_projects = tuple( list(self._all_projects) + [self.project.name]) self._mixin_project()
def executable(self) -> str: # ADD DOCSTRING if not self.connected: raise SSHConnectionError( "SSH connection must be open to determine remote shell") return self._executable
def environ(self) -> PseudoEnviron: if not self.connected: raise SSHConnectionError( "SSH connection must be open to access remote environment") return self._environ
def cwd(self: BaseShell) -> PurePosixPath: if not self.connected: raise SSHConnectionError( "SSH connection must be open to access remote file system") return self._cwd
def shell(self) -> spurplus.SshShell: if not self.connected: raise SSHConnectionError( "SSH connection must be open to access remote shell") return self._shell