1+ import re
2+
3+ PORT_SPEC = re .compile (
4+ "^" # Match full string
5+ "(" # External part
6+ "((?P<host>[a-fA-F\d.:]+):)?" # Address
7+ "(?P<ext>[\d]*)(-(?P<ext_end>[\d]+))?:" # External range
8+ ")?"
9+ "(?P<int>[\d]+)(-(?P<int_end>[\d]+))?" # Internal range
10+ "(?P<proto>/(udp|tcp))?" # Protocol
11+ "$" # Match full string
12+ )
13+
114
215def add_port_mapping (port_bindings , internal_port , external ):
316 if internal_port in port_bindings :
@@ -24,81 +37,41 @@ def build_port_bindings(ports):
2437 return port_bindings
2538
2639
27- def to_port_range (port , randomly_available_port = False ):
28- if not port :
29- return None
30-
31- protocol = ""
32- if "/" in port :
33- parts = port .split ("/" )
34- if len (parts ) != 2 :
35- _raise_invalid_port (port )
36-
37- port , protocol = parts
38- protocol = "/" + protocol
39-
40- if randomly_available_port :
41- return ["%s%s" % (port , protocol )]
42-
43- parts = str (port ).split ('-' )
44-
45- if len (parts ) == 1 :
46- return ["%s%s" % (port , protocol )]
47-
48- if len (parts ) == 2 :
49- full_port_range = range (int (parts [0 ]), int (parts [1 ]) + 1 )
50- return ["%s%s" % (p , protocol ) for p in full_port_range ]
51-
52- raise ValueError ('Invalid port range "%s", should be '
53- 'port or startport-endport' % port )
54-
55-
5640def _raise_invalid_port (port ):
5741 raise ValueError ('Invalid port "%s", should be '
5842 '[[remote_ip:]remote_port[-remote_port]:]'
5943 'port[/protocol]' % port )
6044
6145
62- def split_port (port ):
63- parts = str (port ).split (':' )
64-
65- if not 1 <= len (parts ) <= 3 :
66- _raise_invalid_port (port )
67-
68- if len (parts ) == 1 :
69- internal_port , = parts
70- if not internal_port :
71- _raise_invalid_port (port )
72- return to_port_range (internal_port ), None
73- if len (parts ) == 2 :
74- external_port , internal_port = parts
75-
76- internal_range = to_port_range (internal_port )
77- if internal_range is None :
78- _raise_invalid_port (port )
79-
80- external_range = to_port_range (external_port , len (internal_range ) == 1 )
81- if external_range is None :
82- _raise_invalid_port (port )
83-
84- if len (internal_range ) != len (external_range ):
85- raise ValueError ('Port ranges don\' t match in length' )
86-
87- return internal_range , external_range
46+ def port_range (start , end , proto , randomly_available_port = False ):
47+ if not start :
48+ return start
49+ if not end :
50+ return [start + proto ]
51+ if randomly_available_port :
52+ return ['{}-{}' .format (start , end ) + proto ]
53+ return [str (port ) + proto for port in range (int (start ), int (end ) + 1 )]
8854
89- external_ip , external_port , internal_port = parts
9055
91- if not internal_port :
56+ def split_port (port ):
57+ match = PORT_SPEC .match (port )
58+ if match is None :
9259 _raise_invalid_port (port )
60+ parts = match .groupdict ()
9361
94- internal_range = to_port_range (internal_port )
95- external_range = to_port_range (external_port , len (internal_range ) == 1 )
96-
97- if not external_range :
98- external_range = [None ] * len (internal_range )
99-
100- if len (internal_range ) != len (external_range ):
101- raise ValueError ('Port ranges don\' t match in length' )
62+ host = parts ['host' ]
63+ proto = parts ['proto' ] or ''
64+ internal = port_range (parts ['int' ], parts ['int_end' ], proto )
65+ external = port_range (
66+ parts ['ext' ], parts ['ext_end' ], '' , len (internal ) == 1 )
10267
103- return internal_range , [(external_ip , ex_port or None )
104- for ex_port in external_range ]
68+ if host is None :
69+ if external is not None and len (internal ) != len (external ):
70+ raise ValueError ('Port ranges don\' t match in length' )
71+ return internal , external
72+ else :
73+ if not external :
74+ external = [None ] * len (internal )
75+ elif len (internal ) != len (external ):
76+ raise ValueError ('Port ranges don\' t match in length' )
77+ return internal , [(host , ext_port ) for ext_port in external ]
0 commit comments