2
# -*- mode: python; indent-tabs-mode: nil; -*-
3
# vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
5
# Copyright (C) 2010 Patrick Crews
7
# This program is free software; you can redistribute it and/or modify
8
# it under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 2 of the License, or
10
# (at your option) any later version.
12
# This program is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
17
# You should have received a copy of the GNU General Public License
18
# along with this program; if not, write to the Free Software
19
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22
code for dealing with the various tasks
23
around handing out and managing server ports
24
that we need to run tests
33
""" class for doing the work of handing out and tracking ports """
34
def __init__(self, system_manager, debug = 0):
35
# This is a file that can be read into a dictionary
36
# it is in port:owner format
37
self.skip_keys = [ 'port_file_delimiter'
40
self.working_dir = "/tmp"
41
self.file_prefix = "dbqp_port"
42
self.port_file_delimiter = ':' # what we use to separate port:owner
44
self.logging = system_manager.logging
45
self.system_manager = system_manager
46
self.logging.debug_class(self)
49
def get_port_block(self, requester, base_port, block_size):
50
""" Try to return a block of ports of size
51
block_size, starting with base_port
53
We take a target port and increment it
54
until we find an unused port. We make
55
no guarantee of continuous ports, only
56
that we will try to return block_size
59
We can probably get fancier / smarter in the future
60
but this should work for now :-/
64
current_port = base_port
65
while len(assigned_ports) != block_size:
66
new_port = (self.get_port(requester, current_port))
67
assigned_ports.append(new_port)
68
current_port = new_port+1
71
def get_port(self, requester, desired_port):
72
""" Try to lock in the desired_port
73
if not, we increment the value until
74
we find an unused port.
75
We take max / min port values from test-run.pl
76
This is a bit bobo, but will work for now...
79
searching_for_port = 1
81
attempts_remain = attempt_count
82
max_port_value = 32767
84
while searching_for_port and attempts_remain:
85
# Check if the port is used
86
if self.check_port_status(desired_port):
88
self.assign_port(requester, desired_port)
90
else: # increment the port and try again
91
desired_port = desired_port + 1
92
if desired_port >= max_port_value:
93
desired_port = min_port_value
94
attempts_remain = attempts_remain - 1
95
self.logging.error("Failed to assign a port in %d attempts" %attempt_count)
98
def check_port_status(self, port):
99
""" Check if a port is in use, via the port files
100
which all copies of dbqp.py should use
102
Not *really* sure how well this works with multiple
103
dbqp.py instances...we'll see if we even need it
107
# check existing ports dbqp has created
108
dbqp_ports = self.check_dbqp_ports()
109
if port not in dbqp_ports and not self.is_port_used(port):
114
def is_port_used(self, port):
115
""" See if a given port is used on the system """
116
retcode, output = self.system_manager.execute_cmd("netstat -lant")
118
entry_list = output.split("\n")
120
for entry in entry_list:
121
if entry.startswith('Proto'):
124
# We try to catch additional output
125
# like we see with freebsd
126
if entry.startswith('Active'):
130
if self.system_manager.cur_os in [ 'FreeBSD'
136
port_candidate = entry.split()[3].split(split_token)[-1].strip()
137
if port_candidate.isdigit():
138
used_port = int(port_candidate)
140
used_port = None # not a value we can use
141
if port == used_port:
142
if entry.split()[-1] != "TIME_WAIT":
148
def check_dbqp_ports(self):
149
""" Scan the files in /tmp for those files named
150
dbqp_port_NNNN. Existence indicates said port is 'locked'
154
tmp_files = os.listdir('/tmp')
155
for tmp_file in tmp_files:
156
if tmp_file.startswith('dbqp_port'):
157
used_ports.append(int(tmp_file.split('_')[-1]))
160
def assign_port(self, owner, port):
161
"""Assigns a port - create a tmpfile
162
with a name that 'logs' the port
167
out_file = open(self.get_file_name(port),'w')
168
out_file.write("%s:%d\n" %(owner, port))
171
def free_ports(self, portlist):
172
""" Clean up our ports """
173
for port in portlist:
176
def free_port(self, port):
177
""" Free a single port - we delete the file
182
self.logging.debug("Freeing port %d" %(port))
184
os.remove(self.get_file_name(port))
188
def get_file_name(self, port):
189
""" We generate a file name for the port """
191
port_file_name = "%s_%s_%d" %(self.file_prefix, self.system_manager.cur_user, port )
192
return os.path.join(self.working_dir, port_file_name)