Advanced Networking Implemented
Change-Id: Ibebc5de57cdbba10ebeb0cbc600e2fade96d96b4
This commit is contained in:
parent
246b075cd7
commit
7d0cae73d3
@ -59,7 +59,23 @@ Configure
|
||||
|
||||
# Directory where conductor's data directory located.
|
||||
# "data" must be subdirectory to this.
|
||||
data_dir = /etc/murano-conductor
|
||||
data_dir = /etc/murano-conductor/metadata-cache
|
||||
|
||||
# Provide url to Murano Metadata repository
|
||||
murano_metadata_url = http://localhost:8084
|
||||
|
||||
# Maximum number of environments that can be processed simultaneously
|
||||
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
|
||||
|
||||
# Enforces flat network topology by default.
|
||||
# If set to "False", routed topology will be used
|
||||
flat_by_default = False
|
||||
|
||||
[keystone]
|
||||
# URL of OpenStack KeyStone service REST API.
|
||||
@ -89,6 +105,14 @@ Configure
|
||||
# Valid endpoint types: publicURL (default), internalURL, adminURL
|
||||
endpoint_type = publicURL
|
||||
|
||||
[neutron]
|
||||
# Optional CA cert file to use in SSL connections
|
||||
#ca_cert =
|
||||
# Allow self signed server certificate
|
||||
insecure = False
|
||||
# Valid endpoint types: publicURL (default), internalURL, adminURL
|
||||
endpoint_type = publicURL
|
||||
|
||||
[rabbitmq]
|
||||
# Connection parameters to RabbitMQ service
|
||||
|
||||
|
@ -23,6 +23,16 @@ murano_metadata_url = http://localhost:8084
|
||||
# Maximum number of environments that can be processed simultaneously
|
||||
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
|
||||
|
||||
# Enforces flat network topology by default.
|
||||
# If set to "False", routed topology will be used
|
||||
flat_by_default = False
|
||||
|
||||
[keystone]
|
||||
# URL of OpenStack KeyStone service REST API.
|
||||
# Typically only hostname (or IP) needs to be changed
|
||||
@ -51,6 +61,14 @@ insecure = False
|
||||
# Valid endpoint types: publicURL (default), internalURL, adminURL
|
||||
endpoint_type = publicURL
|
||||
|
||||
[neutron]
|
||||
# Optional CA cert file to use in SSL connections
|
||||
#ca_cert =
|
||||
# Allow self signed server certificate
|
||||
insecure = False
|
||||
# Valid endpoint types: publicURL (default), internalURL, adminURL
|
||||
endpoint_type = publicURL
|
||||
|
||||
[rabbitmq]
|
||||
# Connection parameters to RabbitMQ service
|
||||
|
||||
|
@ -30,6 +30,7 @@ from muranocommon.helpers.token_sanitizer import TokenSanitizer
|
||||
from muranoconductor import metadata
|
||||
import vm_agent
|
||||
import cloud_formation
|
||||
import network
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -15,17 +15,19 @@
|
||||
|
||||
import command
|
||||
import cloud_formation
|
||||
import network
|
||||
import vm_agent
|
||||
|
||||
|
||||
class CommandDispatcher(command.CommandBase):
|
||||
def __init__(self, environment, rmqclient, token,
|
||||
tenant_id, reporter):
|
||||
def __init__(self, environment, rmqclient, token, tenant_id, reporter):
|
||||
self._command_map = {
|
||||
'cf': cloud_formation.HeatExecutor(environment, token, tenant_id,
|
||||
reporter),
|
||||
'agent': vm_agent.VmAgentExecutor(environment, rmqclient,
|
||||
reporter)
|
||||
'agent': vm_agent.VmAgentExecutor(
|
||||
environment, rmqclient, reporter),
|
||||
|
||||
'net': network.NeutronExecutor(tenant_id, token)
|
||||
}
|
||||
|
||||
def execute(self, name, **kwargs):
|
||||
|
196
muranoconductor/commands/network.py
Normal file
196
muranoconductor/commands/network.py
Normal file
@ -0,0 +1,196 @@
|
||||
# Copyright (c) 2013 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 muranoconductor.config
|
||||
from keystoneclient.v2_0 import client as ksclient
|
||||
import netaddr
|
||||
from netaddr.strategy import ipv4
|
||||
from neutronclient.v2_0 import client as client
|
||||
from muranoconductor.commands.command import CommandBase
|
||||
|
||||
|
||||
class NeutronExecutor(CommandBase):
|
||||
def __init__(self, tenant_id, token):
|
||||
keystone_settings = muranoconductor.config.CONF.keystone
|
||||
neutron_settings = muranoconductor.config.CONF.neutron
|
||||
|
||||
self.env_count = muranoconductor.config.CONF.max_environments
|
||||
self.host_count = muranoconductor.config.CONF.max_hosts
|
||||
self.address = muranoconductor.config.CONF.env_ip_template
|
||||
|
||||
self.cidr_waiting_per_router = {}
|
||||
self.router_requests = []
|
||||
self.network_requests = []
|
||||
|
||||
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=tenant_id,
|
||||
token=token):
|
||||
raise client.exceptions.Unauthorized()
|
||||
|
||||
neutron_url = keystone_client.service_catalog.url_for(
|
||||
service_type='network',
|
||||
endpoint_type=neutron_settings.endpoint_type)
|
||||
self.neutron = client.Client(endpoint_url=neutron_url,
|
||||
token=token,
|
||||
ca_cert=neutron_settings.ca_cert or None,
|
||||
insecure=neutron_settings.insecure)
|
||||
|
||||
self.command_map = {
|
||||
"get_subnet": self._schedule_get_subnet,
|
||||
"get_router": self._schedule_get_router,
|
||||
"get_network": self._schedule_get_network
|
||||
}
|
||||
|
||||
def execute(self, command, callback, **kwargs):
|
||||
if command in self.command_map:
|
||||
self.command_map[command](callback, **kwargs)
|
||||
|
||||
def has_pending_commands(self):
|
||||
return len(self.cidr_waiting_per_router) + len(
|
||||
self.router_requests) + len(self.network_requests) > 0
|
||||
|
||||
def execute_pending(self):
|
||||
r1 = self._execute_pending_cidr_requests()
|
||||
r2 = self._execute_pending_net_requests()
|
||||
r3 = self._execute_pending_router_requests()
|
||||
return r1 or r2 or r3
|
||||
|
||||
def _execute_pending_cidr_requests(self):
|
||||
if not len(self.cidr_waiting_per_router):
|
||||
return False
|
||||
for router, callbacks in self.cidr_waiting_per_router.items():
|
||||
results = self._get_subnet(router, len(callbacks))
|
||||
for callback, result in zip(callbacks, results):
|
||||
callback(result)
|
||||
self.cidr_waiting_per_router = {}
|
||||
return True
|
||||
|
||||
def _execute_pending_router_requests(self):
|
||||
if not len(self.router_requests):
|
||||
return False
|
||||
|
||||
routers = self.neutron.list_routers().get("routers")
|
||||
if not len(routers):
|
||||
routerId = None
|
||||
else:
|
||||
routerId = routers[0]["id"]
|
||||
|
||||
if len(routers) > 1:
|
||||
for router in routers:
|
||||
if "murano" in router["name"].lower():
|
||||
routerId = router["id"]
|
||||
break
|
||||
for callback in self.router_requests:
|
||||
callback(routerId)
|
||||
self.router_requests = []
|
||||
return True
|
||||
|
||||
def _execute_pending_net_requests(self):
|
||||
if not len(self.network_requests):
|
||||
return False
|
||||
|
||||
nets = self.neutron.list_networks()["networks"]
|
||||
if not len(nets):
|
||||
netId = None
|
||||
else:
|
||||
netId = nets[0]["id"]
|
||||
if len(nets) > 1:
|
||||
murano_id = None
|
||||
ext_id = None
|
||||
shared_id = None
|
||||
for net in nets:
|
||||
if "murano" in net.get("name").lower():
|
||||
murano_id = net["id"]
|
||||
break
|
||||
if net.get("router:external") and not ext_id:
|
||||
ext_id = net["id"]
|
||||
if net.get("shared") and not shared_id:
|
||||
shared_id = net["id"]
|
||||
if murano_id:
|
||||
netId = murano_id
|
||||
elif ext_id:
|
||||
netId = ext_id
|
||||
elif shared_id:
|
||||
netId = shared_id
|
||||
for callback in self.network_requests:
|
||||
callback(netId)
|
||||
self.network_requests = []
|
||||
return True
|
||||
|
||||
def _get_subnet(self, routerId, count):
|
||||
if routerId == "*":
|
||||
routerId = None
|
||||
if routerId:
|
||||
taken_cidrs = self._get_taken_cidrs_by_router(routerId)
|
||||
else:
|
||||
taken_cidrs = self._get_all_taken_cidrs()
|
||||
results = []
|
||||
for i in range(0, count):
|
||||
res = self._generate_cidr(taken_cidrs)
|
||||
results.append(res)
|
||||
taken_cidrs.append(res)
|
||||
return results
|
||||
|
||||
def _get_taken_cidrs_by_router(self, routerId):
|
||||
ports = self.neutron.list_ports(device_id=routerId)["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 = [subnet["cidr"] for subnet in all_subnets if
|
||||
subnet["id"] in subnet_ids]
|
||||
|
||||
return filtered_cidrs
|
||||
|
||||
def _get_all_taken_cidrs(self):
|
||||
return [subnet["cidr"] for subnet in
|
||||
self.neutron.list_subnets()["subnets"]]
|
||||
|
||||
def _generate_cidr(self, taken_cidrs):
|
||||
bits_for_envs = int(math.ceil(math.log(self.env_count, 2)))
|
||||
bits_for_hosts = int(math.ceil(math.log(self.host_count, 2)))
|
||||
width = ipv4.width
|
||||
mask_width = width - bits_for_hosts - bits_for_envs
|
||||
net = netaddr.IPNetwork(self.address + "/" + str(mask_width))
|
||||
for subnet in net.subnet(width - bits_for_hosts):
|
||||
if str(subnet) in taken_cidrs:
|
||||
continue
|
||||
return str(subnet)
|
||||
return None
|
||||
|
||||
def _schedule_get_subnet(self, callback, **kwargs):
|
||||
routerId = kwargs.get("routerId")
|
||||
if not routerId:
|
||||
routerId = "*"
|
||||
if routerId in self.cidr_waiting_per_router:
|
||||
self.cidr_waiting_per_router[routerId].append(callback)
|
||||
else:
|
||||
self.cidr_waiting_per_router[routerId] = [callback]
|
||||
|
||||
def _schedule_get_router(self, callback, **kwargs):
|
||||
self.router_requests.append(callback)
|
||||
|
||||
def _schedule_get_network(self, callback, **kwargs):
|
||||
self.network_requests.append(callback)
|
@ -58,6 +58,12 @@ heat_opts = [
|
||||
cfg.StrOpt('endpoint_type', default='publicURL')
|
||||
]
|
||||
|
||||
neutron_opts = [
|
||||
cfg.BoolOpt('insecure', default=False),
|
||||
cfg.StrOpt('ca_cert'),
|
||||
cfg.StrOpt('endpoint_type', default='publicURL')
|
||||
]
|
||||
|
||||
keystone_opts = [
|
||||
cfg.StrOpt('auth_url'),
|
||||
cfg.BoolOpt('insecure', default=False),
|
||||
@ -70,6 +76,7 @@ CONF = cfg.CONF
|
||||
CONF.register_opts(paste_deploy_opts, group='paste_deploy')
|
||||
CONF.register_opts(rabbit_opts, group='rabbitmq')
|
||||
CONF.register_opts(heat_opts, group='heat')
|
||||
CONF.register_opts(neutron_opts, group='neutron')
|
||||
CONF.register_opts(keystone_opts, group='keystone')
|
||||
CONF.register_opts(directories)
|
||||
CONF.register_opt(cfg.StrOpt('file_server'))
|
||||
@ -77,6 +84,9 @@ CONF.register_cli_opt(cfg.StrOpt('murano_metadata_url'))
|
||||
|
||||
|
||||
CONF.register_opt(cfg.IntOpt('max_environments', default=20))
|
||||
CONF.register_opt(cfg.IntOpt('max_hosts', default=250))
|
||||
CONF.register_opt(cfg.StrOpt('env_ip_template', default='10.0.0.0'))
|
||||
CONF.register_opt(cfg.BoolOpt('flat_by_default', default=False))
|
||||
|
||||
|
||||
CONF.import_opt('verbose', 'muranoconductor.openstack.common.log')
|
||||
|
95
muranoconductor/network.py
Normal file
95
muranoconductor/network.py
Normal file
@ -0,0 +1,95 @@
|
||||
# Copyright (c) 2013 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.
|
||||
from muranoconductor import xml_code_engine
|
||||
import muranoconductor.config
|
||||
|
||||
from openstack.common import log as logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_available_subnet(engine, context, body, routerId=None, result=None):
|
||||
command_dispatcher = context['/commandDispatcher']
|
||||
|
||||
def callback(result_value):
|
||||
if result is not None:
|
||||
context[result] = {"cidr": result_value}
|
||||
|
||||
success_handler = body.find('success')
|
||||
if success_handler is not None:
|
||||
engine.evaluate_content(success_handler, context)
|
||||
|
||||
command_dispatcher.execute(
|
||||
name="net",
|
||||
command="get_subnet",
|
||||
routerId=routerId,
|
||||
callback=callback)
|
||||
|
||||
|
||||
def get_default_router(engine, context, body, result=None):
|
||||
command_dispatcher = context['/commandDispatcher']
|
||||
|
||||
def callback(result_value):
|
||||
if result is not None:
|
||||
context[result] = {"routerId": result_value}
|
||||
|
||||
success_handler = body.find('success')
|
||||
if success_handler is not None:
|
||||
engine.evaluate_content(success_handler, context)
|
||||
|
||||
command_dispatcher.execute(
|
||||
name="net",
|
||||
command="get_router",
|
||||
callback=callback)
|
||||
|
||||
|
||||
def get_default_network(engine, context, body, result=None):
|
||||
command_dispatcher = context['/commandDispatcher']
|
||||
|
||||
def callback(result_value):
|
||||
if result is not None:
|
||||
context[result] = {"networkId": result_value}
|
||||
|
||||
success_handler = body.find('success')
|
||||
if success_handler is not None:
|
||||
engine.evaluate_content(success_handler, context)
|
||||
|
||||
command_dispatcher.execute(
|
||||
name="net",
|
||||
command="get_network",
|
||||
callback=callback)
|
||||
|
||||
|
||||
def get_network_topology(engine, context, body, result=None):
|
||||
if muranoconductor.config.CONF.flat_by_default:
|
||||
return "flat"
|
||||
else:
|
||||
return "routed"
|
||||
|
||||
|
||||
xml_code_engine.XmlCodeEngine.register_function(
|
||||
get_available_subnet, "get-cidr")
|
||||
|
||||
xml_code_engine.XmlCodeEngine.register_function(
|
||||
get_default_router, "get-default-router-id")
|
||||
|
||||
xml_code_engine.XmlCodeEngine.register_function(
|
||||
get_default_network, "get-default-network-id")
|
||||
|
||||
xml_code_engine.XmlCodeEngine.register_function(
|
||||
get_default_router, "get-default-router-id")
|
||||
|
||||
xml_code_engine.XmlCodeEngine.register_function(
|
||||
get_network_topology, "get-net-topology")
|
@ -12,6 +12,7 @@
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import uuid
|
||||
|
||||
import xml.etree.ElementTree as etree
|
||||
import types
|
||||
@ -127,6 +128,10 @@ def _false_func(**kwargs):
|
||||
return False
|
||||
|
||||
|
||||
def _gen_id(**kwargs):
|
||||
return uuid.uuid4().hex
|
||||
|
||||
|
||||
XmlCodeEngine.register_function(_dict_func, "map")
|
||||
XmlCodeEngine.register_function(_array_func, "list")
|
||||
XmlCodeEngine.register_function(_text_func, "text")
|
||||
@ -135,3 +140,4 @@ XmlCodeEngine.register_function(_function_func, "function")
|
||||
XmlCodeEngine.register_function(_null_func, "null")
|
||||
XmlCodeEngine.register_function(_true_func, "true")
|
||||
XmlCodeEngine.register_function(_false_func, "false")
|
||||
XmlCodeEngine.register_function(_gen_id, "uuid")
|
||||
|
@ -14,3 +14,4 @@ deep
|
||||
murano-common>=0.2.2
|
||||
PyYAML>=3.1.0
|
||||
murano-metadataclient==0.4.a13.gd65dfd2
|
||||
python-neutronclient>=2.3.1
|
||||
|
@ -7,4 +7,3 @@ nosexcover
|
||||
pep8
|
||||
sphinx>=1.1.2
|
||||
mockfs
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user