[gsamfira,r=james-page] Add support for hyperv mechanism driver + VLAN and Flat networking options
This commit is contained in:
commit
8147eb967e
@ -21,3 +21,8 @@ options:
|
|||||||
default: False
|
default: False
|
||||||
type: boolean
|
type: boolean
|
||||||
description: Enable verbose logging
|
description: Enable verbose logging
|
||||||
|
data-port:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
The data port will be added to br-data and will allow usage of flat or VLAN
|
||||||
|
network types
|
||||||
|
@ -170,6 +170,7 @@ def canonical_url(configs, vip_setting='vip'):
|
|||||||
|
|
||||||
:configs : OSTemplateRenderer: A config tempating object to inspect for
|
:configs : OSTemplateRenderer: A config tempating object to inspect for
|
||||||
a complete https context.
|
a complete https context.
|
||||||
|
|
||||||
:vip_setting: str: Setting in charm config that specifies
|
:vip_setting: str: Setting in charm config that specifies
|
||||||
VIP address.
|
VIP address.
|
||||||
'''
|
'''
|
||||||
|
@ -21,12 +21,16 @@ def del_bridge(name):
|
|||||||
subprocess.check_call(["ovs-vsctl", "--", "--if-exists", "del-br", name])
|
subprocess.check_call(["ovs-vsctl", "--", "--if-exists", "del-br", name])
|
||||||
|
|
||||||
|
|
||||||
def add_bridge_port(name, port):
|
def add_bridge_port(name, port, promisc=False):
|
||||||
''' Add a port to the named openvswitch bridge '''
|
''' Add a port to the named openvswitch bridge '''
|
||||||
log('Adding port {} to bridge {}'.format(port, name))
|
log('Adding port {} to bridge {}'.format(port, name))
|
||||||
subprocess.check_call(["ovs-vsctl", "--", "--may-exist", "add-port",
|
subprocess.check_call(["ovs-vsctl", "--", "--may-exist", "add-port",
|
||||||
name, port])
|
name, port])
|
||||||
subprocess.check_call(["ip", "link", "set", port, "up"])
|
subprocess.check_call(["ip", "link", "set", port, "up"])
|
||||||
|
if promisc:
|
||||||
|
subprocess.check_call(["ip", "link", "set", port, "promisc", "on"])
|
||||||
|
else:
|
||||||
|
subprocess.check_call(["ip", "link", "set", port, "promisc", "off"])
|
||||||
|
|
||||||
|
|
||||||
def del_bridge_port(name, port):
|
def del_bridge_port(name, port):
|
||||||
@ -35,6 +39,7 @@ def del_bridge_port(name, port):
|
|||||||
subprocess.check_call(["ovs-vsctl", "--", "--if-exists", "del-port",
|
subprocess.check_call(["ovs-vsctl", "--", "--if-exists", "del-port",
|
||||||
name, port])
|
name, port])
|
||||||
subprocess.check_call(["ip", "link", "set", port, "down"])
|
subprocess.check_call(["ip", "link", "set", port, "down"])
|
||||||
|
subprocess.check_call(["ip", "link", "set", port, "promisc", "off"])
|
||||||
|
|
||||||
|
|
||||||
def set_manager(manager):
|
def set_manager(manager):
|
||||||
|
@ -7,19 +7,36 @@ class OpenStackAmuletDeployment(AmuletDeployment):
|
|||||||
"""This class inherits from AmuletDeployment and has additional support
|
"""This class inherits from AmuletDeployment and has additional support
|
||||||
that is specifically for use by OpenStack charms."""
|
that is specifically for use by OpenStack charms."""
|
||||||
|
|
||||||
def __init__(self, series=None, openstack=None):
|
def __init__(self, series=None, openstack=None, source=None):
|
||||||
"""Initialize the deployment environment."""
|
"""Initialize the deployment environment."""
|
||||||
self.openstack = None
|
|
||||||
super(OpenStackAmuletDeployment, self).__init__(series)
|
super(OpenStackAmuletDeployment, self).__init__(series)
|
||||||
|
self.openstack = openstack
|
||||||
|
self.source = source
|
||||||
|
|
||||||
if openstack:
|
def _add_services(self, this_service, other_services):
|
||||||
self.openstack = openstack
|
"""Add services to the deployment and set openstack-origin."""
|
||||||
|
super(OpenStackAmuletDeployment, self)._add_services(this_service,
|
||||||
|
other_services)
|
||||||
|
name = 0
|
||||||
|
services = other_services
|
||||||
|
services.append(this_service)
|
||||||
|
use_source = ['mysql', 'mongodb', 'rabbitmq-server', 'ceph']
|
||||||
|
|
||||||
|
if self.openstack:
|
||||||
|
for svc in services:
|
||||||
|
if svc[name] not in use_source:
|
||||||
|
config = {'openstack-origin': self.openstack}
|
||||||
|
self.d.configure(svc[name], config)
|
||||||
|
|
||||||
|
if self.source:
|
||||||
|
for svc in services:
|
||||||
|
if svc[name] in use_source:
|
||||||
|
config = {'source': self.source}
|
||||||
|
self.d.configure(svc[name], config)
|
||||||
|
|
||||||
def _configure_services(self, configs):
|
def _configure_services(self, configs):
|
||||||
"""Configure all of the services."""
|
"""Configure all of the services."""
|
||||||
for service, config in configs.iteritems():
|
for service, config in configs.iteritems():
|
||||||
if service == self.this_service:
|
|
||||||
config['openstack-origin'] = self.openstack
|
|
||||||
self.d.configure(service, config)
|
self.d.configure(service, config)
|
||||||
|
|
||||||
def _get_openstack_release(self):
|
def _get_openstack_release(self):
|
||||||
|
@ -74,7 +74,7 @@ class OpenStackAmuletUtils(AmuletUtils):
|
|||||||
if ret:
|
if ret:
|
||||||
return "unexpected tenant data - {}".format(ret)
|
return "unexpected tenant data - {}".format(ret)
|
||||||
if not found:
|
if not found:
|
||||||
return "tenant {} does not exist".format(e.name)
|
return "tenant {} does not exist".format(e['name'])
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def validate_role_data(self, expected, actual):
|
def validate_role_data(self, expected, actual):
|
||||||
@ -91,7 +91,7 @@ class OpenStackAmuletUtils(AmuletUtils):
|
|||||||
if ret:
|
if ret:
|
||||||
return "unexpected role data - {}".format(ret)
|
return "unexpected role data - {}".format(ret)
|
||||||
if not found:
|
if not found:
|
||||||
return "role {} does not exist".format(e.name)
|
return "role {} does not exist".format(e['name'])
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def validate_user_data(self, expected, actual):
|
def validate_user_data(self, expected, actual):
|
||||||
@ -110,7 +110,7 @@ class OpenStackAmuletUtils(AmuletUtils):
|
|||||||
if ret:
|
if ret:
|
||||||
return "unexpected user data - {}".format(ret)
|
return "unexpected user data - {}".format(ret)
|
||||||
if not found:
|
if not found:
|
||||||
return "user {} does not exist".format(e.name)
|
return "user {} does not exist".format(e['name'])
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def validate_flavor_data(self, expected, actual):
|
def validate_flavor_data(self, expected, actual):
|
||||||
@ -192,8 +192,8 @@ class OpenStackAmuletUtils(AmuletUtils):
|
|||||||
|
|
||||||
count = 1
|
count = 1
|
||||||
status = instance.status
|
status = instance.status
|
||||||
while status == 'BUILD' and count < 10:
|
while status != 'ACTIVE' and count < 60:
|
||||||
time.sleep(5)
|
time.sleep(3)
|
||||||
instance = nova.servers.get(instance.id)
|
instance = nova.servers.get(instance.id)
|
||||||
status = instance.status
|
status = instance.status
|
||||||
self.log.debug('instance status: {}'.format(status))
|
self.log.debug('instance status: {}'.format(status))
|
||||||
|
@ -24,6 +24,7 @@ from charmhelpers.core.hookenv import (
|
|||||||
unit_get,
|
unit_get,
|
||||||
unit_private_ip,
|
unit_private_ip,
|
||||||
ERROR,
|
ERROR,
|
||||||
|
INFO
|
||||||
)
|
)
|
||||||
|
|
||||||
from charmhelpers.contrib.hahelpers.cluster import (
|
from charmhelpers.contrib.hahelpers.cluster import (
|
||||||
@ -426,12 +427,13 @@ class ApacheSSLContext(OSContextGenerator):
|
|||||||
"""
|
"""
|
||||||
Generates a context for an apache vhost configuration that configures
|
Generates a context for an apache vhost configuration that configures
|
||||||
HTTPS reverse proxying for one or many endpoints. Generated context
|
HTTPS reverse proxying for one or many endpoints. Generated context
|
||||||
looks something like:
|
looks something like::
|
||||||
{
|
|
||||||
'namespace': 'cinder',
|
{
|
||||||
'private_address': 'iscsi.mycinderhost.com',
|
'namespace': 'cinder',
|
||||||
'endpoints': [(8776, 8766), (8777, 8767)]
|
'private_address': 'iscsi.mycinderhost.com',
|
||||||
}
|
'endpoints': [(8776, 8766), (8777, 8767)]
|
||||||
|
}
|
||||||
|
|
||||||
The endpoints list consists of a tuples mapping external ports
|
The endpoints list consists of a tuples mapping external ports
|
||||||
to internal ports.
|
to internal ports.
|
||||||
@ -641,7 +643,7 @@ class SubordinateConfigContext(OSContextGenerator):
|
|||||||
The subordinate interface allows subordinates to export their
|
The subordinate interface allows subordinates to export their
|
||||||
configuration requirements to the principle for multiple config
|
configuration requirements to the principle for multiple config
|
||||||
files and multiple serivces. Ie, a subordinate that has interfaces
|
files and multiple serivces. Ie, a subordinate that has interfaces
|
||||||
to both glance and nova may export to following yaml blob as json:
|
to both glance and nova may export to following yaml blob as json::
|
||||||
|
|
||||||
glance:
|
glance:
|
||||||
/etc/glance/glance-api.conf:
|
/etc/glance/glance-api.conf:
|
||||||
@ -660,7 +662,8 @@ class SubordinateConfigContext(OSContextGenerator):
|
|||||||
|
|
||||||
It is then up to the principle charms to subscribe this context to
|
It is then up to the principle charms to subscribe this context to
|
||||||
the service+config file it is interestd in. Configuration data will
|
the service+config file it is interestd in. Configuration data will
|
||||||
be available in the template context, in glance's case, as:
|
be available in the template context, in glance's case, as::
|
||||||
|
|
||||||
ctxt = {
|
ctxt = {
|
||||||
... other context ...
|
... other context ...
|
||||||
'subordinate_config': {
|
'subordinate_config': {
|
||||||
@ -687,7 +690,7 @@ class SubordinateConfigContext(OSContextGenerator):
|
|||||||
self.interface = interface
|
self.interface = interface
|
||||||
|
|
||||||
def __call__(self):
|
def __call__(self):
|
||||||
ctxt = {}
|
ctxt = {'sections': {}}
|
||||||
for rid in relation_ids(self.interface):
|
for rid in relation_ids(self.interface):
|
||||||
for unit in related_units(rid):
|
for unit in related_units(rid):
|
||||||
sub_config = relation_get('subordinate_configuration',
|
sub_config = relation_get('subordinate_configuration',
|
||||||
@ -713,10 +716,14 @@ class SubordinateConfigContext(OSContextGenerator):
|
|||||||
|
|
||||||
sub_config = sub_config[self.config_file]
|
sub_config = sub_config[self.config_file]
|
||||||
for k, v in sub_config.iteritems():
|
for k, v in sub_config.iteritems():
|
||||||
ctxt[k] = v
|
if k == 'sections':
|
||||||
|
for section, config_dict in v.iteritems():
|
||||||
|
log("adding section '%s'" % (section))
|
||||||
|
ctxt[k][section] = config_dict
|
||||||
|
else:
|
||||||
|
ctxt[k] = v
|
||||||
|
|
||||||
if not ctxt:
|
log("%d section(s) found" % (len(ctxt['sections'])), level=INFO)
|
||||||
ctxt['sections'] = {}
|
|
||||||
|
|
||||||
return ctxt
|
return ctxt
|
||||||
|
|
||||||
|
@ -30,17 +30,17 @@ def get_loader(templates_dir, os_release):
|
|||||||
loading dir.
|
loading dir.
|
||||||
|
|
||||||
A charm may also ship a templates dir with this module
|
A charm may also ship a templates dir with this module
|
||||||
and it will be appended to the bottom of the search list, eg:
|
and it will be appended to the bottom of the search list, eg::
|
||||||
hooks/charmhelpers/contrib/openstack/templates.
|
|
||||||
|
|
||||||
:param templates_dir: str: Base template directory containing release
|
hooks/charmhelpers/contrib/openstack/templates
|
||||||
sub-directories.
|
|
||||||
:param os_release : str: OpenStack release codename to construct template
|
|
||||||
loader.
|
|
||||||
|
|
||||||
:returns : jinja2.ChoiceLoader constructed with a list of
|
:param templates_dir (str): Base template directory containing release
|
||||||
jinja2.FilesystemLoaders, ordered in descending
|
sub-directories.
|
||||||
order by OpenStack release.
|
:param os_release (str): OpenStack release codename to construct template
|
||||||
|
loader.
|
||||||
|
:returns: jinja2.ChoiceLoader constructed with a list of
|
||||||
|
jinja2.FilesystemLoaders, ordered in descending
|
||||||
|
order by OpenStack release.
|
||||||
"""
|
"""
|
||||||
tmpl_dirs = [(rel, os.path.join(templates_dir, rel))
|
tmpl_dirs = [(rel, os.path.join(templates_dir, rel))
|
||||||
for rel in OPENSTACK_CODENAMES.itervalues()]
|
for rel in OPENSTACK_CODENAMES.itervalues()]
|
||||||
@ -111,7 +111,8 @@ class OSConfigRenderer(object):
|
|||||||
and ease the burden of managing config templates across multiple OpenStack
|
and ease the burden of managing config templates across multiple OpenStack
|
||||||
releases.
|
releases.
|
||||||
|
|
||||||
Basic usage:
|
Basic usage::
|
||||||
|
|
||||||
# import some common context generates from charmhelpers
|
# import some common context generates from charmhelpers
|
||||||
from charmhelpers.contrib.openstack import context
|
from charmhelpers.contrib.openstack import context
|
||||||
|
|
||||||
@ -131,21 +132,19 @@ class OSConfigRenderer(object):
|
|||||||
# write out all registered configs
|
# write out all registered configs
|
||||||
configs.write_all()
|
configs.write_all()
|
||||||
|
|
||||||
Details:
|
**OpenStack Releases and template loading**
|
||||||
|
|
||||||
OpenStack Releases and template loading
|
|
||||||
---------------------------------------
|
|
||||||
When the object is instantiated, it is associated with a specific OS
|
When the object is instantiated, it is associated with a specific OS
|
||||||
release. This dictates how the template loader will be constructed.
|
release. This dictates how the template loader will be constructed.
|
||||||
|
|
||||||
The constructed loader attempts to load the template from several places
|
The constructed loader attempts to load the template from several places
|
||||||
in the following order:
|
in the following order:
|
||||||
- from the most recent OS release-specific template dir (if one exists)
|
- from the most recent OS release-specific template dir (if one exists)
|
||||||
- the base templates_dir
|
- the base templates_dir
|
||||||
- a template directory shipped in the charm with this helper file.
|
- a template directory shipped in the charm with this helper file.
|
||||||
|
|
||||||
|
For the example above, '/tmp/templates' contains the following structure::
|
||||||
|
|
||||||
For the example above, '/tmp/templates' contains the following structure:
|
|
||||||
/tmp/templates/nova.conf
|
/tmp/templates/nova.conf
|
||||||
/tmp/templates/api-paste.ini
|
/tmp/templates/api-paste.ini
|
||||||
/tmp/templates/grizzly/api-paste.ini
|
/tmp/templates/grizzly/api-paste.ini
|
||||||
@ -169,8 +168,8 @@ class OSConfigRenderer(object):
|
|||||||
$CHARM/hooks/charmhelpers/contrib/openstack/templates. This allows
|
$CHARM/hooks/charmhelpers/contrib/openstack/templates. This allows
|
||||||
us to ship common templates (haproxy, apache) with the helpers.
|
us to ship common templates (haproxy, apache) with the helpers.
|
||||||
|
|
||||||
Context generators
|
**Context generators**
|
||||||
---------------------------------------
|
|
||||||
Context generators are used to generate template contexts during hook
|
Context generators are used to generate template contexts during hook
|
||||||
execution. Doing so may require inspecting service relations, charm
|
execution. Doing so may require inspecting service relations, charm
|
||||||
config, etc. When registered, a config file is associated with a list
|
config, etc. When registered, a config file is associated with a list
|
||||||
|
@ -303,7 +303,7 @@ def ensure_ceph_storage(service, pool, rbd_img, sizemb, mount_point,
|
|||||||
blk_device, fstype, system_services=[]):
|
blk_device, fstype, system_services=[]):
|
||||||
"""
|
"""
|
||||||
NOTE: This function must only be called from a single service unit for
|
NOTE: This function must only be called from a single service unit for
|
||||||
the same rbd_img otherwise data loss will occur.
|
the same rbd_img otherwise data loss will occur.
|
||||||
|
|
||||||
Ensures given pool and RBD image exists, is mapped to a block device,
|
Ensures given pool and RBD image exists, is mapped to a block device,
|
||||||
and the device is formatted and mounted at the given mount_point.
|
and the device is formatted and mounted at the given mount_point.
|
||||||
|
@ -25,7 +25,7 @@ cache = {}
|
|||||||
def cached(func):
|
def cached(func):
|
||||||
"""Cache return values for multiple executions of func + args
|
"""Cache return values for multiple executions of func + args
|
||||||
|
|
||||||
For example:
|
For example::
|
||||||
|
|
||||||
@cached
|
@cached
|
||||||
def unit_get(attribute):
|
def unit_get(attribute):
|
||||||
@ -445,18 +445,19 @@ class UnregisteredHookError(Exception):
|
|||||||
class Hooks(object):
|
class Hooks(object):
|
||||||
"""A convenient handler for hook functions.
|
"""A convenient handler for hook functions.
|
||||||
|
|
||||||
Example:
|
Example::
|
||||||
|
|
||||||
hooks = Hooks()
|
hooks = Hooks()
|
||||||
|
|
||||||
# register a hook, taking its name from the function name
|
# register a hook, taking its name from the function name
|
||||||
@hooks.hook()
|
@hooks.hook()
|
||||||
def install():
|
def install():
|
||||||
...
|
pass # your code here
|
||||||
|
|
||||||
# register a hook, providing a custom hook name
|
# register a hook, providing a custom hook name
|
||||||
@hooks.hook("config-changed")
|
@hooks.hook("config-changed")
|
||||||
def config_changed():
|
def config_changed():
|
||||||
...
|
pass # your code here
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# execute a hook based on the name the program is called by
|
# execute a hook based on the name the program is called by
|
||||||
|
@ -211,13 +211,13 @@ def file_hash(path):
|
|||||||
def restart_on_change(restart_map, stopstart=False):
|
def restart_on_change(restart_map, stopstart=False):
|
||||||
"""Restart services based on configuration files changing
|
"""Restart services based on configuration files changing
|
||||||
|
|
||||||
This function is used a decorator, for example
|
This function is used a decorator, for example::
|
||||||
|
|
||||||
@restart_on_change({
|
@restart_on_change({
|
||||||
'/etc/ceph/ceph.conf': [ 'cinder-api', 'cinder-volume' ]
|
'/etc/ceph/ceph.conf': [ 'cinder-api', 'cinder-volume' ]
|
||||||
})
|
})
|
||||||
def ceph_client_changed():
|
def ceph_client_changed():
|
||||||
...
|
pass # your code here
|
||||||
|
|
||||||
In this example, the cinder-api and cinder-volume services
|
In this example, the cinder-api and cinder-volume services
|
||||||
would be restarted if /etc/ceph/ceph.conf is changed by the
|
would be restarted if /etc/ceph/ceph.conf is changed by the
|
||||||
@ -313,9 +313,11 @@ def get_nic_hwaddr(nic):
|
|||||||
|
|
||||||
def cmp_pkgrevno(package, revno, pkgcache=None):
|
def cmp_pkgrevno(package, revno, pkgcache=None):
|
||||||
'''Compare supplied revno with the revno of the installed package
|
'''Compare supplied revno with the revno of the installed package
|
||||||
1 => Installed revno is greater than supplied arg
|
|
||||||
0 => Installed revno is the same as supplied arg
|
* 1 => Installed revno is greater than supplied arg
|
||||||
-1 => Installed revno is less than supplied arg
|
* 0 => Installed revno is the same as supplied arg
|
||||||
|
* -1 => Installed revno is less than supplied arg
|
||||||
|
|
||||||
'''
|
'''
|
||||||
import apt_pkg
|
import apt_pkg
|
||||||
if not pkgcache:
|
if not pkgcache:
|
||||||
|
@ -235,31 +235,39 @@ def configure_sources(update=False,
|
|||||||
sources_var='install_sources',
|
sources_var='install_sources',
|
||||||
keys_var='install_keys'):
|
keys_var='install_keys'):
|
||||||
"""
|
"""
|
||||||
Configure multiple sources from charm configuration
|
Configure multiple sources from charm configuration.
|
||||||
|
|
||||||
|
The lists are encoded as yaml fragments in the configuration.
|
||||||
|
The frament needs to be included as a string.
|
||||||
|
|
||||||
Example config:
|
Example config:
|
||||||
install_sources:
|
install_sources: |
|
||||||
- "ppa:foo"
|
- "ppa:foo"
|
||||||
- "http://example.com/repo precise main"
|
- "http://example.com/repo precise main"
|
||||||
install_keys:
|
install_keys: |
|
||||||
- null
|
- null
|
||||||
- "a1b2c3d4"
|
- "a1b2c3d4"
|
||||||
|
|
||||||
Note that 'null' (a.k.a. None) should not be quoted.
|
Note that 'null' (a.k.a. None) should not be quoted.
|
||||||
"""
|
"""
|
||||||
sources = safe_load(config(sources_var))
|
sources = safe_load((config(sources_var) or '').strip()) or []
|
||||||
keys = config(keys_var)
|
keys = safe_load((config(keys_var) or '').strip()) or None
|
||||||
if keys is not None:
|
|
||||||
keys = safe_load(keys)
|
if isinstance(sources, basestring):
|
||||||
if isinstance(sources, basestring) and (
|
sources = [sources]
|
||||||
keys is None or isinstance(keys, basestring)):
|
|
||||||
add_source(sources, keys)
|
if keys is None:
|
||||||
|
for source in sources:
|
||||||
|
add_source(source, None)
|
||||||
else:
|
else:
|
||||||
if not len(sources) == len(keys):
|
if isinstance(keys, basestring):
|
||||||
msg = 'Install sources and keys lists are different lengths'
|
keys = [keys]
|
||||||
raise SourceConfigError(msg)
|
|
||||||
for src_num in range(len(sources)):
|
if len(sources) != len(keys):
|
||||||
add_source(sources[src_num], keys[src_num])
|
raise SourceConfigError(
|
||||||
|
'Install sources and keys lists are different lengths')
|
||||||
|
for source, key in zip(sources, keys):
|
||||||
|
add_source(source, key)
|
||||||
if update:
|
if update:
|
||||||
apt_update(fatal=True)
|
apt_update(fatal=True)
|
||||||
|
|
||||||
|
@ -5,12 +5,16 @@ from charmhelpers.core.hookenv import (
|
|||||||
config,
|
config,
|
||||||
unit_get,
|
unit_get,
|
||||||
)
|
)
|
||||||
|
from charmhelpers.core.host import list_nics, get_nic_hwaddr
|
||||||
from charmhelpers.contrib.openstack import context
|
from charmhelpers.contrib.openstack import context
|
||||||
from charmhelpers.core.host import service_running, service_start
|
from charmhelpers.core.host import service_running, service_start
|
||||||
from charmhelpers.contrib.network.ovs import add_bridge
|
from charmhelpers.contrib.network.ovs import add_bridge, add_bridge_port
|
||||||
from charmhelpers.contrib.openstack.utils import get_host_ip
|
from charmhelpers.contrib.openstack.utils import get_host_ip
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
OVS_BRIDGE = 'br-int'
|
OVS_BRIDGE = 'br-int'
|
||||||
|
DATA_BRIDGE = 'br-data'
|
||||||
|
|
||||||
|
|
||||||
def _neutron_security_groups():
|
def _neutron_security_groups():
|
||||||
@ -43,10 +47,31 @@ class OVSPluginContext(context.NeutronContext):
|
|||||||
def neutron_security_groups(self):
|
def neutron_security_groups(self):
|
||||||
return _neutron_security_groups()
|
return _neutron_security_groups()
|
||||||
|
|
||||||
|
def get_data_port(self):
|
||||||
|
data_ports = config('data-port')
|
||||||
|
if not data_ports:
|
||||||
|
return None
|
||||||
|
hwaddrs = {}
|
||||||
|
for nic in list_nics(['eth', 'bond']):
|
||||||
|
hwaddrs[get_nic_hwaddr(nic).lower()] = nic
|
||||||
|
mac_regex = re.compile(r'([0-9A-F]{2}[:-]){5}([0-9A-F]{2})', re.I)
|
||||||
|
for entry in data_ports.split():
|
||||||
|
entry = entry.strip().lower()
|
||||||
|
if re.match(mac_regex, entry):
|
||||||
|
if entry in hwaddrs:
|
||||||
|
return hwaddrs[entry]
|
||||||
|
else:
|
||||||
|
return entry
|
||||||
|
return None
|
||||||
|
|
||||||
def _ensure_bridge(self):
|
def _ensure_bridge(self):
|
||||||
if not service_running('openvswitch-switch'):
|
if not service_running('openvswitch-switch'):
|
||||||
service_start('openvswitch-switch')
|
service_start('openvswitch-switch')
|
||||||
add_bridge(OVS_BRIDGE)
|
add_bridge(OVS_BRIDGE)
|
||||||
|
add_bridge(DATA_BRIDGE)
|
||||||
|
data_port = self.get_data_port()
|
||||||
|
if data_port:
|
||||||
|
add_bridge_port(DATA_BRIDGE, data_port, promisc=True)
|
||||||
|
|
||||||
def ovs_ctxt(self):
|
def ovs_ctxt(self):
|
||||||
# In addition to generating config context, ensure the OVS service
|
# In addition to generating config context, ensure the OVS service
|
||||||
|
@ -5,9 +5,9 @@
|
|||||||
# Config managed by neutron-openvswitch charm
|
# Config managed by neutron-openvswitch charm
|
||||||
###############################################################################
|
###############################################################################
|
||||||
[ml2]
|
[ml2]
|
||||||
type_drivers = gre,vxlan
|
type_drivers = gre,vxlan,vlan,flat
|
||||||
tenant_network_types = gre,vxlan
|
tenant_network_types = gre,vxlan,vlan,flat
|
||||||
mechanism_drivers = openvswitch
|
mechanism_drivers = openvswitch,hyperv
|
||||||
|
|
||||||
[ml2_type_gre]
|
[ml2_type_gre]
|
||||||
tunnel_id_ranges = 1:1000
|
tunnel_id_ranges = 1:1000
|
||||||
@ -15,9 +15,16 @@ tunnel_id_ranges = 1:1000
|
|||||||
[ml2_type_vxlan]
|
[ml2_type_vxlan]
|
||||||
vni_ranges = 1001:2000
|
vni_ranges = 1001:2000
|
||||||
|
|
||||||
|
[ml2_type_vlan]
|
||||||
|
network_vlan_ranges = physnet1:1000:2000
|
||||||
|
|
||||||
|
[ml2_type_flat]
|
||||||
|
flat_networks = physnet1
|
||||||
|
|
||||||
[ovs]
|
[ovs]
|
||||||
enable_tunneling = True
|
enable_tunneling = True
|
||||||
local_ip = {{ local_ip }}
|
local_ip = {{ local_ip }}
|
||||||
|
bridge_mappings = physnet1:br-data
|
||||||
|
|
||||||
[agent]
|
[agent]
|
||||||
tunnel_types = gre
|
tunnel_types = gre
|
||||||
|
@ -10,9 +10,12 @@ TO_PATCH = [
|
|||||||
'config',
|
'config',
|
||||||
'unit_get',
|
'unit_get',
|
||||||
'add_bridge',
|
'add_bridge',
|
||||||
|
'add_bridge_port',
|
||||||
'service_running',
|
'service_running',
|
||||||
'service_start',
|
'service_start',
|
||||||
'get_host_ip',
|
'get_host_ip',
|
||||||
|
'get_nic_hwaddr',
|
||||||
|
'list_nics',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -29,6 +32,38 @@ class OVSPluginContextTest(CharmTestCase):
|
|||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
super(OVSPluginContextTest, self).tearDown()
|
super(OVSPluginContextTest, self).tearDown()
|
||||||
|
|
||||||
|
def test_data_port_name(self):
|
||||||
|
self.test_config.set('data-port', 'em1')
|
||||||
|
self.assertEquals(context.OVSPluginContext().get_data_port(), 'em1')
|
||||||
|
|
||||||
|
def test_data_port_mac(self):
|
||||||
|
machine_machs = {
|
||||||
|
'em1': 'aa:aa:aa:aa:aa:aa',
|
||||||
|
'eth0': 'bb:bb:bb:bb:bb:bb',
|
||||||
|
}
|
||||||
|
absent_mac = "cc:cc:cc:cc:cc:cc"
|
||||||
|
config_macs = "%s %s" % (absent_mac, machine_machs['em1'])
|
||||||
|
self.test_config.set('data-port', config_macs)
|
||||||
|
|
||||||
|
def get_hwaddr(eth):
|
||||||
|
return machine_machs[eth]
|
||||||
|
self.get_nic_hwaddr.side_effect = get_hwaddr
|
||||||
|
self.list_nics.return_value = machine_machs.keys()
|
||||||
|
self.assertEquals(context.OVSPluginContext().get_data_port(), 'em1')
|
||||||
|
|
||||||
|
@patch.object(context.OVSPluginContext, 'get_data_port')
|
||||||
|
def test_ensure_bridge_data_port_present(self, get_data_port):
|
||||||
|
def add_port(bridge, port, promisc):
|
||||||
|
if bridge == 'br-data' and port == 'em1' and promisc is True:
|
||||||
|
self.bridge_added = True
|
||||||
|
return
|
||||||
|
self.bridge_added = False
|
||||||
|
|
||||||
|
get_data_port.return_value = 'em1'
|
||||||
|
self.add_bridge_port.side_effect = add_port
|
||||||
|
context.OVSPluginContext()._ensure_bridge()
|
||||||
|
self.assertEquals(self.bridge_added, True)
|
||||||
|
|
||||||
@patch.object(charmhelpers.contrib.openstack.context, 'config')
|
@patch.object(charmhelpers.contrib.openstack.context, 'config')
|
||||||
@patch.object(charmhelpers.contrib.openstack.context, 'unit_get')
|
@patch.object(charmhelpers.contrib.openstack.context, 'unit_get')
|
||||||
@patch.object(charmhelpers.contrib.openstack.context, 'is_clustered')
|
@patch.object(charmhelpers.contrib.openstack.context, 'is_clustered')
|
||||||
|
Loading…
Reference in New Issue
Block a user