perfkitbenchmarker/linux_benchmarks/scp_benchmark.py (92 lines of code) (raw):
# Copyright 2024 PerfKitBenchmarker Authors. 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.
"""SCP files between VMs."""
import logging
import time
from typing import Any
from absl import flags
from perfkitbenchmarker import background_tasks
from perfkitbenchmarker import benchmark_spec as bm_spec
from perfkitbenchmarker import configs
from perfkitbenchmarker import sample
FLAGS = flags.FLAGS
flags.DEFINE_integer('scp_file_size_gb', 1,
'Size of the file to be created for SCP transfer in GB.')
BENCHMARK_NAME = 'scp'
BENCHMARK_CONFIG = """
scp:
description: Runs SCP benchmark.
vm_groups:
vm_1:
vm_spec: *default_dual_core
disk_spec: *default_500_gb
vm_2:
vm_spec: *default_dual_core
disk_spec: *default_500_gb
"""
def GetConfig(user_config: dict[str, Any]) -> dict[str, Any]:
"""Returns the configuration of a benchmark."""
return configs.LoadConfig(BENCHMARK_CONFIG, user_config, BENCHMARK_NAME)
def GenerateSSHKey(vm):
"""Generates a new SSH key pair on the VM."""
vm.RemoteCommand('ssh-keygen -t rsa -N "" -f ~/.ssh/scp_benchmark_key')
public_key, _ = vm.RemoteCommand('cat ~/.ssh/scp_benchmark_key.pub')
return public_key.strip()
def GenerateAndExchangeSSHKeys(vm1, vm2):
"""Exchanges SSH keys between two VMs."""
public_key1 = GenerateSSHKey(vm1)
public_key2 = GenerateSSHKey(vm2)
vm1.RemoteCommand(f'echo "{public_key2}" >> ~/.ssh/authorized_keys')
vm2.RemoteCommand(f'echo "{public_key1}" >> ~/.ssh/authorized_keys')
def Prepare(benchmark_spec: bm_spec.BenchmarkSpec):
"""Prepares the VMs and files to be transferred."""
logging.info('Preparing SCP benchmark')
vms = benchmark_spec.vms
if len(vms) != 2:
raise ValueError(
f'scp benchmark requires exactly two machines, found {len(vms)}'
)
# Backup current states
for vm in vms:
vm.RemoteCommand('sysctl -a > /tmp/original_sysctl.conf')
# Generate and exchange SSH keys
GenerateAndExchangeSSHKeys(vms[0], vms[1])
# Create a payload file on both VM
try:
background_tasks.RunThreaded(PrepareFileForTransfer, vms)
except Exception as e:
logging.error('Error during file preparation: %s', str(e))
def PrepareFileForTransfer(vm):
"""Creates a test file of specified size on the VM."""
logging.info('Preparing the test file')
# Create a random file of specified size
file_size_gb = FLAGS.scp_file_size_gb
block_size = '50M' # 50MiB per block
count = file_size_gb * 20 # 1GB = 1000MiB = 20 blocks
# Using default mount_point /scratch
vm.RemoteCommand(
'dd if=/dev/urandom of=/scratch/payload.img'
f' bs={block_size} count={count} status=progress'
)
def Run(benchmark_spec: bm_spec.BenchmarkSpec) -> list[sample.Sample]:
"""Runs the benchmark and returns a dict of performance data."""
logging.info('Running SCP benchmark')
vms = benchmark_spec.vms
vm1, vm2 = vms[0], vms[1]
results = []
def TransferFile(vm_pair):
source_vm, dest_vm = vm_pair
# clear any existing received file from previous runs
dest_vm.RemoteCommand('rm -f /scratch/received.img')
scp_cmd = (
'scp -o StrictHostKeyChecking=no -i ~/.ssh/scp_benchmark_key '
'/scratch/payload.img '
f'{dest_vm.user_name}@{dest_vm.internal_ip}:/scratch/received.img'
)
start_time = time.time()
source_vm.RemoteCommand(scp_cmd)
end_time = time.time()
return end_time - start_time
# Run TransferFile on both VMs
thread_params = [
(((vm1, vm2),), {}), # VM1 to VM2
(((vm2, vm1),), {}), # VM2 to VM1
]
transfer_times = background_tasks.RunThreaded(TransferFile, thread_params)
transfer_time_1, transfer_time_2 = transfer_times
metadata = {
'file_size_gb': FLAGS.scp_file_size_gb,
'vm1_zone': vm1.zone,
'vm2_zone': vm2.zone,
'vm1_machine_type': vm1.machine_type,
'vm2_machine_type': vm2.machine_type,
}
# Report individual transfer times
results.append(sample.Sample(
'SCP_Transfer_Time_VM1_to_VM2', transfer_time_1, 'seconds', metadata))
results.append(sample.Sample(
'SCP_Transfer_Time_VM2_to_VM1', transfer_time_2, 'seconds', metadata))
return results
def Cleanup(benchmark_spec: bm_spec.BenchmarkSpec):
"""Cleans up after SCP benchmark completes."""
vms = benchmark_spec.vms
for vm in vms:
vm.RemoteCommand('rm -rf /scratch')
vm.RemoteCommand(
'rm -f ~/.ssh/scp_benchmark_key ~/.ssh/scp_benchmark_key.pub'
)
vm.RemoteCommand("sed -i '/scp_benchmark_key/d' ~/.ssh/authorized_keys")
# restore original state in case
vm.RemoteCommand('sudo sysctl -p /tmp/original_sysctl.conf')
vm.RemoteCommand('rm /tmp/original_sysctl.conf')