def __init__(self, ip_address=None, username='******', password=None, key=None, connection_timeout=600, retry_interval=10): self.client_log = cclogging.getLogger( cclogging.get_object_namespace(self.__class__)) self.ip_address = ip_address self.username = username self.password = password self.connection_timeout = connection_timeout # Verify the IP address has a valid format try: IP(ip_address) except ValueError: raise InvalidAddressFormat(ip_address) # Verify the server can be pinged before attempting to connect PingClient.ping_until_reachable(ip_address, timeout=connection_timeout, interval_time=retry_interval) if key is not None: auth_strategy = SSHAuthStrategy.KEY_STRING else: auth_strategy = SSHAuthStrategy.PASSWORD allow_agent = True if not key: allow_agent = False self.ssh_client = SSHClient(username=self.username, password=self.password, host=self.ip_address, tcp_timeout=20, auth_strategy=auth_strategy, look_for_keys=False, key=key, allow_agent=allow_agent) self.ssh_client.connect_with_timeout(cooldown=20, timeout=connection_timeout) if not self.ssh_client.is_connected(): message = ('SSH timeout after {timeout} seconds: ' 'Could not connect to {ip_address}.') raise SshConnectionException( message.format(timeout=connection_timeout, ip_address=ip_address))
def __init__(self, ip_address=None, username='******', password=None, key=None, connection_timeout=600, retry_interval=10): self.client_log = cclogging.getLogger( cclogging.get_object_namespace(self.__class__)) if ip_address is None: raise ServerUnreachable("None") self.ip_address = ip_address self.username = username self.password = password self.connection_timeout = connection_timeout # Verify the server can be pinged before attempting to connect start = int(time.time()) reachable = False while not reachable: reachable = PingClient.ping(ip_address) if reachable: break time.sleep(retry_interval) if int(time.time()) - start >= connection_timeout: raise ServerUnreachable(ip_address) if key is not None: auth_strategy = SSHAuthStrategy.KEY_STRING else: auth_strategy = SSHAuthStrategy.PASSWORD self.ssh_client = SSHClient( username=self.username, password=self.password, host=self.ip_address, tcp_timeout=20, auth_strategy=auth_strategy, look_for_keys=False, key=key) self.ssh_client.connect_with_timeout( cooldown=20, timeout=connection_timeout) if not self.ssh_client.is_connected(): message = ('SSH timeout after {timeout} seconds: ' 'Could not connect to {ip_address}.') raise SshConnectionException(message.format( timeout=connection_timeout, ip_address=ip_address))
def __init__(self, ip_address=None, username='******', password=None, key=None, connection_timeout=600, retry_interval=10): self.client_log = cclogging.getLogger( cclogging.get_object_namespace(self.__class__)) self.ip_address = ip_address self.username = username self.password = password self.connection_timeout = connection_timeout # Verify the IP address has a valid format try: IP(ip_address) except ValueError: raise InvalidAddressFormat(ip_address) # Verify the server can be pinged before attempting to connect PingClient.ping_until_reachable(ip_address, timeout=connection_timeout, interval_time=retry_interval) if key is not None: auth_strategy = SSHAuthStrategy.KEY_STRING else: auth_strategy = SSHAuthStrategy.PASSWORD allow_agent = True if not key: allow_agent = False self.ssh_client = SSHClient( username=self.username, password=self.password, host=self.ip_address, tcp_timeout=20, auth_strategy=auth_strategy, look_for_keys=False, key=key, allow_agent=allow_agent) self.ssh_client.connect_with_timeout( cooldown=20, timeout=connection_timeout) if not self.ssh_client.is_connected(): message = ('SSH timeout after {timeout} seconds: ' 'Could not connect to {ip_address}.') raise SshConnectionException(message.format( timeout=connection_timeout, ip_address=ip_address))
def __init__(self, ip_address=None, username='******', password=None, key=None, connection_timeout=600, retry_interval=10): self.client_log = cclogging.getLogger( cclogging.get_object_namespace(self.__class__)) if ip_address is None: raise ServerUnreachable("None") self.ip_address = ip_address self.username = username self.password = password self.connection_timeout = connection_timeout # Verify the server can be pinged before attempting to connect start = int(time.time()) reachable = False while not reachable: reachable = PingClient.ping(ip_address) if reachable: break time.sleep(retry_interval) if int(time.time()) - start >= connection_timeout: raise ServerUnreachable( 'Could not reach the server at {ip_address}'.format( ip_address=ip_address)) if key is not None: auth_strategy = SSHAuthStrategy.KEY_STRING else: auth_strategy = SSHAuthStrategy.PASSWORD self.ssh_client = SSHClient( username=self.username, password=self.password, host=self.ip_address, tcp_timeout=20, auth_strategy=auth_strategy, look_for_keys=False, key=key) self.ssh_client.connect_with_timeout( cooldown=20, timeout=connection_timeout) if not self.ssh_client.is_connected(): message = ('SSH timeout after {timeout} seconds: ' 'Could not connect to {ip_address}.') raise SshConnectionException(message.format( timeout=connection_timeout, ip_address=ip_address))
class LinuxClient(RemoteInstanceClient): def __init__(self, ip_address=None, username='******', password=None, key=None, connection_timeout=600, retry_interval=10): self.client_log = cclogging.getLogger( cclogging.get_object_namespace(self.__class__)) self.ip_address = ip_address self.username = username self.password = password self.connection_timeout = connection_timeout # Verify the IP address has a valid format try: IP(ip_address) except ValueError: raise InvalidAddressFormat(ip_address) # Verify the server can be pinged before attempting to connect PingClient.ping_until_reachable(ip_address, timeout=connection_timeout, interval_time=retry_interval) if key is not None: auth_strategy = SSHAuthStrategy.KEY_STRING else: auth_strategy = SSHAuthStrategy.PASSWORD allow_agent = True if not key: allow_agent = False self.ssh_client = SSHClient( username=self.username, password=self.password, host=self.ip_address, tcp_timeout=20, auth_strategy=auth_strategy, look_for_keys=False, key=key, allow_agent=allow_agent) self.ssh_client.connect_with_timeout( cooldown=20, timeout=connection_timeout) if not self.ssh_client.is_connected(): message = ('SSH timeout after {timeout} seconds: ' 'Could not connect to {ip_address}.') raise SshConnectionException(message.format( timeout=connection_timeout, ip_address=ip_address)) def can_authenticate(self): """ Verifies that a connection was made to the remote server @return: Whether the connection was successful @rtype: bool """ return self.ssh_client.is_connected() def get_hostname(self): """ Gets the host name of the server @return: The host name of the server @rtype: string """ output = self.ssh_client.execute_command("hostname") if output: return output.stdout.rstrip() def get_allocated_ram(self): """ Returns the amount of RAM the server has @return: The RAM size in MB @rtype: string """ output = self.ssh_client.execute_command('free -m | grep Mem') if output: return output.stdout.split()[1] def get_disk_size(self, disk_path): """ Returns the size of a given disk @return: The disk size in GB @rtype: int """ disks = self.get_all_disks() return disks.get(disk_path) def get_number_of_cpus(self): """ Return the number of CPUs assigned to the server @return: The number of CPUs a server has @rtype: int """ command = 'cat /proc/cpuinfo | grep processor | wc -l' output = self.ssh_client.execute_command(command) if output: return int(output.stdout) def get_uptime(self): """ Get the uptime time of the server. @return: The uptime of the server in seconds @rtype: int """ result = self.ssh_client.execute_command('cat /proc/uptime') if result: uptime = float(result.stdout.split(' ')[0]) return uptime def get_local_users(self): """ Get a list of the local user accounts on the server @return: A list of users @rtype: List of strings """ command = 'cat /etc/passwd | cut -d: -f1' output = self.ssh_client.execute_command(command) if output: return output.stdout.split() def create_file(self, file_name, file_content, file_path=None): """ Creates a new file with the provided content. @param file_name: File name @type file_name: string @param file_content: File content @type file_content: String @rtype: FileDetails """ file_path = file_path or "/root" if not file_path.endswith("/"): file_path = "{0}/".format(file_path) file_path = "{0}{1}".format(file_path, file_name) self.ssh_client.execute_command( 'echo -n {file_content} >> {file_path}'.format( file_content=file_content, file_path=file_path)) return FileDetails("644", file_content, file_path) def create_large_file(self, filepath='/var/tmp/file.txt', multiplier=1): """ @summary: Creates a large file on the remote host with a base size in Gigabytes @param filepath: The filepath including filename @type filepath: String @param multiplier: A decimal number indicating the number of Gigabytes for example: 0.1 multiplier will create a 1.07374e8 byte file @type multiplier: Float @return: Data read from standard output during execution of the command @rtype: String """ seek = int(1024 * 1024 * multiplier) cmd = ('dd if=/dev/zero of={0} count=0 bs=1024 seek={1} && ' 'echo "File created" || echo "File not created"' .format(filepath, str(seek))) output = self.ssh_client.execute_command(cmd).stdout return output.rstrip('\n') == 'File created' def get_file_details(self, file_path): """ Retrieves the contents of a file and its permissions. @param file_path: Path to the file @type file_path: string @return: File details including permissions and content @rtype: FileDetails """ command = ('[ -f {file_path} ] && echo "File exists" || ' 'echo "File does not exist"'.format(file_path=file_path)) output = self.ssh_client.execute_command(command) if output is None: return None output = output.stdout if not output.rstrip('\n') == 'File exists': raise FileNotFoundException( "File {file_path} not found on instance.".format( file_path=file_path)) file_permissions = self.ssh_client.execute_command( 'stat -c %a {file_path}'.format( file_path=file_path)).stdout.rstrip("\n") file_contents = self.ssh_client.execute_command( 'cat {file_path}'.format(file_path=file_path)).stdout return FileDetails(file_permissions, file_contents, file_path) def is_file_present(self, file_path): """ Verifies if the given file is present. @param file_path: Path to the file @type file_path: string @return: True if File exists, False otherwise @rtype: bool """ command = ('[ -f {file_path} ] && echo "File exists" || ' 'echo "File does not exist"'.format(file_path=file_path)) output = self.ssh_client.execute_command(command).stdout if output: return output.rstrip('\n') == 'File exists' def delete_file(self, file_path): """ Deletes the file. @param file_path: Path to the file @type file_path: string """ command = 'rm {file_path}'.format(file_path=file_path) self.ssh_client.execute_command(command) def generate_mountpoint(self, prefix=None): """ Generates a string to use as a path for mounting drives on the remote linux os @param prefix: Optional string to prepend to the mountpoint name default prefix is 'mountpoint' @type source_path: string """ return "/{0}".format( datagen.random_string(prefix=prefix or "mountpoint_")) def mount_disk(self, source_path, destination_path): """ Mounts a disk to specified destination. @param source_path: Path to file source @type source_path: string @param destination_path: Path to mount destination @type destination_path: string """ self.ssh_client.execute_command( 'mount {source_path} {destination_path}'.format( source_path=source_path, destination_path=destination_path)) def unmount_disk(self, disk_path): """ Unmounts the disk at the specified location. @param disk_path: Path to file source @type disk_path: string """ self.ssh_client.execute_command( 'umount {disk_path}'.format(disk_path=disk_path)) def get_xen_user_metadata(self): """ Retrieves the user-metadata section from the XenStore. @return: The contents of the user-metadata @rtype: dict """ command = 'xenstore-ls vm-data/user-metadata' output = self.ssh_client.execute_command(command) if not output: return None output = output.stdout meta_list = output.split('\n') meta = {} for item in meta_list: # Skip any blank lines if item: meta_item = item.split("=") key = meta_item[0].strip() value = meta_item[1].strip('" ') meta[key] = value return meta def get_xenstore_disk_config_value(self): """ Returns the XenStore value for disk config. @return: Whether the virtual machine uses auto disk config @rtype: bool """ command = 'xenstore-read vm-data/auto-disk-config' output = self.ssh_client.execute_command(command) if output is None: return None output = output.stdout return output.strip().lower() == 'true' def create_directory(self, path): """ Creates a directory at the specified path. @param path: Directory path @type path: string """ command = "mkdir -p {path}".format(path=path) output = self.ssh_client.execute_command(command) if output is None: return None return output.stdout def is_directory_present(self, directory_path): """ Check if given directory exists. @param directory_path: Path to the directory @type directory_path: string @return: Result of directory check @rtype: bool """ command = ("[ -d {path} ] && echo 'Directory found'" "|| echo 'Directory {path} not found'".format( path=directory_path)) output = self.ssh_client.execute_command(command) if output is None: return None output = output.stdout return output.rstrip('\n') == 'Directory found' def get_directory_details(self, dir_path): """ Retrieves informational data about a directory. @param dir_path: Path to the directory @type dir_path: string @return: Directory details @rtype: DirectoryDetails """ output = self.is_directory_present(dir_path) if output is None: raise DirectoryNotFoundException( "Directory: {0} not found.".format(dir_path)) dir_permissions = self.ssh_client.execute_command( "stat -c %a {0}".format(dir_path)).stdout.rstrip("\n") dir_size = float(self.ssh_client.execute_command( "du -s {0}".format(dir_path)).stdout.split('\t', 1)[0]) return DirectoryDetails(dir_permissions, dir_size, dir_path) def get_all_disks(self): """ Returns a list of all block devices for a server. @return: The accessible block devices @rtype: dict """ disks_raw = self.ssh_client.execute_command('fdisk -l') if disks_raw is None: return None disks_raw = disks_raw.stdout p = re.compile('Disk /dev/\w+: \d+.*') disks_list = p.findall(disks_raw) disks = {} for disk in disks_list: items = disk.split() disk_name = items[1].replace(':', '') size = int(items[4])/(1 << 30) disks[disk_name] = size return disks def get_all_disk_details(self): """ Returns a list of dictionaries, each containing the name, type, and size in bytes of all devices) @return: The accessible disks @rtype: list """ disk_list = [] info = self.ssh_client.execute_command( 'lsblk -bio KNAME,TYPE,SIZE').stdout if info is None: return None info = info.splitlines()[1::] for line in info: group = re.split('\s*', line) if len(group) != 3: return None disk_list.append( {'name': group[0], 'type': group[1], 'size': group[2]}) return disk_list def format_disk(self, disk, filesystem_type): """ Formats a disk to the provided filesystem type. @param disk: The path to the disk to be formatted @type disk: string @param filesystem_type: The filesystem type to format the disk to @type filesystem_type: string @return: Output of command execution @rtype: string """ out = self.ssh_client.execute_command('mkfs -t {type} {disk}'.format( type=filesystem_type, disk=disk)) if out is None: return None return out.stdout def get_disk_fs_type(self, device_path): """ Expects full path to device like so: /dev/sda1 Returns file system type as a string. """ out = self.ssh_client.execute_command( 'sudo lsblk -bio FSTYPE {0} -n'.format(device_path)) if out is None: return None return out.stdout def get_md5sum_for_remote_file(self, file_location, file_name): """ @summary: Gets the md5sum of file on the server @param filepath: The path name including file name @type filepath: String """ output = self.ssh_client.execute_command('md5sum {0}/{1}'.format( file_location, file_name)).stdout if output: return output.split()[0] def get_network_bytes_for_interface(self, interface): """ @summary: Retrieves the byte counters from a given network interface. @param interface: The network interface of an instance. @type interface: String @return: The received bytes and transmitted bytes from the network interface, respectively @rtype: Tuple """ cmd = "ifconfig {0}".format(interface) output = self.ssh_client.execute_command(cmd).stdout rx_bytes = re.findall('RX bytes:([0-9]*) ', output)[0] tx_bytes = re.findall('TX bytes:([0-9]*) ', output)[0] return rx_bytes, tx_bytes def generate_bandwidth_from_server_to_client(self, public_ip_address, gb_file_size, server_filepath, client_filepath): """ @summary: Creates and transfers a file from server to client @param public_ip_address: The eth0 address of the instance @type public_ip_address: String @param gb_file_size: The size of the file to be generated in Gigabytes @type gb_file_size: Float @param server_filepath: The path name including file name on server @type server_filepath: String @param client_filepath: The path name including file name on client @type client_filepath: String @return: On successful bandwidth generation, return tx_bytes @rtype: int @todo: Use json bridge to poll global db for bw_usage_cache update instead of sleeping on ssh_timeout """ time.sleep(self.connection_timeout) # delete same filename locally if it existed in a prior run if os.path.exists(client_filepath): os.remove(client_filepath) # get the initial values from the network interface rx_bytes, tx_bytes = self.get_network_bytes_for_interface('eth0') if not self.create_large_file(server_filepath, gb_file_size): raise Exception("File was not created on server: {0}, {1}" .format(public_ip_address, server_filepath)) server_filelocation, server_filename = os.path.split(server_filepath) md5sum_server = self.get_md5sum_for_remote_file(server_filelocation, server_filename) if not md5sum_server: raise Exception("No md5sum from file on server: {0}, {1}" .format(public_ip_address, server_filepath)) if not self.ssh_client.retrieve_file_from(client_filepath, server_filepath): raise Exception("The file {0} was not downloaded from " "the server {1}, {2}".format(client_filepath, public_ip_address, server_filepath)) md5sum_client = get_md5_hash(data=client_filepath, block_size_multiplier=16) if md5sum_server != md5sum_client: raise Exception("The md5sums did not match: {0}, {1} != {2}, {3}" .format(md5sum_server, public_ip_address, md5sum_client, "localhost")) # clean up and delete the local file we just downloaded if os.path.exists(client_filepath): os.remove(client_filepath) # get the byte values after generating bandwidth and subtract rx_bytes_after, tx_bytes_after = ( self.get_network_bytes_for_interface('eth0')) tx_bytes = int(tx_bytes_after) - int(tx_bytes) time.sleep(self.connection_timeout) return tx_bytes def check_rhel_activation(self): """ @summary: Returns boolean - true or false depending of the activation if it is passed or failed @return: Activation status @rtype: bool """ status_of_activation = False command = 'rhn_check -v' echo_call = 'echo $?' rhel_network_satellite = self.ssh_client.execute_command(command) if rhel_network_satellite.stdout is '': output = self.ssh_client.execute_command(echo_call) if int(output.stdout) is 0: status_of_activation = True return status_of_activation def get_distribution_and_version(self): """ Get the distribution and version of the server Works for all Linux distibutions, not working for freebsd example root@ivo-ubuntu-2:~# lsb_release -d Description: Ubuntu 12.04.4 LTS @return: Full name of the distibution @rtype: int """ result = self.ssh_client.execute_command('lsb_release -d') if result: try: distro = result.stdout.split(':')[1] return distro except IndexError: result = self.ssh_client.execute_command('cat /etc/*-release |' ' head -1') if result: return result.stdout else: return '' def filesystem_sync(self): self.ssh_client.execute_command('sync')
class LinuxClient(RemoteInstanceClient): def __init__(self, ip_address=None, username='******', password=None, key=None, connection_timeout=600, retry_interval=10): self.client_log = cclogging.getLogger( cclogging.get_object_namespace(self.__class__)) if ip_address is None: raise ServerUnreachable("None") self.ip_address = ip_address self.username = username self.password = password self.connection_timeout = connection_timeout # Verify the server can be pinged before attempting to connect start = int(time.time()) reachable = False while not reachable: reachable = PingClient.ping(ip_address) if reachable: break time.sleep(retry_interval) if int(time.time()) - start >= connection_timeout: raise ServerUnreachable( 'Could not reach the server at {ip_address}'.format( ip_address=ip_address)) if key is not None: auth_strategy = SSHAuthStrategy.KEY_STRING else: auth_strategy = SSHAuthStrategy.PASSWORD self.ssh_client = SSHClient( username=self.username, password=self.password, host=self.ip_address, tcp_timeout=20, auth_strategy=auth_strategy, look_for_keys=False, key=key) self.ssh_client.connect_with_timeout( cooldown=20, timeout=connection_timeout) if not self.ssh_client.is_connected(): message = ('SSH timeout after {timeout} seconds: ' 'Could not connect to {ip_address}.') raise SshConnectionException(message.format( timeout=connection_timeout, ip_address=ip_address)) def can_authenticate(self): """ Verifies that a connection was made to the remote server @return: Whether the connection was successful @rtype: bool """ return self.ssh_client.is_connected() def get_hostname(self): """ Gets the host name of the server @return: The host name of the server @rtype: string """ output = self.ssh_client.execute_command("hostname") if output: return output.stdout.rstrip() def get_allocated_ram(self): """ Returns the amount of RAM the server has @return: The RAM size in MB @rtype: string """ output = self.ssh_client.execute_command('free -m | grep Mem') if output: return output.stdout.split()[1] def get_disk_size(self, disk_path): """ Returns the size of a given disk @return: The disk size in GB @rtype: int """ disks = self.get_all_disks() return disks.get(disk_path) def get_number_of_cpus(self): """ Return the number of CPUs assigned to the server @return: The number of CPUs a server has @rtype: int """ command = 'cat /proc/cpuinfo | grep processor | wc -l' output = self.ssh_client.execute_command(command) if output: return int(output.stdout) def get_uptime(self): """ Get the uptime time of the server. @return: The uptime of the server in seconds @rtype: int """ result = self.ssh_client.execute_command('cat /proc/uptime') if result: uptime = float(result.stdout.split(' ')[0]) return uptime def create_file(self, file_name, file_content, file_path=None): """ Creates a new file with the provided content. @param file_name: File name @type file_name: string @param file_content: File content @type file_content: String @rtype: FileDetails """ if file_path is None: file_path = "/root/{file_name}".format(file_name=file_name) self.ssh_client.execute_command( 'echo -n {file_content} >> {file_path}'.format( file_content=file_content, file_name=file_name)) return FileDetails("644", file_content, file_path) def create_large_file(self, filepath='/var/tmp/file.txt', multiplier=1): """ @summary: Creates a large file on the remote host with a base size in Gigabytes @param filepath: The filepath including filename @type filepath: String @param multiplier: A decimal number indicating the number of Gigabytes for example: 0.1 multiplier will create a 1.07374e8 byte file @type multiplier: Float @return: Data read from standard output during execution of the command @rtype: String """ seek = int(1024 * 1024 * multiplier) cmd = ('dd if=/dev/zero of={0} count=0 bs=1024 seek={1} && ' 'echo "File created" || echo "File not created"' .format(filepath, str(seek))) output = self.ssh_client.execute_command(cmd).stdout return output.rstrip('\n') == 'File created' def get_file_details(self, file_path): """ Retrieves the contents of a file and its permissions. @param file_path: Path to the file @type file_path: string @return: File details including permissions and content @rtype: FileDetails """ command = ('[ -f {file_path} ] && echo "File exists" || ' 'echo "File does not exist"'.format(file_path=file_path)) output = self.ssh_client.execute_command(command) if output is None: return None output = output.stdout if not output.rstrip('\n') == 'File exists': raise FileNotFoundException( "File {file_path} not found on instance.".format( file_path=file_path)) file_permissions = self.ssh_client.execute_command( 'stat -c %a {file_path}'.format( file_path=file_path)).stdout.rstrip("\n") file_contents = self.ssh_client.execute_command( 'cat {file_path}'.format(file_path=file_path)).stdout return FileDetails(file_permissions, file_contents, file_path) def is_file_present(self, file_path): """ Verifies if the given file is present. @param file_path: Path to the file @type file_path: string @return: True if File exists, False otherwise @rtype: bool """ command = ('[ -f {file_path} ] && echo "File exists" || ' 'echo "File does not exist"'.format(file_path=file_path)) output = self.ssh_client.execute_command(command).stdout if output: return output.rstrip('\n') == 'File exists' def mount_disk(self, source_path, destination_path): """ Mounts a disk to specified destination. @param source_path: Path to file source @type source_path: string @param destination_path: Path to mount destination @type destination_path: string """ self.ssh_client.execute_command( 'mount {source_path} {destination_path}'.format( source_path=source_path, destination_path=destination_path)) def unmount_disk(self, disk_path): """ Unmounts the disk at the specified location. @param disk_path: Path to file source @type disk_path: string """ self.ssh_client.execute_command( 'umount {disk_path}'.format(disk_path=disk_path)) def get_xen_user_metadata(self): """ Retrieves the user-metadata section from the XenStore. @return: The contents of the user-metadata @rtype: dict """ command = 'xenstore-ls vm-data/user-metadata' output = self.ssh_client.execute_command(command) if not output: return None output = output.stdout meta_list = output.split('\n') meta = {} for item in meta_list: # Skip any blank lines if item: meta_item = item.split("=") key = meta_item[0].strip() value = meta_item[1].strip('" ') meta[key] = value return meta def get_xenstore_disk_config_value(self): """ Returns the XenStore value for disk config. @return: Whether the virtual machine uses auto disk config @rtype: bool """ command = 'xenstore-read vm-data/auto-disk-config' output = self.ssh_client.execute_command(command) if output is None: return None output = output.stdout return output.strip().lower() == 'true' def create_directory(self, path): """ Creates a directory at the specified path. @param path: Directory path @type path: string """ command = "mkdir -p {path}".format(path=path) output = self.ssh_client.execute_command(command) if output is None: return None return output.stdout def is_directory_present(self, directory_path): """ Check if given directory exists. @param directory_path: Path to the directory @type directory_path: string @return: Result of directory check @rtype: bool """ command = ("[ -d {path} ] && echo 'Directory found'" "|| echo 'Directory {path} not found'".format( path=directory_path)) output = self.ssh_client.execute_command(command) if output is None: return None output = output.stdout return output.rstrip('\n') == 'Directory found' def get_directory_details(self, dir_path): """ Retrieves informational data about a directory. @param dir_path: Path to the directory @type dir_path: string @return: Directory details @rtype: DirectoryDetails """ output = self.is_directory_present(dir_path) if output is None: raise DirectoryNotFoundException( "Directory: {0} not found.".format(dir_path)) dir_permissions = self.ssh_client.execute_command( "stat -c %a {0}".format(dir_path)).stdout.rstrip("\n") dir_size = float(self.ssh_client.execute_command( "du -s {0}".format(dir_path)).stdout.split('\t', 1)[0]) return DirectoryDetails(dir_permissions, dir_size, dir_path) def get_all_disks(self): """ Returns a list of all block devices for a server. @return: The accessible block devices @rtype: dict """ disks_raw = self.ssh_client.execute_command('fdisk -l') if disks_raw is None: return None disks_raw = disks_raw.stdout p = re.compile('Disk /dev/\w+: \d+.*') disks_list = p.findall(disks_raw) disks = {} for disk in disks_list: items = disk.split() disk_name = items[1].replace(':', '') size = int(items[4])/(1 << 30) disks[disk_name] = size return disks def format_disk(self, disk, filesystem_type): """ Formats a disk to the provided filesystem type. @param disk: The path to the disk to be formatted @type disk: string @param filesystem_type: The filesystem type to format the disk to @type filesystem_type: string @return: Output of command execution @rtype: string """ out = self.ssh_client.execute_command('mkfs -t {type} {disk}'.format( type=filesystem_type, disk=disk)) if out is None: return None return out.stdout def get_md5sum_for_remote_file(self, filepath): """ @summary: Gets the md5sum of file on the server @param filepath: The path name including file name @type filepath: String """ output = self.ssh_client.execute_command('md5sum {0}'.format( filepath)).stdout if output: return output.split()[0] def get_network_bytes_for_interface(self, interface): """ @summary: Retrieves the byte counters from a given network interface. @param interface: The network interface of an instance. @type interface: String @return: The received bytes and transmitted bytes from the network interface, respectively @rtype: Tuple """ cmd = "ifconfig {0}".format(interface) output = self.ssh_client.execute_command(cmd).stdout rx_bytes = re.findall('RX bytes:([0-9]*) ', output)[0] tx_bytes = re.findall('TX bytes:([0-9]*) ', output)[0] return rx_bytes, tx_bytes def generate_bandwidth_from_server_to_client(self, public_ip_address, gb_file_size, server_filepath, client_filepath): """ @summary: Creates and transfers a file from server to client @param linux_client: A linux client for a given instance @type address: Instance @param public_ip_address: The eth0 address of the instance @type public_ip_address: String @param gb_file_size: The size of the file to be generated in Gigabytes @type gb_file_size: Float @param server_filepath: The path name including file name on server @type server_filepath: String @param client_filepath: The path name including file name on client @type client_filepath: String @return: On successful bandwidth generation, return tx_bytes @rtype: int @todo: Use json bridge to poll global db for bw_usage_cache update instead of sleeping on ssh_timeout """ time.sleep(self.connection_timeout) # delete same filename locally if it existed in a prior run if os.path.exists(client_filepath): os.remove(client_filepath) # get the initial values from the network interface rx_bytes, tx_bytes = self.get_network_bytes_for_interface('eth0') if not self.create_large_file(server_filepath, gb_file_size): raise Exception("File was not created on server: {0}, {1}" .format(public_ip_address, server_filepath)) md5sum_server = self.get_md5sum_for_remote_file(server_filepath) if not md5sum_server: raise Exception("No md5sum from file on server: {0}, {1}" .format(public_ip_address, server_filepath)) if not self.ssh_client.retrieve_file_from(client_filepath, server_filepath): raise Exception("The file {0} was not downloaded from " "the server {1}, {2}".format(client_filepath, public_ip_address, server_filepath)) md5sum_client = get_md5_hash(data=client_filepath, block_size_multiplier=16) if md5sum_server != md5sum_client: raise Exception("The md5sums did not match: {0}, {1} != {2}, {3}" .format(md5sum_server, public_ip_address, md5sum_client, "localhost")) # clean up and delete the local file we just downloaded if os.path.exists(client_filepath): os.remove(client_filepath) # get the byte values after generating bandwidth and subtract rx_bytes_after, tx_bytes_after = ( self.get_network_bytes_for_interface('eth0')) tx_bytes = int(tx_bytes_after) - int(tx_bytes) time.sleep(self.connection_timeout) return tx_bytes
class LinuxClient(RemoteInstanceClient): def __init__(self, ip_address=None, username='******', password=None, key=None, connection_timeout=600, retry_interval=10): self.client_log = cclogging.getLogger( cclogging.get_object_namespace(self.__class__)) if ip_address is None: raise ServerUnreachable("None") self.ip_address = ip_address self.username = username self.password = password self.connection_timeout = connection_timeout # Verify the server can be pinged before attempting to connect start = int(time.time()) reachable = False while not reachable: reachable = PingClient.ping(ip_address) if reachable: break time.sleep(retry_interval) if int(time.time()) - start >= connection_timeout: raise ServerUnreachable(ip_address) if key is not None: auth_strategy = SSHAuthStrategy.KEY_STRING else: auth_strategy = SSHAuthStrategy.PASSWORD self.ssh_client = SSHClient( username=self.username, password=self.password, host=self.ip_address, tcp_timeout=20, auth_strategy=auth_strategy, look_for_keys=False, key=key) self.ssh_client.connect_with_timeout( cooldown=20, timeout=connection_timeout) if not self.ssh_client.is_connected(): message = ('SSH timeout after {timeout} seconds: ' 'Could not connect to {ip_address}.') raise SshConnectionException(message.format( timeout=connection_timeout, ip_address=ip_address)) def can_authenticate(self): """ Verifies that a connection was made to the remote server @return: Whether the connection was successful @rtype: bool """ return self.ssh_client.is_connected() def get_hostname(self): """ Gets the host name of the server @return: The host name of the server @rtype: string """ output = self.ssh_client.execute_command("hostname") if output: return output.stdout.rstrip() def get_allocated_ram(self): """ Returns the amount of RAM the server has @return: The RAM size in MB @rtype: string """ output = self.ssh_client.execute_command('free -m | grep Mem') if output: return output.stdout.split()[1] def get_disk_size(self, disk_path): """ Returns the size of a given disk @return: The disk size in GB @rtype: int """ disks = self.get_all_disks() return disks.get(disk_path) def get_number_of_cpus(self): """ Return the number of CPUs assigned to the server @return: The number of CPUs a server has @rtype: int """ command = 'cat /proc/cpuinfo | grep processor | wc -l' output = self.ssh_client.execute_command(command) if output: return int(output.stdout) def get_uptime(self): """ Get the uptime time of the server. @return: The uptime of the server in seconds @rtype: int """ result = self.ssh_client.execute_command('cat /proc/uptime') if result: uptime = float(result.stdout.split(' ')[0]) return uptime def create_file(self, file_name, file_content, file_path=None): """ Creates a new file with the provided content. @param file_name: File name @type file_name: string @param file_content: File content @type file_content: String @rtype: FileDetails """ if file_path is None: file_path = "/root/{file_name}".format(file_name=file_name) self.ssh_client.execute_command( 'echo -n {file_content} >> {file_path}'.format( file_content=file_content, file_name=file_name)) return FileDetails("644", file_content, file_path) def create_large_file(self, filepath='/var/tmp/file.txt', multiplier=1): """ @summary: Creates a large file on the remote host with a base size in Gigabytes @param filepath: The filepath including filename @type filepath: String @param multiplier: A decimal number indicating the number of Gigabytes for example: 0.1 multiplier will create a 1.07374e8 byte file @type multiplier: Float @return: Data read from standard output during execution of the command @rtype: String """ seek = int(1024 * 1024 * multiplier) cmd = ('dd if=/dev/zero of={0} count=0 bs=1024 seek={1} && ' 'echo "File created" || echo "File not created"' .format(filepath, str(seek))) output = self.ssh_client.execute_command(cmd).stdout return output.rstrip('\n') == 'File created' def get_file_details(self, file_path): """ Retrieves the contents of a file and its permissions. @param file_path: Path to the file @type file_path: string @return: File details including permissions and content @rtype: FileDetails """ command = ('[ -f {file_path} ] && echo "File exists" || ' 'echo "File does not exist"'.format(file_path=file_path)) output = self.ssh_client.execute_command(command) if output is None: return None output = output.stdout if not output.rstrip('\n') == 'File exists': raise FileNotFoundException( "File {file_path} not found on instance.".format( file_path=file_path)) file_permissions = self.ssh_client.execute_command( 'stat -c %a {file_path}'.format( file_path=file_path)).stdout.rstrip("\n") file_contents = self.ssh_client.execute_command( 'cat {file_path}'.format(file_path=file_path)).stdout return FileDetails(file_permissions, file_contents, file_path) def is_file_present(self, file_path): """ Verifies if the given file is present. @param file_path: Path to the file @type file_path: string @return: True if File exists, False otherwise @rtype: bool """ command = ('[ -f {file_path} ] && echo "File exists" || ' 'echo "File does not exist"'.format(file_path=file_path)) output = self.ssh_client.execute_command(command).stdout if output: return output.rstrip('\n') == 'File exists' def mount_disk(self, source_path, destination_path): """ Mounts a disk to specified destination. @param source_path: Path to file source @type source_path: string @param destination_path: Path to mount destination @type destination_path: string """ self.ssh_client.execute_command( 'mount {source_path} {destination_path}'.format( source_path=source_path, destination_path=destination_path)) def unmount_disk(self, disk_path): """ Unmounts the disk at the specified location. @param disk_path: Path to file source @type disk_path: string """ self.ssh_client.execute_command( 'umount {disk_path}'.format(disk_path=disk_path)) def get_xen_user_metadata(self): """ Retrieves the user-metadata section from the XenStore. @return: The contents of the user-metadata @rtype: dict """ command = 'xenstore-ls vm-data/user-metadata' output = self.ssh_client.execute_command(command) if not output: return None output = output.stdout meta_list = output.split('\n') meta = {} for item in meta_list: # Skip any blank lines if item: meta_item = item.split("=") key = meta_item[0].strip() value = meta_item[1].strip('" ') meta[key] = value return meta def get_xenstore_disk_config_value(self): """ Returns the XenStore value for disk config. @return: Whether the virtual machine uses auto disk config @rtype: bool """ command = 'xenstore-read vm-data/auto-disk-config' output = self.ssh_client.execute_command(command) if output is None: return None output = output.stdout return output.strip().lower() == 'true' def create_directory(self, path): """ Creates a directory at the specified path. @param path: Directory path @type path: string """ command = "mkdir -p {path}".format(path=path) output = self.ssh_client.execute_command(command) if output is None: return None return output.stdout def is_directory_present(self, directory_path): """ Check if given directory exists. @param directory_path: Path to the directory @type directory_path: string @return: Result of directory check @rtype: bool """ command = ("[ -d {path} ] && echo 'Directory found'" "|| echo 'Directory {path} not found'".format( path=directory_path)) output = self.ssh_client.execute_command(command) if output is None: return None output = output.stdout return output.rstrip('\n') == 'Directory found' def get_directory_details(self, dir_path): """ Retrieves informational data about a directory. @param dir_path: Path to the directory @type dir_path: string @return: Directory details @rtype: DirectoryDetails """ output = self.is_directory_present(dir_path) if output is None: raise DirectoryNotFoundException( "Directory: {0} not found.".format(dir_path)) dir_permissions = self.ssh_client.execute_command( "stat -c %a {0}".format(dir_path)).stdout.rstrip("\n") dir_size = float(self.ssh_client.execute_command( "du -s {0}".format(dir_path)).stdout.split('\t', 1)[0]) return DirectoryDetails(dir_permissions, dir_size, dir_path) def get_all_disks(self): """ Returns a list of all block devices for a server. @return: The accessible block devices @rtype: dict """ disks_raw = self.ssh_client.execute_command('fdisk -l') if disks_raw is None: return None disks_raw = disks_raw.stdout p = re.compile('Disk /dev/\w+: \d+.*') disks_list = p.findall(disks_raw) disks = {} for disk in disks_list: items = disk.split() disk_name = items[1].replace(':', '') size = int(items[4])/(1 << 30) disks[disk_name] = size return disks def format_disk(self, disk, filesystem_type): """ Formats a disk to the provided filesystem type. @param disk: The path to the disk to be formatted @type disk: string @param filesystem_type: The filesystem type to format the disk to @type filesystem_type: string @return: Output of command execution @rtype: string """ out = self.ssh_client.execute_command('mkfs -t {type} {disk}'.format( type=filesystem_type, disk=disk)) if out is None: return None return out.stdout def get_md5sum_for_remote_file(self, filepath): """ @summary: Gets the md5sum of file on the server @param filepath: The path name including file name @type filepath: String """ output = self.ssh_client.execute_command('md5sum {0}'.format( filepath)).stdout if output: return output.split()[0] def get_network_bytes_for_interface(self, interface): """ @summary: Retrieves the byte counters from a given network interface. @param interface: The network interface of an instance. @type interface: String @return: The received bytes and transmitted bytes from the network interface, respectively @rtype: Tuple """ cmd = "ifconfig {0}".format(interface) output = self.ssh_client.execute_command(cmd).stdout rx_bytes = re.findall('RX bytes:([0-9]*) ', output)[0] tx_bytes = re.findall('TX bytes:([0-9]*) ', output)[0] return rx_bytes, tx_bytes def generate_bandwidth_from_server_to_client(self, public_ip_address, gb_file_size, server_filepath, client_filepath): """ @summary: Creates and transfers a file from server to client @param linux_client: A linux client for a given instance @type address: Instance @param public_ip_address: The eth0 address of the instance @type public_ip_address: String @param gb_file_size: The size of the file to be generated in Gigabytes @type gb_file_size: Float @param server_filepath: The path name including file name on server @type server_filepath: String @param client_filepath: The path name including file name on client @type client_filepath: String @return: On successful bandwidth generation, return tx_bytes @rtype: int @todo: Use json bridge to poll global db for bw_usage_cache update instead of sleeping on ssh_timeout """ time.sleep(self.connection_timeout) # delete same filename locally if it existed in a prior run if os.path.exists(client_filepath): os.remove(client_filepath) # get the initial values from the network interface rx_bytes, tx_bytes = self.get_network_bytes_for_interface('eth0') if not self.create_large_file(server_filepath, gb_file_size): raise Exception("File was not created on server: {0}, {1}" .format(public_ip_address, server_filepath)) md5sum_server = self.get_md5sum_for_remote_file(server_filepath) if not md5sum_server: raise Exception("No md5sum from file on server: {0}, {1}" .format(public_ip_address, server_filepath)) if not self.ssh_client.retrieve_file_from(client_filepath, server_filepath): raise Exception("The file {0} was not downloaded from " "the server {1}, {2}".format(client_filepath, public_ip_address, server_filepath)) md5sum_client = get_md5_hash(data=client_filepath, block_size_multiplier=16) if md5sum_server != md5sum_client: raise Exception("The md5sums did not match: {0}, {1} != {2}, {3}" .format(md5sum_server, public_ip_address, md5sum_client, "localhost")) # clean up and delete the local file we just downloaded if os.path.exists(client_filepath): os.remove(client_filepath) # get the byte values after generating bandwidth and subtract rx_bytes_after, tx_bytes_after = ( self.get_network_bytes_for_interface('eth0')) tx_bytes = int(tx_bytes_after) - int(tx_bytes) time.sleep(self.connection_timeout) return tx_bytes