~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to tests/lib/sys_mgmt/port_management.py

  • Committer: patrick crews
  • Date: 2011-01-15 21:27:41 UTC
  • mto: (2119.2.1 drizzle)
  • mto: This revision was merged to the branch mainline in revision 2121.
  • Revision ID: gleebix@gmail.com-20110115212741-htz3af0cib4fwdlv
Updated tree so that test-run.pl and test-run.py may live together in peace for a time

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#! /usr/bin/python
 
2
# -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*-
 
3
# vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
 
4
#
 
5
# Copyright (C) 2010 Patrick Crews
 
6
#
 
7
"""port_management.py
 
8
   code for dealing with the various tasks
 
9
   around handing out and managing server ports
 
10
   that we need to run tests
 
11
 
 
12
"""
 
13
 
 
14
# imports
 
15
import os
 
16
import sys
 
17
 
 
18
class portManager:
 
19
    """ class for doing the work of handing out and tracking ports """
 
20
    def __init__(self, system_manager, debug = 0):
 
21
        # This is a file that can be read into a dictionary
 
22
        # it is in port:owner format
 
23
        self.skip_keys = [ 'port_file_delimiter'
 
24
                         , 'system_manager'
 
25
                         ]
 
26
        self.port_catalog = "/tmp/drizzle_test_port_catalog.dat"
 
27
        self.port_file_delimiter = ':' # what we use to separate port:owner 
 
28
        self.debug = debug
 
29
        self.logging = system_manager.logging
 
30
        self.system_manager = system_manager
 
31
        if self.debug:
 
32
            self.logging.debug_class(self)
 
33
        
 
34
 
 
35
    def get_port_block(self, requester, base_port, block_size):
 
36
        """ Try to return a block of ports of size
 
37
            block_size, starting with base_port
 
38
 
 
39
            We take a target port and increment it
 
40
            until we find an unused port.  We make
 
41
            no guarantee of continuous ports, only
 
42
            that we will try to return block_size
 
43
            ports for use
 
44
    
 
45
            We can probably get fancier / smarter in the future
 
46
            but this should work for now :-/
 
47
 
 
48
        """
 
49
        assigned_ports = []
 
50
        current_port = base_port
 
51
        while len(assigned_ports) != block_size:
 
52
            new_port = (self.get_port(requester, current_port))
 
53
            assigned_ports.append(new_port)
 
54
            current_port = new_port+1
 
55
        return assigned_ports
 
56
 
 
57
    def get_port(self, requester, desired_port):
 
58
        """ Try to lock in the desired_port
 
59
            if not, we increment the value until
 
60
            we find an unused port.
 
61
            We take max / min port values from test-run.pl
 
62
            This is a bit bobo, but will work for now...
 
63
 
 
64
        """
 
65
        
 
66
        searching_for_port = 1
 
67
        attempts_remain = 100
 
68
        max_port_value = 32767
 
69
        min_port_value = 5001
 
70
        while searching_for_port and attempts_remain:
 
71
            # Check if the port is used
 
72
            if self.check_port_status(desired_port): 
 
73
                # assign it
 
74
                self.assign_port(requester, desired_port)
 
75
                return desired_port
 
76
            else: # increment the port and try again
 
77
                desired_port = desired_port + 1
 
78
                if desired_port >= max_port_value:
 
79
                    desired_port = min_port_value
 
80
                attempts_remain = attempts_remain - 1
 
81
        self.logging.error("Failed to assign a port in %d attempts")
 
82
        sys.exit(1)
 
83
 
 
84
    def check_port_status(self, port):
 
85
        """ Check if a port is in use, via the catalog file 
 
86
            which all copies of test-run.py should use
 
87
 
 
88
            Not *really* sure how well this works with multiple
 
89
            test-run.py instances...we'll see if we even need it 
 
90
            to work 
 
91
 
 
92
        """
 
93
        # read the catalog file
 
94
        port_catalog = self.process_port_catalog()
 
95
        if port not in port_catalog and not self.is_port_used(port):
 
96
            return 1
 
97
        else:
 
98
            return 0
 
99
 
 
100
    def is_port_used(self, port):
 
101
        """ See if a given port is used on the system """
 
102
        retcode, output = self.system_manager.execute_cmd("netstat -lant")
 
103
        # parse our output
 
104
        entry_list = output.split("\n")
 
105
        good_data = 0
 
106
        for entry in entry_list:
 
107
            if entry.startswith('Proto'):
 
108
                good_data = 1
 
109
            elif good_data:
 
110
                used_port = int(entry.split()[3].split(':')[-1].strip())
 
111
                if port == used_port:
 
112
                    if entry.split()[-1] != "TIME_WAIT":
 
113
                        return 1
 
114
        return 0
 
115
 
 
116
    def process_port_catalog(self):
 
117
        """ Read in the catalog file so that we can see
 
118
            if the port is in use or not
 
119
 
 
120
        """
 
121
        port_catalog = {}
 
122
        delimiter = ':'
 
123
        if os.path.exists(self.port_catalog):
 
124
            try:
 
125
                port_file = open(self.port_catalog,'r')
 
126
                for line in port_file:
 
127
                    line = line.strip()
 
128
                    port, owner = line.split(self.port_file_delimiter)
 
129
                    port_catalog[port] = owner
 
130
                port_file.close()
 
131
            except IOError, e:
 
132
                self.logging.error("Problem opening port catalog file: %s" %(self.port_catalog))
 
133
                self.logging.error("%s" %e)
 
134
                sys.exit(1)
 
135
        return port_catalog
 
136
 
 
137
    def assign_port(self, owner, port):
 
138
        """Assigns a port - logs it in the port_catalog file"""
 
139
 
 
140
        data_string = "%d:%s\n" %(port, owner)
 
141
        try:
 
142
            port_file = open(self.port_catalog,'a')
 
143
            port_file.write(data_string)
 
144
            port_file.close()
 
145
        except IOError, e:
 
146
            self.logging.error("Problem opening port catalog file: %s" %(self.port_catalog))
 
147
            self.logging.error("%s" %e)
 
148
            sys.exit(1)
 
149
 
 
150
    def free_ports(self, portlist):
 
151
       """ Clean up our port catalog """
 
152
       for port in portlist:
 
153
          self.free_port(port)
 
154
 
 
155
    def free_port(self, port):
 
156
       """ Free a single port from the catalog """
 
157
       if self.debug:
 
158
           self.logging.debug("Freeing port %d" %(port))
 
159
       port_catalog = self.process_port_catalog()
 
160
       port_catalog.pop(str(port),None)
 
161
       self.write_port_catalog(port_catalog)
 
162
 
 
163
    def write_port_catalog(self, port_catalog):
 
164
        port_file = open(self.port_catalog, 'w')
 
165
        for key, value in port_catalog.items():
 
166
            port_file.write(("%s:%s\n" %(key, value)))
 
167
        port_file.close()
 
168
 
 
169
        
 
170
       
 
171
       
 
172