def _execute(self): self.source_remote_executor = RemoteHostExecutor(self._source.remote_host) self._execute_on_every_device(self._sync_disk, self._sync_partition) self.source_remote_executor.close() self.source_remote_executor = None return Commander.Signal.SLEEP
def __init__(self, remote_host): """ :param remote_host: the remote host to execute on :type remote_host: remote_host.public.RemoteHost """ self.remote_executor = RemoteHostExecutor(remote_host)
def _execute(self): self.source_remote_executor = RemoteHostExecutor( self._source.remote_host) self._execute_on_every_device(self._replicate_partition_table, None, include_swap=True) self.source_remote_executor.close() self.source_remote_executor = None
def _execute(self): root_source_mountpoint = self._find_root_source_mountpoint() remote_executor = RemoteHostExecutor(self._target.remote_host) self._create_source_environment_mountpoints(remote_executor, root_source_mountpoint) self._mount_source_environment(remote_executor, root_source_mountpoint) self._mount_source_mountpoints(remote_executor, root_source_mountpoint) self._reinstall_bootloader(remote_executor, root_source_mountpoint)
def _comment_out_listen_address(self): """ comments out the ListenAddress line in the sshd_config file """ RemoteFileEditor(RemoteHostExecutor(self._target.remote_host)).edit( SourceFileLocationResolver(self._source).resolve_path(self.SSHD_CONFIG_LOCATION), 'ListenAddress', '# ListenAddress' )
def _write_network_config(self, network_config): """ wirtes a given network config to the network config file :param network_config: the network config to persist :type network_config: str """ RemoteFileEditor(RemoteHostExecutor(self._target.remote_host)).write( SourceFileLocationResolver(self._source).resolve_path(self.NETWORK_CONFIG_FILE_LOCATION), network_config )
def _execute_on_every_device(self, executable_for_disks, executable_for_partitions=None, include_swap=False): """ execute the given executable with all devices. :param executable_for_disks: a function which takes the parameters: remote_executor, source_device, target_device :type executable_for_disks: (self: Any, RemoteExecutor, (str, dict), (str, dict)) -> None :param executable: a function which takes the parameters: remote_executor, source_device, target_device, source_partition_device, target_partition_device :type executable_for_partitions: (self: Any, RemoteExecutor, (str, dict), (str, dict), (str, dict), (str, dict) ) -> None :param include_swap: should a swap device also be iterated over :type include_swap: bool :return: the used remote_executor, for extended use :rtype: RemoteHostExecutor """ remote_executor = RemoteHostExecutor(self._target.remote_host) for source_device_id, target_device in self._target.device_mapping.items( ): source_device = self._source.remote_host.system_info[ 'block_devices'][source_device_id] if executable_for_disks and (include_swap or not include_swap and source_device['fs'] != 'swap'): executable_for_disks( remote_executor, (source_device_id, source_device), (target_device['id'], target_device), ) if executable_for_partitions: for source_partition_id, target_partition in target_device[ 'children'].items(): source_partition = source_device['children'][ source_partition_id] if (include_swap or not include_swap and source_partition['fs'] != 'swap'): executable_for_partitions( remote_executor, (source_device_id, source_device), (target_device['id'], target_device), (source_partition_id, source_partition), (target_partition['id'], target_partition), ) return remote_executor
class RemoteScriptExecutor(): """ takes care of executing a given python script on a remote host and injecting environment variables """ REMOTE_SCRIPT_BASE_TEMPLATE = ('CONTEXT = {env_string}\n{script_string}') PYTHON_SCRIPT_EXECUTION_COMMAND = RemoteHostCommand( '{SUDO_PREFIX}python -c "import base64;exec(base64.b64decode({ENCODED_SCRIPT}))"' ) def __init__(self, remote_host): """ :param remote_host: the remote host to execute on :type remote_host: remote_host.public.RemoteHost """ self.remote_executor = RemoteHostExecutor(remote_host) def execute(self, script, env=None, sudo=False): """ executes the given script on the remote host and injects the env dict as a CONTEXT dict into the script :param script: the script to execute :type script: str :param env: the env to inject into the script :type env: dict :param sudo: whether the script should be executed as sudo or not :type sudo: bool :return: the stdout of the script execution :rtype: str """ return self.remote_executor.execute( self._render_script_execution_command(script, env, sudo)) def _render_script_execution_command(self, script, env, sudo): return self.PYTHON_SCRIPT_EXECUTION_COMMAND.render( sudo_prefix='sudo ' if sudo else '', encoded_script=self._encode_script( self.REMOTE_SCRIPT_BASE_TEMPLATE.format( env_string=self._render_env(env), script_string=script, )), ) def _render_env(self, env): return str(env if env else {}) def _encode_script(self, rendered_script): return base64.b64encode(rendered_script.encode())
class CreatePartitionsCommand(DeviceModifyingCommand): """ takes care of creating the partitions on the target system """ class CanNotCreatePartitionException( DeviceModifyingCommand.CommandExecutionException): """ is called if a partition can not be created, for whatever reason """ COMMAND_DOES = 'create the partitions' ERROR_REPORT_EXCEPTION_CLASS = CanNotCreatePartitionException READ_PARTITION_TABLE_COMMAND = RemoteHostCommand('sudo sfdisk -d {DEVICE}') WRITE_PARTITION_TABLE_COMMAND = RemoteHostCommand( 'echo "{PARTITION_TABLE}" | sudo sfdisk {DEVICE}') def _execute(self): self.source_remote_executor = RemoteHostExecutor( self._source.remote_host) self._execute_on_every_device(self._replicate_partition_table, None, include_swap=True) self.source_remote_executor.close() self.source_remote_executor = None @DeviceModifyingCommand._collect_errors def _replicate_partition_table(self, remote_executor, source_device, target_device): """ replicates the partition table of the source device and applies it to the target device :param remote_executor: remote executor to use for execution :type remote_executor: RemoteHostExecutor :param source_device: the source device :type source_device: (str, dict) :param target_device: the target device :type target_device: (str, dict) """ partition_table = self._read_partition_table( self.source_remote_executor, source_device[0]) if partition_table: self._write_partition_table(remote_executor, target_device[0], partition_table) else: self.logger.debug( 'no valid partition table found for {source_device}'.format( source_device=source_device)) def _write_partition_table(self, remote_executor, target_device_id, partition_table): remote_executor.execute( self.WRITE_PARTITION_TABLE_COMMAND.render( partition_table=self._make_string_echo_safe(partition_table), device='/dev/{device_id}'.format(device_id=target_device_id))) def _read_partition_table(self, source_remote_executor, source_device_id): try: return source_remote_executor.execute( self.READ_PARTITION_TABLE_COMMAND.render( device='/dev/{device_id}'.format( device_id=source_device_id))) except RemoteHostExecutor.ExecutionException: return '' def _make_string_echo_safe(self, string): return string.replace('"', '\\"')
def _init_test_data(self, **kwargs): self.remote_executor = RemoteHostExecutor( RemoteHost.objects.create(address='ubuntu16')) TestAsset.REMOTE_HOST_MOCKS['ubuntu16'].add_command( 'sudo cat /etc/testfile.txt', self.TEST_FILE_CONTENT)
def _init_operator_class(self, operator_class): return operator_class(RemoteHostExecutor(self.remote_host))
class SyncCommand(DeviceModifyingCommand): """ does the actual sync and makes sure, that temp mounts are created and used, to avoid problems introduced by overlapping mountpoints. """ class SyncingException(DeviceModifyingCommand.CommandExecutionException): """ raised if an error occurs during the sync """ COMMAND_DOES = 'do the sync' ERROR_REPORT_EXCEPTION_CLASS = SyncingException ACCEPTED_EXIT_CODES = (24,) def _execute(self): self.source_remote_executor = RemoteHostExecutor(self._source.remote_host) self._execute_on_every_device(self._sync_disk, self._sync_partition) self.source_remote_executor.close() self.source_remote_executor = None return Commander.Signal.SLEEP def _sync_disk(self, remote_executor, source_device, target_device): self._sync_device(self.source_remote_executor, source_device[1]['mountpoint'], target_device[1]['mountpoint']) def _sync_partition( self, remote_executor, source_device, target_device, source_partition_device, target_partition_device ): self._sync_device( self.source_remote_executor, source_partition_device[1]['mountpoint'], target_partition_device[1]['mountpoint'], ) @DeviceModifyingCommand._collect_errors def _sync_device(self, remote_executor, source_directory, target_directory): if source_directory: remote_executor.execute( RemoteHostCommand(self._target.blueprint['commands']['sync']).render( source_dir=self._create_temp_bind_mount(remote_executor, source_directory), target_dir='{user}{remote_host_address}:{target_directory}'.format( user=('{username}@'.format(username=self._target.remote_host.username)) if self._target.remote_host.username else '', remote_host_address=self._target.remote_host.address, target_directory=target_directory, ) ), accepted_exit_codes=self.ACCEPTED_EXIT_CODES ) def _create_temp_bind_mount(self, remote_executor, source_directory): temp_mountpoint = MountpointMapper.map_mountpoint('/tmp', source_directory) remote_executor.execute( DefaultRemoteHostCommand.MAKE_DIRECTORY.render(directory=temp_mountpoint) ) try: remote_executor.execute(DefaultRemoteHostCommand.CHECK_MOUNTPOINT.render(directory=temp_mountpoint)) except RemoteHostExecutor.ExecutionException: remote_executor.execute( DefaultRemoteHostCommand.BIND_MOUNT.render( directory=source_directory, mountpoint=temp_mountpoint ) ) return temp_mountpoint
def _execute(self): RemoteHostExecutor(self._target.remote_host).execute( 'sudo shutdown -P now &', block_for_response=False) self._cloud_manager.stop_target( self._target.remote_host.cloud_metadata['id'])