# All Rights Reserved. # # 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. """Serial consoles module.""" import socket from oslo_log import log as logging import nova.conf from nova import exception from nova import utils LOG = logging.getLogger(__name__) ALLOCATED_PORTS = set() # in-memory set of already allocated ports SERIAL_LOCK = 'serial-lock' CONF = nova.conf.CONF # TODO(sahid): Add a method to initialize ALLOCATED_PORTS with the # already binded TPC port(s). (cf from danpb: list all running guests and # query the XML in libvirt driver to find out the TCP port(s) it uses). @utils.synchronized(SERIAL_LOCK) def acquire_port(host): """Returns a free TCP port on host. Find and returns a free TCP port on 'host' in the range of 'CONF.serial_console.port_range'. """ start, stop = _get_port_range() for port in range(start, stop): if (host, port) in ALLOCATED_PORTS: continue try: _verify_port(host, port) ALLOCATED_PORTS.add((host, port)) return port except exception.SocketPortInUseException as e: LOG.warning(e.format_message()) raise exception.SocketPortRangeExhaustedException(host=host) @utils.synchronized(SERIAL_LOCK) def release_port(host, port): """Release TCP port to be used next time.""" ALLOCATED_PORTS.discard((host, port)) def _get_port_range(): config_range = CONF.serial_console.port_range start, stop = map(int, config_range.split(':')) if start >= stop: default_port_range = nova.conf.serial_console.DEFAULT_PORT_RANGE LOG.warning("serial_console.port_range should be in the " "format : and start < stop, " "Given value %(port_range)s is invalid. " "Taking the default port range %(default)s.", {'port_range': config_range, 'default': default_port_range}) start, stop = map(int, default_port_range.split(':')) return start, stop def _verify_port(host, port): s = socket.socket() try: s.bind((host, port)) except socket.error as e: raise exception.SocketPortInUseException( host=host, port=port, error=e) finally: s.close()