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 3a62ac44 Alexandre Quessy
#!/usr/bin/env python
2 3a62ac44 Alexandre Quessy
# -*- coding: utf-8 -*-
3 3a62ac44 Alexandre Quessy
# 
4 3a62ac44 Alexandre Quessy
# Scenic
5 3a62ac44 Alexandre Quessy
# Copyright (C) 2008 Société des arts technologiques (SAT)
6 3a62ac44 Alexandre Quessy
# http://www.sat.qc.ca
7 3a62ac44 Alexandre Quessy
# All rights reserved.
8 3a62ac44 Alexandre Quessy
#
9 3a62ac44 Alexandre Quessy
# This file is free software: you can redistribute it and/or modify
10 3a62ac44 Alexandre Quessy
# it under the terms of the GNU General Public License as published by
11 3a62ac44 Alexandre Quessy
# the Free Software Foundation, either version 2 of the License, or
12 3a62ac44 Alexandre Quessy
# (at your option) any later version.
13 3a62ac44 Alexandre Quessy
#
14 3a62ac44 Alexandre Quessy
# Scenic is distributed in the hope that it will be useful,
15 3a62ac44 Alexandre Quessy
# but WITHOUT ANY WARRANTY; without even the implied warranty of
16 3a62ac44 Alexandre Quessy
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 3a62ac44 Alexandre Quessy
# GNU General Public License for more details.
18 3a62ac44 Alexandre Quessy
#
19 3a62ac44 Alexandre Quessy
# You should have received a copy of the GNU General Public License
20 3a62ac44 Alexandre Quessy
# along with Scenic. If not, see <http://www.gnu.org/licenses/>.
21 3a62ac44 Alexandre Quessy
"""
22 3a62ac44 Alexandre Quessy
Tools to help with choosing listening port numbers.
23 3a62ac44 Alexandre Quessy
"""
24 3a62ac44 Alexandre Quessy
25 3a62ac44 Alexandre Quessy
# for port_is_avaiable
26 3a62ac44 Alexandre Quessy
import socket 
27 3a62ac44 Alexandre Quessy
28 3a62ac44 Alexandre Quessy
class PortsAllocatorError(Exception):
29 3a62ac44 Alexandre Quessy
    """
30 3a62ac44 Alexandre Quessy
    Any error raised by the PortsAllocator
31 3a62ac44 Alexandre Quessy
    """
32 3a62ac44 Alexandre Quessy
    pass
33 3a62ac44 Alexandre Quessy
34 3a62ac44 Alexandre Quessy
class PortsAllocator(object):
35 3a62ac44 Alexandre Quessy
    """
36 3a62ac44 Alexandre Quessy
    Allocates ports from a pool
37 3a62ac44 Alexandre Quessy
    """
38 3a62ac44 Alexandre Quessy
    def __init__(self, minimum=10000, increment=10, maximum=65535):
39 3a62ac44 Alexandre Quessy
        self.minimum = minimum
40 3a62ac44 Alexandre Quessy
        self.increment = increment
41 3a62ac44 Alexandre Quessy
        self.maximum = maximum
42 3a62ac44 Alexandre Quessy
        self.allocated = set()
43 3a62ac44 Alexandre Quessy
44 3a62ac44 Alexandre Quessy
    def check_port(self, port):
45 3a62ac44 Alexandre Quessy
        """
46 3a62ac44 Alexandre Quessy
        Verify that port is available by trying to bind to it. Socket
47 3a62ac44 Alexandre Quessy
        does not persist. Note that some other process could still
48 3a62ac44 Alexandre Quessy
        bind to this port in between when this check happens and when
49 3a62ac44 Alexandre Quessy
        we bind to the port.
50 3a62ac44 Alexandre Quessy
        
51 3a62ac44 Alexandre Quessy
        :param port: int
52 3a62ac44 Alexandre Quessy
        Raises a PortsAllocatorError if port is not available.
53 3a62ac44 Alexandre Quessy
        """
54 7f598188 Alexandre Quessy
        #TODO: return bool
55 3a62ac44 Alexandre Quessy
        # Set the socket parameters
56 3a62ac44 Alexandre Quessy
        host = 'localhost'
57 3a62ac44 Alexandre Quessy
        addr = (host, port)
58 3a62ac44 Alexandre Quessy
        busy = False
59 3a62ac44 Alexandre Quessy
60 3a62ac44 Alexandre Quessy
        # Create socket and bind to address
61 3a62ac44 Alexandre Quessy
        udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
62 3a62ac44 Alexandre Quessy
        try:
63 3a62ac44 Alexandre Quessy
            udp_socket.bind(addr)
64 3a62ac44 Alexandre Quessy
        except socket.error, e:
65 3a62ac44 Alexandre Quessy
            busy = True
66 3a62ac44 Alexandre Quessy
        finally:
67 3a62ac44 Alexandre Quessy
            # Close socket
68 3a62ac44 Alexandre Quessy
            udp_socket.close()
69 3a62ac44 Alexandre Quessy
        if busy:
70 3a62ac44 Alexandre Quessy
            raise PortsAllocatorError("Port %d is not available. Reason:%s" % (port, e))
71 3a62ac44 Alexandre Quessy
        
72 3a62ac44 Alexandre Quessy
    def allocate(self):
73 3a62ac44 Alexandre Quessy
        """
74 3a62ac44 Alexandre Quessy
        Allocates a port number and returns it.
75 3a62ac44 Alexandre Quessy
        """
76 3a62ac44 Alexandre Quessy
        value = self.minimum
77 3a62ac44 Alexandre Quessy
        chosen = False
78 3a62ac44 Alexandre Quessy
        while not chosen:
79 3a62ac44 Alexandre Quessy
            while value in self.allocated: # loop over allocated ports
80 3a62ac44 Alexandre Quessy
                value += self.increment
81 3a62ac44 Alexandre Quessy
                if value > self.maximum:
82 3a62ac44 Alexandre Quessy
                    raise PortsAllocatorError("Maximum value reached. No more ports available.")
83 3a62ac44 Alexandre Quessy
            try:
84 3a62ac44 Alexandre Quessy
                self.check_port(value)
85 3a62ac44 Alexandre Quessy
            except PortsAllocatorError, e:
86 afbe021a Tristan Matthews
                print('error: %s' % (e.message)) 
87 3a62ac44 Alexandre Quessy
            else:
88 3a62ac44 Alexandre Quessy
                chosen = True
89 3a62ac44 Alexandre Quessy
        self.allocated.add(value)
90 3a62ac44 Alexandre Quessy
        return value
91 3a62ac44 Alexandre Quessy
    
92 3a62ac44 Alexandre Quessy
    def free(self, value):
93 3a62ac44 Alexandre Quessy
        """
94 3a62ac44 Alexandre Quessy
        Frees an allocated port number.
95 3a62ac44 Alexandre Quessy
        Raises a PortsAllocatorError if not allocated.
96 3a62ac44 Alexandre Quessy
        """
97 3a62ac44 Alexandre Quessy
        if value not in self.allocated:
98 3a62ac44 Alexandre Quessy
            raise PortsAllocatorError("Value %d not in allocated ports set." % (value))
99 3a62ac44 Alexandre Quessy
        self.allocated.remove(value)
100 3a62ac44 Alexandre Quessy
101 3a62ac44 Alexandre Quessy
        
102 3a62ac44 Alexandre Quessy
    def allocate_many(self, num=1):
103 3a62ac44 Alexandre Quessy
        """
104 3a62ac44 Alexandre Quessy
        Allocates many ports at once.
105 3a62ac44 Alexandre Quessy
        Returns a list of allocated ports.
106 3a62ac44 Alexandre Quessy
        """
107 3a62ac44 Alexandre Quessy
        ret = []
108 3a62ac44 Alexandre Quessy
        for i in range(num):
109 3a62ac44 Alexandre Quessy
            ret.append(self.allocate())
110 3a62ac44 Alexandre Quessy
        return ret
111 3a62ac44 Alexandre Quessy
    
112 3a62ac44 Alexandre Quessy
    def free_many(self, values):
113 3a62ac44 Alexandre Quessy
        """
114 3a62ac44 Alexandre Quessy
        Frees many allocated ports at a time.
115 3a62ac44 Alexandre Quessy
        :param values: list of integers.
116 3a62ac44 Alexandre Quessy
        """
117 3a62ac44 Alexandre Quessy
        for value in values:
118 3a62ac44 Alexandre Quessy
            self.free(value)