D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
opt
/
psa
/
admin
/
plib
/
modules
/
site-import
/
backend
/
lib
/
python
/
parallels
/
plesk
/
source
/
web
/
Filename :
source.py
back
Copy
import ntpath import posixpath from contextlib import contextmanager from parallels.core import MigrationError from parallels.core.connections.ftp.connection_pool import FTPConnectionPool from parallels.core.connections.source import Source from parallels.core.connections.server import Server from parallels.core.migrator_config import PhysicalUnixServerConfig, SSHAuthPassword from parallels.core.runners.ftp.runner import FTPRunner from parallels.core.runners.unix.ssh import SSHRunner, TestCommandNonZeroExitCodeFailure from parallels.core.runners.web.runner import WebRPCAgentRunner from parallels.core.runners.web.utils.agent import WebRPCAgent from parallels.core.utils import ssh_utils from parallels.core.utils.common import cached, is_empty from parallels.core.utils.common.ip import resolve from parallels.core.utils.windows_mysql_client_deploy import deploy_mysqldump, deploy_mysql from parallels.plesk.source.web.config import TransferFilesModeOption from parallels.plesk.source.web.session_dir import WebSessionDir from parallels.plesk.source.web import messages class WebSource(Source, Server): """Source for migration from web """ def __init__(self, source_id, config, migrator_server): Source.__init__(self, source_id, config, migrator_server) Server.__init__(self) self._web_source_ssh_server = None self._ssh_runner = None self._ftp = None self._ftp_runner = None self._plesk_agent_id = '' self._web_rpc_agent = None self._web_rpc_agent_runner = None @property def config(self): """Return web source config :rtype: parallels.plesk.source.web.connections.WebSourceConfig """ return super(WebSource, self).config @contextmanager def runner(self): """Get runner to execute commands on the source :rtype: parallels.core.runners.web.runner.WebRPCAgentRunner """ yield self.get_web_rpc_runner() def get_filesystem_runner(self): """Get runner which should be used to work with files (upload, download, remove, etc) on the source site :rtype: parallels.core.runners.base.BaseRunner """ if self.is_ssh_available(): return self.get_ssh_runner() elif self.is_ftp_available(): return self.get_ftp_runner() else: raise MigrationError(messages.INTERNAL_ERROR_NO_FILE_TRANSPORT) def get_filesystem_runner_title(self): """Get human-readable title of the used filesystem runner ("SSH" or "FTP") :rtype: str """ if self.is_ssh_available(): return 'SSH' elif self.is_ftp_available(): return 'FTP' else: raise MigrationError(messages.INTERNAL_ERROR_NO_FILE_TRANSPORT) def get_filesystem_runner_encoding(self): """Get encoding in which filesystem runner works with file names :rtype: str """ if self.is_ssh_available(): # do not actually detect encoding, consider that all modern Linux servers use UTF-8 return 'utf-8' elif self.is_ftp_available(): return self.ftp.get_encoding() else: raise MigrationError(messages.INTERNAL_ERROR_NO_FILE_TRANSPORT) @contextmanager def filesystem_runner(self): """Get runner which should be used to work with files (upload, download, remove, etc) on the source site """ yield self.get_filesystem_runner() def connect_by_ftp(self): """ :rtype: None """ self._ftp = FTPConnectionPool.get_instance().get( self.config.host, self.config.username, self.config.password, self.config.ftp_encoding, self.config.ignore_passive_mode_server_ip, ftps_only=not self.config.effective_allow_insecure_connections ) self._ftp_runner = FTPRunner(self._ftp) @property @cached def ftp(self): """ :rtype: parallels.core.utils.ftp.Ftp """ if self._ftp is None: raise MigrationError(messages.INTERNAL_ERROR_NO_FTP) return self._ftp def get_ftp_runner(self): """Get runner based on FTP connection :rtype: parallels.core.runners.ftp.runner.FTPRunner """ if self._ftp_runner is None: raise MigrationError(messages.INTERNAL_ERROR_NO_FTP) return self._ftp_runner @contextmanager def ftp_runner(self): """Get runner based on FTP connection """ if self._ftp_runner is None: raise MigrationError(messages.INTERNAL_ERROR_NO_FTP) yield self._ftp_runner def is_ftp_available(self): """ :rtype: bool """ return self._ftp_runner is not None def connect_by_ssh(self, max_connect_attempts=5, interval_between_connect_attempts=10, tcp_timeout=15): """ :rtype: None """ try: self._web_source_ssh_server = WebSSHServer( self.config.host, self.config.username, self.config.password, self.config.ssh_port, self.config.transfer_files_mode, max_connect_attempts, interval_between_connect_attempts, tcp_timeout ) ssh_runner = self._web_source_ssh_server.get_runner() except TestCommandNonZeroExitCodeFailure: raise MigrationError(messages.FAILED_TO_EXECUTE_TEST_COMMAND_BY_SSH) self._check_ssh_required_utils(ssh_runner) self._ssh_runner = ssh_runner @staticmethod def _check_ssh_required_utils(ssh_runner): """ :type ssh_runner: parallels.core.runners.unix.ssh.SSHRunner :rtype: None :raises: parallels.core.MigrationError """ if not ssh_runner.is_utility_find_available() and not ssh_runner.is_utility_ls_available(): raise MigrationError(messages.NO_FIND_OR_LS_ON_SERVER) if not ssh_runner.is_utility_rsync_available() and not ssh_runner.is_utility_scp_available(): raise MigrationError(messages.NO_RSYNC_OR_SCP_ON_SERVER) def get_ssh_runner(self): """Get runner based on SSH connection :rtype: parallels.core.runners.unix.ssh.SSHRunner """ if self._ssh_runner is None: raise MigrationError(messages.INTERNAL_ERROR_NO_SSH) return self._ssh_runner @contextmanager def ssh_runner(self): """Get runner based on SSH connection """ if self._ssh_runner is None: raise MigrationError(messages.INTERNAL_ERROR_NO_SSH) yield self._ssh_runner def get_ssh_server(self): """ :rtype: parallels.plesk.source.web.source.WebSSHServer """ if self._web_source_ssh_server is None: raise MigrationError(messages.INTERNAL_ERROR_NO_SSH) return self._web_source_ssh_server def is_ssh_available(self): """ :rtype: bool """ return self._ssh_runner is not None def is_rsync_available(self): """ :rtype: bool """ return self._ssh_runner is not None and self._ssh_runner.is_utility_rsync_available() def connect_by_web_rpc_agent(self): """ :rtype: None """ web_rpc_agent = WebRPCAgent( self.get_filesystem_runner(), self.config.document_root, self.config.base_url, self._plesk_agent_id, verify_certificates=not self.config.effective_allow_insecure_connections, single_file_agent=self.config.single_file_web_rpc_agent ) web_rpc_agent.deploy() self._web_rpc_agent = web_rpc_agent self._web_rpc_agent_runner = WebRPCAgentRunner(self.web_rpc_agent, self.config.host) self._web_rpc_agent_runner.connect() @cached def get_web_rpc_runner(self): """ :rtype: parallels.core.runners.web.runner.WebRPCAgentRunner """ if self._web_rpc_agent_runner is None: raise MigrationError(messages.INTERNAL_ERROR_NO_WEB_RPC_AGENT) return self._web_rpc_agent_runner @cached def get_web_rpc_runner_encoding(self): """Get encoding in which web RPC runner works with file names :rtype: str """ # For Linux, we consider that all modern servers use UTF-8 # For Windows, we don't know actual encoding (detection implementation is required), # and use UTF-8, while it is usually not correct return 'utf-8' def is_web_rpc_agent_deployed(self): """ :rtype: bool """ return self._web_rpc_agent is not None @property @cached def web_rpc_agent(self): """ :rtype: parallels.core.runners.web.utils.agent.WebRPCAgent """ if self._web_rpc_agent is None: raise MigrationError(messages.INTERNAL_ERROR_NO_WEB_RPC_AGENT) return self._web_rpc_agent @cached def get_absolute_filesystem_root_path(self): """Retrieve path where located files of this source :rtype: str """ web_rpc_runner = self.get_web_rpc_runner() return web_rpc_runner.get_absolute_filesystem_root_path() @cached def get_files_root(self): """Path to start scan for files, applications and document root, related to FTP/SSH root. When not specified, default is used: when transport is FTP, then FTP root is used, when transport is SSH, then home directory of SSH user is used. :rtype: str | unicode """ if not is_empty(self.config.files_root): return self.config.files_root else: if self.is_ssh_available(): # By default, for SSH start search from a directory, which is the current right after login # (in most cases - home directory of system user). We can not always use "/" as retrieving files # tree and detection of document root and applications could take too much time and memory. return self.get_ssh_runner().execute_command('pwd').stdout.strip() else: return '/' def get_absolute_files_root(self): """Get absolute path for files root path. :rtype: str | unicode """ files_root = self.get_files_root() absolute_fs_root_path = self.get_absolute_filesystem_root_path() files_root_parts = [item for item in files_root.split('/') if item != ''] if self.is_windows(): return ntpath.join(absolute_fs_root_path, *files_root_parts) else: return posixpath.join(absolute_fs_root_path, *files_root_parts) @cached def get_path_to_mysqldump(self): """ :rtype: str | unicode """ if self.is_windows(): return deploy_mysqldump(self) else: return 'mysqldump' @cached def get_path_to_mysql(self): """ :rtype: str | unicode """ if self.is_windows(): return deploy_mysql(self) else: return 'mysql' def is_windows(self): """ :rtype: bool """ web_rpc_runner = self.get_web_rpc_runner() return web_rpc_runner.is_windows def _create_session_dir(self): """ :rtype: parallels.plesk.source.web.session_dir.WebSessionDir """ return WebSessionDir(self.runner, self.config.session_dir) def set_plesk_agent_id(self, plesk_agent_id): """ :type plesk_agent_id: str | unicode :rtype: None """ # convert agent ID to binary string, to avoid issues when using URL parse/unparse methods while combining # agent ID into URLs self._plesk_agent_id = str(plesk_agent_id) def get_plesk_agent_id(self): """ :rtype: str | unicode | None """ return self._plesk_agent_id class WebSSHServer(object): def __init__( self, host, username, password, port, transfer_files_mode, max_connect_attempts=5, interval_between_connect_attempts=10, tcp_timeout=15 ): self._host = host self._username = username self._password = password self._port = port self._tcp_timeout = tcp_timeout self._ssh = ssh_utils.connect( self.settings(), max_connect_attempts, interval_between_connect_attempts ).__enter__() if transfer_files_mode == TransferFilesModeOption.SSH_SCP: copy_files_mode = SSHRunner.COPY_FILES_MODE_SCP elif transfer_files_mode == TransferFilesModeOption.SSH_RSYNC: copy_files_mode = SSHRunner.COPY_FILES_MODE_RSYNC else: copy_files_mode = SSHRunner.COPY_FILES_MODE_AUTO self._ssh_runner = SSHRunner( self._ssh, self, SSHRunner.LIST_FILES_MODE_AUTO, copy_files_mode ) @contextmanager def runner(self): yield self.get_runner() def get_runner(self): """ :rtype: parallels.core.runners.unix.ssh.SSHRunner """ return self._ssh_runner def description(self): return 'remote server' def ip(self): return resolve(self._host) def settings(self): return PhysicalUnixServerConfig( 'web', self.ip(), None, SSHAuthPassword(self._port, self._username, self._password, self._tcp_timeout) ) def ssh_key(self): return None