def get_available_capsule_port(port_pool=None): """returns a list of unused ports dedicated for fake capsules This calls a fuser command on the server prompting for a port range. fuser commands returns a list of ports which have a PID assigned (a list of ports which are already used). This function then substracts unavailable ports from the other ones and returns one of available ones randomly. :param port_pool: A list of ports used for fake capsules (for RHEL7+: don't forget to set a correct selinux context before otherwise you'll get Connection Refused error) :return: Random available port from interval <9091, 9190>. :rtype: int """ if port_pool is None: port_pool_range = settings.fake_capsules.port_range if type(port_pool_range) is tuple and len(port_pool_range) is 2: port_pool = range(int(port_pool_range[0]), int(port_pool_range[1])) else: raise TypeError( '''Expected type of port_range is a tuple of 2 elements, got {0} instead''' .format(type(port_pool_range)) ) # returns a list of strings fuser_cmd = ssh.command( 'fuser -n tcp {{{0}..{1}}} 2>&1 | awk -F/ \'{{print$1}}\'' .format(port_pool[0], port_pool[-1]) ) if fuser_cmd.stderr: raise CapsuleTunnelError( 'Failed to create ssh tunnel: Error getting port status: {0}' .format(fuser_cmd.stderr) ) # converts a List of strings to a List of integers try: print(fuser_cmd) used_ports = map( int, [val for val in fuser_cmd.stdout[:-1] if val != 'Cannot stat file '] ) except ValueError: raise CapsuleTunnelError( 'Failed parsing the port numbers from stdout: {0}' .format(fuser_cmd.stdout[:-1]) ) try: # take the list of available ports and return randomly selected one return random.choice( [port for port in port_pool if port not in used_ports] ) except IndexError: raise CapsuleTunnelError( 'Failed to create ssh tunnel: No more ports available for mapping' )
def get_available_capsule_port(self, port_pool=None): """returns a list of unused ports dedicated for fake capsules This calls an ss command on the server prompting for a port range. ss returns a list of ports which have a PID assigned (a list of ports which are already used). This function then substracts unavailable ports from the other ones and returns one of available ones randomly. :param port_pool: A list of ports used for fake capsules (for RHEL7+: don't forget to set a correct selinux context before otherwise you'll get Connection Refused error) :return: Random available port from interval <9091, 9190>. :rtype: int """ if port_pool is None: from robottelo.config import settings port_pool_range = settings.fake_capsules.port_range if isinstance(port_pool_range, str): port_pool_range = tuple(port_pool_range.split('-')) if isinstance(port_pool_range, tuple) and len(port_pool_range) == 2: port_pool = range(int(port_pool_range[0]), int(port_pool_range[1])) else: raise TypeError( 'Expected type of port_range is a tuple of 2 elements,' f'got {type(port_pool_range)} instead' ) # returns a list of strings ss_cmd = self.execute( f"ss -tnaH sport ge {port_pool[0]} sport le {port_pool[-1]}" " | awk '{n=split($4, p, \":\"); print p[n]}' | sort -u" ) if ss_cmd.stderr[1]: raise CapsuleTunnelError( f'Failed to create ssh tunnel: Error getting port status: {ss_cmd.stderr}' ) # converts a List of strings to a List of integers try: used_ports = map( int, [val for val in ss_cmd.stdout.splitlines()[:-1] if val != 'Cannot stat file '] ) except ValueError: raise CapsuleTunnelError( f'Failed parsing the port numbers from stdout: {ss_cmd.stdout.splitlines()[:-1]}' ) try: # take the list of available ports and return randomly selected one return random.choice([port for port in port_pool if port not in used_ports]) except IndexError: raise CapsuleTunnelError( 'Failed to create ssh tunnel: No more ports available for mapping' )
def default_url_on_new_port(oldport, newport): """Creates context where the default capsule is forwarded on a new port :param int oldport: Port to be forwarded. :param int newport: New port to be used to forward `oldport`. :return: A string containing the new capsule URL with port. :rtype: str """ domain = settings.server.hostname with ssh.get_connection() as connection: command = f'ncat -kl -p {newport} -c "ncat {domain} {oldport}"' logger.debug(f'Creating tunnel: {command}') transport = connection.get_transport() channel = transport.open_session() channel.get_pty() channel.exec_command(command) # if exit_status appears until command_timeout, throw error if channel.exit_status_ready(): if channel.recv_exit_status() != 0: stderr = '' while channel.recv_stderr_ready(): stderr += channel.recv_stderr(1) logger.debug(f'Tunnel failed: {stderr}') # Something failed, so raise an exception. raise CapsuleTunnelError(stderr) yield f'https://{domain}:{newport}'
def default_url_on_new_port(oldport, newport): """Creates context where the default capsule is forwarded on a new port :param int oldport: Port to be forwarded. :param int newport: New port to be used to forward `oldport`. :return: A string containing the new capsule URL with port. :rtype: str """ logger = logging.getLogger('robottelo') domain = settings.server.hostname with ssh.get_connection() as connection: command = (u'ncat -kl -p {0} -c "ncat {1} {2}"').format( newport, domain, oldport) logger.debug('Creating tunnel: {0}'.format(command)) transport = connection.get_transport() channel = transport.open_session() channel.get_pty() channel.exec_command(command) # if exit_status appears until command_timeout, throw error if channel.exit_status_ready(): if channel.recv_exit_status() != 0: stderr = u'' while channel.recv_stderr_ready(): stderr += channel.recv_stderr(1) logger.debug('Tunnel failed: {0}'.format(stderr)) # Something failed, so raise an exception. raise CapsuleTunnelError(stderr) yield 'https://{0}:{1}'.format(domain, newport)
def default_url_on_new_port(oldport, newport): """Creates context where the default capsule is forwarded on a new port :param int oldport: Port to be forwarded. :param int newport: New port to be used to forward `oldport`. :return: A string containing the new capsule URL with port. :rtype: str """ domain = settings.server.hostname client = ssh.get_client() pre_ncat_procs = client.execute('pgrep ncat').stdout.splitlines() with client.session.shell() as channel: # if ncat isn't backgrounded, it prevents the channel from closing command = f'ncat -kl -p {newport} -c "ncat {domain} {oldport}" &' # broker 0.1.25 makes these debug messages redundant logger.debug(f'Creating tunnel: {command}') channel.send(command) post_ncat_procs = client.execute('pgrep ncat').stdout.splitlines() ncat_pid = set(post_ncat_procs).difference(set(pre_ncat_procs)) if not len(ncat_pid): stderr = channel.get_exit_status()[1] logger.debug(f'Tunnel failed: {stderr}') # Something failed, so raise an exception. raise CapsuleTunnelError(f'Starting ncat failed: {stderr}') forward_url = f'https://{domain}:{newport}' logger.debug(f'Yielding capsule forward port url: {forward_url}') try: yield forward_url finally: logger.debug(f'Killing ncat pid: {ncat_pid}') client.execute(f'kill {ncat_pid.pop()}')