
Now all OpenStack resource classes inherit CloudResource that provides getRegion method and regionName property. This allows to assign resources to different regions. getRegion() returns CloudRegion instance that resource or it parent belong to. CloudRegion has the similar interface to Environment class and is the correct way to get HeatStack instance associated with the regoin, default network configuration, security group manager and agent listener instances. Environment acts as the default region so backward compatibility is not broken. However new applications should not use environment to set security group rules but rather a region(s) of their instance(s) in order to work correctly when their instances were configured to use region other then the default. Change-Id: I4dbf40c65042e9a354f3bfadfcd63a63e6e3e418
198 lines
7.6 KiB
Python
198 lines
7.6 KiB
Python
# Copyright (c) 2014 Mirantis Inc.
|
|
#
|
|
# 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 math
|
|
|
|
import netaddr
|
|
from netaddr.strategy import ipv4
|
|
import neutronclient.v2_0.client as nclient
|
|
from oslo_config import cfg
|
|
from oslo_log import log as logging
|
|
from oslo_utils import uuidutils
|
|
import retrying
|
|
|
|
from murano.common import auth_utils
|
|
from murano.common import exceptions as exc
|
|
from murano.common.i18n import _LI
|
|
from murano.dsl import dsl
|
|
from murano.dsl import helpers
|
|
from murano.dsl import session_local_storage
|
|
|
|
CONF = cfg.CONF
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
@dsl.name('io.murano.system.NetworkExplorer')
|
|
class NetworkExplorer(object):
|
|
def __init__(self, this):
|
|
session = helpers.get_execution_session()
|
|
self._project_id = session.project_id
|
|
self._settings = CONF.networking
|
|
self._available_cidrs = self._generate_possible_cidrs()
|
|
self._region = this.find_owner('io.murano.CloudRegion')
|
|
|
|
@staticmethod
|
|
@session_local_storage.execution_session_memoize
|
|
def _get_client(region_name):
|
|
neutron_settings = CONF.neutron
|
|
return nclient.Client(**auth_utils.get_session_client_parameters(
|
|
service_type='network', region=region_name, conf=neutron_settings
|
|
))
|
|
|
|
@property
|
|
def _client(self):
|
|
region = None if self._region is None else self._region['name']
|
|
return self._get_client(region)
|
|
|
|
# NOTE(starodubcevna): to avoid simultaneous router requests we use retry
|
|
# decorator with random delay 1-10 seconds between attempts and maximum
|
|
# delay time 30 seconds.
|
|
@retrying.retry(retry_on_exception=lambda e: isinstance(e,
|
|
exc.RouterInfoException),
|
|
wait_random_min=1000, wait_random_max=10000,
|
|
stop_max_delay=30000)
|
|
def get_default_router(self):
|
|
router_name = self._settings.router_name
|
|
|
|
routers = self._client.list_routers(
|
|
tenant_id=self._project_id, name=router_name).get('routers')
|
|
if len(routers) == 0:
|
|
LOG.debug('Router {name} not found'.format(name=router_name))
|
|
if self._settings.create_router:
|
|
LOG.debug('Attempting to create Router {router}'.
|
|
format(router=router_name))
|
|
external_network = self._settings.external_network
|
|
kwargs = {'id': external_network} \
|
|
if uuidutils.is_uuid_like(external_network) \
|
|
else {'name': external_network}
|
|
networks = self._client.list_networks(**kwargs).get('networks')
|
|
ext_nets = list(filter(lambda n: n['router:external'],
|
|
networks))
|
|
if len(ext_nets) == 0:
|
|
raise KeyError('Router %s could not be created, '
|
|
'no external network found' % router_name)
|
|
nid = ext_nets[0]['id']
|
|
|
|
body_data = {
|
|
'router': {
|
|
'name': router_name,
|
|
'external_gateway_info': {
|
|
'network_id': nid
|
|
},
|
|
'admin_state_up': True,
|
|
}
|
|
}
|
|
router = self._client.create_router(
|
|
body=body_data).get('router')
|
|
LOG.info(_LI('Created router: {id}').format(id=router['id']))
|
|
return router['id']
|
|
else:
|
|
raise KeyError('Router %s was not found' % router_name)
|
|
else:
|
|
if routers[0]['external_gateway_info'] is None:
|
|
raise exc.RouterInfoException('Please set external gateway for'
|
|
' the router %s ' % router_name)
|
|
router_id = routers[0]['id']
|
|
return router_id
|
|
|
|
def get_available_cidr(self, router_id, net_id):
|
|
"""Uses hash of network IDs to minimize the collisions
|
|
|
|
Different nets will attempt to pick different cidrs out of available
|
|
range.
|
|
If the cidr is taken will pick another one.
|
|
"""
|
|
taken_cidrs = self._get_cidrs_taken_by_router(router_id)
|
|
id_hash = hash(net_id)
|
|
num_fails = 0
|
|
while num_fails < len(self._available_cidrs):
|
|
cidr = self._available_cidrs[
|
|
(id_hash + num_fails) % len(self._available_cidrs)]
|
|
if any(self._cidrs_overlap(cidr, taken_cidr) for taken_cidr in
|
|
taken_cidrs):
|
|
num_fails += 1
|
|
else:
|
|
return str(cidr)
|
|
return None
|
|
|
|
def get_default_dns(self):
|
|
return self._settings.default_dns
|
|
|
|
def get_external_network_id_for_router(self, router_id):
|
|
router = self._client.show_router(router_id).get('router')
|
|
if not router or 'external_gateway_info' not in router:
|
|
return None
|
|
return router['external_gateway_info'].get('network_id')
|
|
|
|
def get_external_network_id_for_network(self, network_id):
|
|
network = self._client.show_network(network_id).get('network')
|
|
if network.get('router:external', False):
|
|
return network_id
|
|
|
|
# Get router interfaces of the network
|
|
router_ports = self._client.list_ports(
|
|
**{'device_owner': 'network:router_interface',
|
|
'network_id': network_id}).get('ports')
|
|
|
|
# For each router this network is connected to
|
|
# check if the router has external_gateway set
|
|
for router_port in router_ports:
|
|
ext_net_id = self.getExternalNetworkIdForRouter(
|
|
router_port.get('device_id'))
|
|
if ext_net_id:
|
|
return ext_net_id
|
|
return None
|
|
|
|
def _get_cidrs_taken_by_router(self, router_id):
|
|
if not router_id:
|
|
return []
|
|
ports = self._client.list_ports(device_id=router_id)['ports']
|
|
subnet_ids = []
|
|
for port in ports:
|
|
for fixed_ip in port['fixed_ips']:
|
|
subnet_ids.append(fixed_ip['subnet_id'])
|
|
|
|
all_subnets = self._client.list_subnets()['subnets']
|
|
filtered_cidrs = [netaddr.IPNetwork(subnet['cidr']) for subnet in
|
|
all_subnets if subnet['id'] in subnet_ids]
|
|
|
|
return filtered_cidrs
|
|
|
|
@staticmethod
|
|
def _cidrs_overlap(cidr1, cidr2):
|
|
return (cidr1 in cidr2) or (cidr2 in cidr1)
|
|
|
|
def _generate_possible_cidrs(self):
|
|
bits_for_envs = int(
|
|
math.ceil(math.log(self._settings.max_environments, 2)))
|
|
bits_for_hosts = int(math.ceil(math.log(self._settings.max_hosts, 2)))
|
|
width = ipv4.width
|
|
mask_width = width - bits_for_hosts - bits_for_envs
|
|
net = netaddr.IPNetwork(
|
|
'{0}/{1}'.format(self._settings.env_ip_template, mask_width))
|
|
return list(net.subnet(width - bits_for_hosts))
|
|
|
|
def list_networks(self):
|
|
return self._client.list_networks()['networks']
|
|
|
|
def list_subnetworks(self):
|
|
return self._client.list_subnets()['subnets']
|
|
|
|
def list_ports(self):
|
|
return self._client.list_ports()['ports']
|
|
|
|
@session_local_storage.execution_session_memoize
|
|
def list_neutron_extensions(self):
|
|
return self._client.list_extensions()['extensions']
|