App Engine Python SDK version 1.9.12
[gae.git] / python / google / appengine / client / services / port_manager.py
blobe920825e331607338a93f3668a1b0a4357d26667
1 #!/usr/bin/env python
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."""
19 import logging
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
26 8080, # HTTP server
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."""
39 pass
42 class IllegalPortConfigurationError(vme_errors.PermanentAppError):
43 """Raised if the port configuration is illegal."""
44 pass
47 def CreatePortManager(forwarded_ports):
48 """Construct a PortManager object with port forwarding configured.
50 Args:
51 forwarded_ports: A dictionary containing desired mappings from VM host port
52 to docker container port.
54 Returns:
55 The PortManager instance.
56 """
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."""
67 def __init__(self):
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.
74 Args:
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
77 reporting.
79 Raises:
80 InconsistentPortConfigurationError: If a port is configured to do
81 two different conflicting things.
82 IllegalPortConfigurationError: If the port is out of range or
83 is not a number.
85 Returns:
86 A dictionary with forwarding rules as external_port => local_port.
87 """
88 if isinstance(ports, basestring):
89 # split a csv
90 ports = [port.strip() for port in ports.split(',')]
91 port_translations = {}
92 for port in ports:
93 try:
94 if ':' in port:
95 host_port, docker_port = (int(p.strip()) for p in port.split(':'))
96 port_translations[host_port] = docker_port
97 else:
98 host_port = int(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'
111 % (kind, port))
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.'
119 % docker_port)
120 if host_port in RESERVED_HOST_PORTS:
121 raise IllegalPortConfigurationError(
122 'Cannot use port %d as it is reserved on the VM.'
123 % host_port)
124 except ValueError as e:
125 logging.exception('Bad port description')
126 raise IllegalPortConfigurationError(
127 'Failed to load %s port configuration: "%s" error: "%s"'
128 % (kind, port, e))
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.
136 Returns:
137 A dict of port mappings {host: docker}
139 return self.port_mappings