3 # Copyright 2007 Google Inc.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 """A helper file with a helper class for opening ports."""
21 from google
.appengine
.client
.services
import vme_errors
23 # These ports are used by our code or critical system daemons.
24 RESERVED_HOST_PORTS
= [22, # SSH
25 5000, # Docker registry
27 10000, # For unlocking?
28 10001, # Nanny stubby proxy endpoint
30 # We allow users to forward traffic to our HTTP server internally.
31 RESERVED_DOCKER_PORTS
= [22, # SSH
32 5000, # Docker registry
33 10001, # Nanny stubby proxy endpoint
37 class InconsistentPortConfigurationError(vme_errors
.PermanentAppError
):
38 """The port is already in use."""
42 class IllegalPortConfigurationError(vme_errors
.PermanentAppError
):
43 """Raised if the port configuration is illegal."""
47 def CreatePortManager(forwarded_ports
):
48 """Construct a PortManager object with port forwarding configured.
51 forwarded_ports: A dictionary containing desired mappings from VM host port
52 to docker container port.
55 The PortManager instance.
57 port_manager_obj
= PortManager()
58 ports_list
= forwarded_ports
if forwarded_ports
else []
59 logging
.debug('setting forwarded ports %s', ports_list
)
60 port_manager_obj
.Add(ports_list
, 'forwarded')
61 return port_manager_obj
64 class PortManager(object):
65 """A helper class for VmManager to deal with port mappings."""
68 self
.used_host_ports
= {}
69 self
.port_mappings
= {}
71 def Add(self
, ports
, kind
):
72 """Load port configurations and adds them to an internal dict.
75 ports: A list of strings or a CSV representing port forwarding.
76 kind: what kind of port configuration this is, only used for error
80 InconsistentPortConfigurationError: If a port is configured to do
81 two different conflicting things.
82 IllegalPortConfigurationError: If the port is out of range or
86 A dictionary with forwarding rules as external_port => local_port.
88 if isinstance(ports
, basestring
):
90 ports
= [port
.strip() for port
in ports
.split(',')]
91 port_translations
= {}
95 host_port
, docker_port
= (int(p
.strip()) for p
in port
.split(':'))
96 port_translations
[host_port
] = docker_port
99 docker_port
= host_port
100 port_translations
[host_port
] = host_port
101 if (host_port
in self
.used_host_ports
and
102 self
.used_host_ports
[host_port
] != docker_port
):
103 raise InconsistentPortConfigurationError(
104 'Configuration conflict, port %d configured to forward '
105 'differently.' % host_port
)
106 self
.used_host_ports
[host_port
] = docker_port
107 if (host_port
< 1 or host_port
> 65535 or
108 docker_port
< 1 or docker_port
> 65535):
109 raise IllegalPortConfigurationError(
110 'Failed to load %s port configuration: invalid port %s'
112 if docker_port
< 1024:
113 raise IllegalPortConfigurationError(
114 'Cannot listen on port %d as it is priviliged, use a forwarding '
115 'port.' % docker_port
)
116 if docker_port
in RESERVED_DOCKER_PORTS
:
117 raise IllegalPortConfigurationError(
118 'Cannot use port %d as it is reserved on the VM.'
120 if host_port
in RESERVED_HOST_PORTS
:
121 raise IllegalPortConfigurationError(
122 'Cannot use port %d as it is reserved on the VM.'
124 except ValueError as e
:
125 logging
.exception('Bad port description')
126 raise IllegalPortConfigurationError(
127 'Failed to load %s port configuration: "%s" error: "%s"'
129 # At this point we know they are not destructive.
130 self
.port_mappings
.update(port_translations
)
131 return port_translations
133 def GetAllMappedPorts(self
):
134 """Returns all mapped ports.
137 A dict of port mappings {host: docker}
139 return self
.port_mappings