blob: 621f58b1b490c2cf95ab25eeb3f2f0fea972ce3d [file] [log] [blame]
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
from twitter.common.lang import Compatibility
class PortResolver(object):
class CycleException(Exception): pass
@classmethod
def resolve(cls, portmap):
"""
Given an announce-style portmap, return a fully dereferenced portmap.
For example, given the portmap:
{
'http': 80,
'aurora: 'http',
'https': 'aurora',
'thrift': 'service'
}
Returns {'http': 80, 'aurora': 80, 'https': 80, 'thrift': 'service'}
"""
for (name, port) in portmap.items():
if not isinstance(name, Compatibility.string):
raise ValueError('All portmap keys must be strings!')
if not isinstance(port, (int, Compatibility.string)):
raise ValueError('All portmap values must be strings or integers!')
portmap = portmap.copy()
for port in list(portmap):
try:
portmap[port] = int(portmap[port])
except ValueError:
continue
def resolve_one(static_port):
visited = set()
root = portmap[static_port]
while root in portmap:
visited.add(root)
if portmap[root] in visited:
raise cls.CycleException('Found cycle in portmap!')
root = portmap[root]
return root
return dict((name, resolve_one(name)) for name in portmap)