This URL has Read-Only access.

Statistics
| Branch: | Tag: | Revision:

root / py / maugis / scenic / ports.py @ 8a08628e

History | View | Annotate | Download (3.6 kB)

1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3
# 
4
# Scenic
5
# Copyright (C) 2008 Société des arts technologiques (SAT)
6
# http://www.sat.qc.ca
7
# All rights reserved.
8
#
9
# This file is free software: you can redistribute it and/or modify
10
# it under the terms of the GNU General Public License as published by
11
# the Free Software Foundation, either version 2 of the License, or
12
# (at your option) any later version.
13
#
14
# Scenic is distributed in the hope that it will be useful,
15
# but WITHOUT ANY WARRANTY; without even the implied warranty of
16
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
# GNU General Public License for more details.
18
#
19
# You should have received a copy of the GNU General Public License
20
# along with Scenic. If not, see <http://www.gnu.org/licenses/>.
21
"""
22
Tools to help with choosing listening port numbers.
23
"""
24

    
25
# for port_is_avaiable
26
import socket 
27

    
28
class PortsAllocatorError(Exception):
29
    """
30
    Any error raised by the PortsAllocator
31
    """
32
    pass
33

    
34
class PortsAllocator(object):
35
    """
36
    Allocates ports from a pool
37
    """
38
    def __init__(self, minimum=10000, increment=10, maximum=65535):
39
        self.minimum = minimum
40
        self.increment = increment
41
        self.maximum = maximum
42
        self.allocated = set()
43

    
44
    def check_port(self, port):
45
        """
46
        Verify that port is available by trying to bind to it. Socket
47
        does not persist. Note that some other process could still
48
        bind to this port in between when this check happens and when
49
        we bind to the port.
50
        
51
        :param port: int
52
        Raises a PortsAllocatorError if port is not available.
53
        """
54
        #TODO: return bool
55
        # Set the socket parameters
56
        host = 'localhost'
57
        addr = (host, port)
58
        busy = False
59

    
60
        # Create socket and bind to address
61
        udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
62
        try:
63
            udp_socket.bind(addr)
64
        except socket.error, e:
65
            busy = True
66
        finally:
67
            # Close socket
68
            udp_socket.close()
69
        if busy:
70
            raise PortsAllocatorError("Port %d is not available. Reason:%s" % (port, e))
71
        
72
    def allocate(self):
73
        """
74
        Allocates a port number and returns it.
75
        """
76
        value = self.minimum
77
        chosen = False
78
        while not chosen:
79
            while value in self.allocated: # loop over allocated ports
80
                value += self.increment
81
                if value > self.maximum:
82
                    raise PortsAllocatorError("Maximum value reached. No more ports available.")
83
            try:
84
                self.check_port(value)
85
            except PortsAllocatorError, e:
86
                print('error: %s' % (e.message)) 
87
            else:
88
                chosen = True
89
        self.allocated.add(value)
90
        return value
91
    
92
    def free(self, value):
93
        """
94
        Frees an allocated port number.
95
        Raises a PortsAllocatorError if not allocated.
96
        """
97
        if value not in self.allocated:
98
            raise PortsAllocatorError("Value %d not in allocated ports set." % (value))
99
        self.allocated.remove(value)
100

    
101
        
102
    def allocate_many(self, num=1):
103
        """
104
        Allocates many ports at once.
105
        Returns a list of allocated ports.
106
        """
107
        ret = []
108
        for i in range(num):
109
            ret.append(self.allocate())
110
        return ret
111
    
112
    def free_many(self, values):
113
        """
114
        Frees many allocated ports at a time.
115
        :param values: list of integers.
116
        """
117
        for value in values:
118
            self.free(value)
119