390 lines
16 KiB
Python
390 lines
16 KiB
Python
# 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.
|
|
|
|
# import types so that we can reference ListType in sphinx param declarations.
|
|
# We can't just use list, because sphinx gets confused by
|
|
# openstack.resource.Resource.list and openstack.resource2.Resource.list
|
|
import threading
|
|
import types # noqa
|
|
|
|
from openstack.cloud import exc
|
|
|
|
|
|
class NetworkCommonCloudMixin:
|
|
"""Shared networking functions used by FloatingIP, Network, Compute
|
|
classes."""
|
|
|
|
def __init__(self):
|
|
self._external_ipv4_names = self.config.get_external_ipv4_networks()
|
|
self._internal_ipv4_names = self.config.get_internal_ipv4_networks()
|
|
self._external_ipv6_names = self.config.get_external_ipv6_networks()
|
|
self._internal_ipv6_names = self.config.get_internal_ipv6_networks()
|
|
self._nat_destination = self.config.get_nat_destination()
|
|
self._nat_source = self.config.get_nat_source()
|
|
self._default_network = self.config.get_default_network()
|
|
|
|
self._use_external_network = self.config.config.get(
|
|
'use_external_network', True
|
|
)
|
|
self._use_internal_network = self.config.config.get(
|
|
'use_internal_network', True
|
|
)
|
|
|
|
self._networks_lock = threading.Lock()
|
|
self._reset_network_caches()
|
|
|
|
def use_external_network(self):
|
|
return self._use_external_network
|
|
|
|
def use_internal_network(self):
|
|
return self._use_internal_network
|
|
|
|
def _reset_network_caches(self):
|
|
# Variables to prevent us from going through the network finding
|
|
# logic again if we've done it once. This is different from just
|
|
# the cached value, since "None" is a valid value to find.
|
|
with self._networks_lock:
|
|
self._external_ipv4_networks = []
|
|
self._external_ipv4_floating_networks = []
|
|
self._internal_ipv4_networks = []
|
|
self._external_ipv6_networks = []
|
|
self._internal_ipv6_networks = []
|
|
self._nat_destination_network = None
|
|
self._nat_source_network = None
|
|
self._default_network_network = None
|
|
self._network_list_stamp = False
|
|
|
|
def _set_interesting_networks(self):
|
|
external_ipv4_networks = []
|
|
external_ipv4_floating_networks = []
|
|
internal_ipv4_networks = []
|
|
external_ipv6_networks = []
|
|
internal_ipv6_networks = []
|
|
nat_destination = None
|
|
nat_source = None
|
|
default_network = None
|
|
|
|
all_subnets = None
|
|
|
|
# Filter locally because we have an or condition
|
|
try:
|
|
# TODO(mordred): Rackspace exposes neutron but it does not
|
|
# work. I think that overriding what the service catalog
|
|
# reports should be a thing os-client-config should handle
|
|
# in a vendor profile - but for now it does not. That means
|
|
# this search_networks can just totally fail. If it does
|
|
# though, that's fine, clearly the neutron introspection is
|
|
# not going to work.
|
|
all_networks = self.list_networks()
|
|
except exc.OpenStackCloudException:
|
|
self._network_list_stamp = True
|
|
return
|
|
|
|
for network in all_networks:
|
|
# External IPv4 networks
|
|
if (
|
|
network['name'] in self._external_ipv4_names
|
|
or network['id'] in self._external_ipv4_names
|
|
):
|
|
external_ipv4_networks.append(network)
|
|
elif (
|
|
(
|
|
network.is_router_external
|
|
or network.provider_physical_network
|
|
)
|
|
and network['name'] not in self._internal_ipv4_names
|
|
and network['id'] not in self._internal_ipv4_names
|
|
):
|
|
external_ipv4_networks.append(network)
|
|
|
|
# Internal networks
|
|
if (
|
|
network['name'] in self._internal_ipv4_names
|
|
or network['id'] in self._internal_ipv4_names
|
|
):
|
|
internal_ipv4_networks.append(network)
|
|
elif (
|
|
not network.is_router_external
|
|
and not network.provider_physical_network
|
|
and network['name'] not in self._external_ipv4_names
|
|
and network['id'] not in self._external_ipv4_names
|
|
):
|
|
internal_ipv4_networks.append(network)
|
|
|
|
# External networks
|
|
if (
|
|
network['name'] in self._external_ipv6_names
|
|
or network['id'] in self._external_ipv6_names
|
|
):
|
|
external_ipv6_networks.append(network)
|
|
elif (
|
|
network.is_router_external
|
|
and network['name'] not in self._internal_ipv6_names
|
|
and network['id'] not in self._internal_ipv6_names
|
|
):
|
|
external_ipv6_networks.append(network)
|
|
|
|
# Internal networks
|
|
if (
|
|
network['name'] in self._internal_ipv6_names
|
|
or network['id'] in self._internal_ipv6_names
|
|
):
|
|
internal_ipv6_networks.append(network)
|
|
elif (
|
|
not network.is_router_external
|
|
and network['name'] not in self._external_ipv6_names
|
|
and network['id'] not in self._external_ipv6_names
|
|
):
|
|
internal_ipv6_networks.append(network)
|
|
|
|
# External Floating IPv4 networks
|
|
if self._nat_source in (network['name'], network['id']):
|
|
if nat_source:
|
|
raise exc.OpenStackCloudException(
|
|
'Multiple networks were found matching'
|
|
' {nat_net} which is the network configured'
|
|
' to be the NAT source. Please check your'
|
|
' cloud resources. It is probably a good idea'
|
|
' to configure this network by ID rather than'
|
|
' by name.'.format(nat_net=self._nat_source)
|
|
)
|
|
external_ipv4_floating_networks.append(network)
|
|
nat_source = network
|
|
elif self._nat_source is None:
|
|
if network.is_router_external:
|
|
external_ipv4_floating_networks.append(network)
|
|
nat_source = nat_source or network
|
|
|
|
# NAT Destination
|
|
if self._nat_destination in (network['name'], network['id']):
|
|
if nat_destination:
|
|
raise exc.OpenStackCloudException(
|
|
'Multiple networks were found matching'
|
|
' {nat_net} which is the network configured'
|
|
' to be the NAT destination. Please check your'
|
|
' cloud resources. It is probably a good idea'
|
|
' to configure this network by ID rather than'
|
|
' by name.'.format(nat_net=self._nat_destination)
|
|
)
|
|
nat_destination = network
|
|
elif self._nat_destination is None:
|
|
# TODO(mordred) need a config value for floating
|
|
# ips for this cloud so that we can skip this
|
|
# No configured nat destination, we have to figured
|
|
# it out.
|
|
if all_subnets is None:
|
|
try:
|
|
all_subnets = self.list_subnets()
|
|
except exc.OpenStackCloudException:
|
|
# Thanks Rackspace broken neutron
|
|
all_subnets = []
|
|
|
|
for subnet in all_subnets:
|
|
# TODO(mordred) trap for detecting more than
|
|
# one network with a gateway_ip without a config
|
|
if (
|
|
'gateway_ip' in subnet
|
|
and subnet['gateway_ip']
|
|
and network['id'] == subnet['network_id']
|
|
):
|
|
nat_destination = network
|
|
break
|
|
|
|
# Default network
|
|
if self._default_network in (network['name'], network['id']):
|
|
if default_network:
|
|
raise exc.OpenStackCloudException(
|
|
'Multiple networks were found matching'
|
|
' {default_net} which is the network'
|
|
' configured to be the default interface'
|
|
' network. Please check your cloud resources.'
|
|
' It is probably a good idea'
|
|
' to configure this network by ID rather than'
|
|
' by name.'.format(default_net=self._default_network)
|
|
)
|
|
default_network = network
|
|
|
|
# Validate config vs. reality
|
|
for net_name in self._external_ipv4_names:
|
|
if net_name not in [net['name'] for net in external_ipv4_networks]:
|
|
raise exc.OpenStackCloudException(
|
|
"Networks: {network} was provided for external IPv4"
|
|
" access and those networks could not be found".format(
|
|
network=net_name
|
|
)
|
|
)
|
|
|
|
for net_name in self._internal_ipv4_names:
|
|
if net_name not in [net['name'] for net in internal_ipv4_networks]:
|
|
raise exc.OpenStackCloudException(
|
|
"Networks: {network} was provided for internal IPv4"
|
|
" access and those networks could not be found".format(
|
|
network=net_name
|
|
)
|
|
)
|
|
|
|
for net_name in self._external_ipv6_names:
|
|
if net_name not in [net['name'] for net in external_ipv6_networks]:
|
|
raise exc.OpenStackCloudException(
|
|
"Networks: {network} was provided for external IPv6"
|
|
" access and those networks could not be found".format(
|
|
network=net_name
|
|
)
|
|
)
|
|
|
|
for net_name in self._internal_ipv6_names:
|
|
if net_name not in [net['name'] for net in internal_ipv6_networks]:
|
|
raise exc.OpenStackCloudException(
|
|
"Networks: {network} was provided for internal IPv6"
|
|
" access and those networks could not be found".format(
|
|
network=net_name
|
|
)
|
|
)
|
|
|
|
if self._nat_destination and not nat_destination:
|
|
raise exc.OpenStackCloudException(
|
|
'Network {network} was configured to be the'
|
|
' destination for inbound NAT but it could not be'
|
|
' found'.format(network=self._nat_destination)
|
|
)
|
|
|
|
if self._nat_source and not nat_source:
|
|
raise exc.OpenStackCloudException(
|
|
'Network {network} was configured to be the'
|
|
' source for inbound NAT but it could not be'
|
|
' found'.format(network=self._nat_source)
|
|
)
|
|
|
|
if self._default_network and not default_network:
|
|
raise exc.OpenStackCloudException(
|
|
'Network {network} was configured to be the'
|
|
' default network interface but it could not be'
|
|
' found'.format(network=self._default_network)
|
|
)
|
|
|
|
self._external_ipv4_networks = external_ipv4_networks
|
|
self._external_ipv4_floating_networks = external_ipv4_floating_networks
|
|
self._internal_ipv4_networks = internal_ipv4_networks
|
|
self._external_ipv6_networks = external_ipv6_networks
|
|
self._internal_ipv6_networks = internal_ipv6_networks
|
|
self._nat_destination_network = nat_destination
|
|
self._nat_source_network = nat_source
|
|
self._default_network_network = default_network
|
|
|
|
def _find_interesting_networks(self):
|
|
if self._networks_lock.acquire():
|
|
try:
|
|
if self._network_list_stamp:
|
|
return
|
|
if (
|
|
not self._use_external_network
|
|
and not self._use_internal_network
|
|
):
|
|
# Both have been flagged as skip - don't do a list
|
|
return
|
|
if not self.has_service('network'):
|
|
return
|
|
self._set_interesting_networks()
|
|
self._network_list_stamp = True
|
|
finally:
|
|
self._networks_lock.release()
|
|
|
|
def get_nat_destination(self):
|
|
"""Return the network that is configured to be the NAT destination.
|
|
|
|
:returns: A network ``Network`` object if one is found
|
|
"""
|
|
self._find_interesting_networks()
|
|
return self._nat_destination_network
|
|
|
|
def get_nat_source(self):
|
|
"""Return the network that is configured to be the NAT destination.
|
|
|
|
:returns: A network ``Network`` object if one is found
|
|
"""
|
|
self._find_interesting_networks()
|
|
return self._nat_source_network
|
|
|
|
def get_default_network(self):
|
|
"""Return the network that is configured to be the default interface.
|
|
|
|
:returns: A network ``Network`` object if one is found
|
|
"""
|
|
self._find_interesting_networks()
|
|
return self._default_network_network
|
|
|
|
def get_external_networks(self):
|
|
"""Return the networks that are configured to route northbound.
|
|
|
|
This should be avoided in favor of the specific ipv4/ipv6 method,
|
|
but is here for backwards compatibility.
|
|
|
|
:returns: A list of network ``Network`` objects if any are found
|
|
"""
|
|
self._find_interesting_networks()
|
|
return list(self._external_ipv4_networks) + list(
|
|
self._external_ipv6_networks
|
|
)
|
|
|
|
def get_internal_networks(self):
|
|
"""Return the networks that are configured to not route northbound.
|
|
|
|
This should be avoided in favor of the specific ipv4/ipv6 method,
|
|
but is here for backwards compatibility.
|
|
|
|
:returns: A list of network ``Network`` objects if any are found
|
|
"""
|
|
self._find_interesting_networks()
|
|
return list(self._internal_ipv4_networks) + list(
|
|
self._internal_ipv6_networks
|
|
)
|
|
|
|
def get_external_ipv4_networks(self):
|
|
"""Return the networks that are configured to route northbound.
|
|
|
|
:returns: A list of network ``Network`` objects if any are found
|
|
"""
|
|
self._find_interesting_networks()
|
|
return self._external_ipv4_networks
|
|
|
|
def get_external_ipv4_floating_networks(self):
|
|
"""Return the networks that are configured to route northbound.
|
|
|
|
:returns: A list of network ``Network`` objects if any are found
|
|
"""
|
|
self._find_interesting_networks()
|
|
return self._external_ipv4_floating_networks
|
|
|
|
def get_internal_ipv4_networks(self):
|
|
"""Return the networks that are configured to not route northbound.
|
|
|
|
:returns: A list of network ``Network`` objects if any are found
|
|
"""
|
|
self._find_interesting_networks()
|
|
return self._internal_ipv4_networks
|
|
|
|
def get_external_ipv6_networks(self):
|
|
"""Return the networks that are configured to route northbound.
|
|
|
|
:returns: A list of network ``Network`` objects if any are found
|
|
"""
|
|
self._find_interesting_networks()
|
|
return self._external_ipv6_networks
|
|
|
|
def get_internal_ipv6_networks(self):
|
|
"""Return the networks that are configured to not route northbound.
|
|
|
|
:returns: A list of network ``Network`` objects if any are found
|
|
"""
|
|
self._find_interesting_networks()
|
|
return self._internal_ipv6_networks
|