Merge "Added NetworkExplorer engine object class"

This commit is contained in:
Jenkins 2014-04-29 09:49:17 +00:00 committed by Gerrit Code Review
commit 650a4c78d4
5 changed files with 148 additions and 0 deletions

View File

@ -159,3 +159,13 @@ url = http://localhost:8082
#key_file =
# If set then the server's certificate will not be verified
insecure = False
[networking]
# Maximum number of environments that use a single router per tenant
max_environments = 20
# Maximum number of VMs per environment
max_hosts = 250
# Template IP address for generating environment subnet cidrs
env_ip_template = 10.0.0.0

View File

@ -87,6 +87,12 @@ murano_opts = [
cfg.StrOpt('endpoint_type', default='publicURL')
]
networking_opts = [
cfg.IntOpt('max_environments', default=20),
cfg.IntOpt('max_hosts', default=250),
cfg.StrOpt('env_ip_template', default='10.0.0.0'),
]
stats_opt = [
cfg.IntOpt('period', default=5,
help=_('Statistics collection interval in minutes.'
@ -115,6 +121,7 @@ CONF.register_cli_opt(metadata_dir)
CONF.register_cli_opt(packages_cache)
CONF.register_cli_opt(package_size_limit)
CONF.register_opts(stats_opt, group='stats')
CONF.register_opts(networking_opts, group='networking')
CONF.import_opt('connection',
'muranoapi.openstack.common.db.options',

View File

@ -0,0 +1,128 @@
# 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 keystoneclient.apiclient.exceptions as ks_exc
import keystoneclient.v2_0.client as ksclient
import netaddr
from netaddr.strategy import ipv4
import neutronclient.v2_0.client as nclient
import muranoapi.common.config as config
import muranoapi.dsl.helpers as helpers
import muranoapi.dsl.murano_class as murano_class
import muranoapi.dsl.murano_object as murano_object
@murano_class.classname('io.murano.system.NetworkExplorer')
class NetworkExplorer(murano_object.MuranoObject):
# noinspection PyAttributeOutsideInit
def initialize(self, _context):
environment = helpers.get_environment(_context)
self._tenant_id = environment.tenant_id
keystone_settings = config.CONF.keystone
neutron_settings = config.CONF.neutron
self._settings = config.CONF.networking
keystone_client = ksclient.Client(
endpoint=keystone_settings.auth_url,
cacert=keystone_settings.ca_file or None,
cert=keystone_settings.cert_file or None,
key=keystone_settings.key_file or None,
insecure=keystone_settings.insecure)
if not keystone_client.authenticate(
auth_url=keystone_settings.auth_url,
tenant_id=environment.tenant_id,
token=environment.token):
raise ks_exc.AuthorizationFailure()
neutron_url = keystone_client.service_catalog.url_for(
service_type='network',
endpoint_type=neutron_settings.endpoint_type)
self._neutron = \
nclient.Client(endpoint_url=neutron_url,
token=environment.token,
ca_cert=neutron_settings.ca_cert or None,
insecure=neutron_settings.insecure)
self._available_cidrs = self._generate_possible_cidrs()
# noinspection PyPep8Naming
def getDefaultRouter(self):
routers = self._neutron.list_routers(tenant_id=self._tenant_id).\
get("routers")
if len(routers) == 0:
return "NOT_FOUND"
else:
router_id = routers[0]["id"]
if len(routers) > 1:
for router in routers:
if "murano" in router["name"].lower():
return router["id"]
return router_id
# noinspection PyPep8Naming
def getAvailableCidr(self, routerId, netId):
"""
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(routerId)
id_hash = hash(netId)
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_cidrs_taken_by_router(self, router_id):
if not router_id:
return []
ports = self._neutron.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._neutron.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))

View File

@ -20,6 +20,7 @@ from muranoapi.engine.system import agent
from muranoapi.engine.system import agent_listener
from muranoapi.engine.system import heat_stack
from muranoapi.engine.system import instance_reporter
from muranoapi.engine.system import net_explorer
from muranoapi.engine.system import resource_manager
from muranoapi.engine.system import status_reporter
@ -50,3 +51,4 @@ def register(class_loader, package_loader):
class_loader.import_class(ResourceManagerWrapper)
class_loader.import_class(instance_reporter.InstanceReportNotifier)
class_loader.import_class(status_reporter.StatusReporter)
class_loader.import_class(net_explorer.NetworkExplorer)

View File

@ -34,6 +34,7 @@ passlib
jsonschema>=2.0.0,<3.0.0
python-keystoneclient>=0.7.0
python-heatclient>=0.2.3
python-neutronclient>=2.3.4,<3
oslo.config>=1.2.0
oslo.messaging>=1.3.0a9