Deploy from source
This commit is contained in:
commit
529c148ec8
@ -1,2 +1,3 @@
|
|||||||
bin
|
bin
|
||||||
.coverage
|
.coverage
|
||||||
|
tags
|
||||||
|
7
Makefile
7
Makefile
@ -2,7 +2,7 @@
|
|||||||
PYTHON := /usr/bin/env python
|
PYTHON := /usr/bin/env python
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
@flake8 --exclude hooks/charmhelpers hooks unit_tests tests
|
@flake8 --exclude hooks/charmhelpers actions hooks unit_tests tests
|
||||||
@charm proof
|
@charm proof
|
||||||
|
|
||||||
unit_test:
|
unit_test:
|
||||||
@ -15,7 +15,7 @@ bin/charm_helpers_sync.py:
|
|||||||
> bin/charm_helpers_sync.py
|
> bin/charm_helpers_sync.py
|
||||||
|
|
||||||
sync: bin/charm_helpers_sync.py
|
sync: bin/charm_helpers_sync.py
|
||||||
@$(PYTHON) bin/charm_helpers_sync.py -c charm-helpers-sync.yaml
|
@$(PYTHON) bin/charm_helpers_sync.py -c charm-helpers-hooks.yaml
|
||||||
@$(PYTHON) bin/charm_helpers_sync.py -c charm-helpers-tests.yaml
|
@$(PYTHON) bin/charm_helpers_sync.py -c charm-helpers-tests.yaml
|
||||||
|
|
||||||
test:
|
test:
|
||||||
@ -25,7 +25,8 @@ test:
|
|||||||
# https://bugs.launchpad.net/amulet/+bug/1320357
|
# https://bugs.launchpad.net/amulet/+bug/1320357
|
||||||
@juju test -v -p AMULET_HTTP_PROXY --timeout 900 \
|
@juju test -v -p AMULET_HTTP_PROXY --timeout 900 \
|
||||||
00-setup 14-basic-precise-icehouse 15-basic-trusty-icehouse \
|
00-setup 14-basic-precise-icehouse 15-basic-trusty-icehouse \
|
||||||
16-basic-trusty-juno
|
16-basic-trusty-icehouse-git 17-basic-trusty-juno \
|
||||||
|
18-basic-trusty-juno-git
|
||||||
|
|
||||||
publish: lint unit_test
|
publish: lint unit_test
|
||||||
bzr push lp:charms/neutron-openvswitch
|
bzr push lp:charms/neutron-openvswitch
|
||||||
|
87
README.md
87
README.md
@ -41,3 +41,90 @@ This charm has a configuration option to allow users to disable any per-instance
|
|||||||
...
|
...
|
||||||
|
|
||||||
These compute nodes could then be accessed by cloud users via use of host aggregates with specific flavors to target instances to hypervisors with no per-instance security.
|
These compute nodes could then be accessed by cloud users via use of host aggregates with specific flavors to target instances to hypervisors with no per-instance security.
|
||||||
|
|
||||||
|
# Deploying from source
|
||||||
|
|
||||||
|
The minimum openstack-origin-git config required to deploy from source is:
|
||||||
|
|
||||||
|
openstack-origin-git:
|
||||||
|
"repositories:
|
||||||
|
- {name: requirements,
|
||||||
|
repository: 'git://git.openstack.org/openstack/requirements',
|
||||||
|
branch: stable/juno}
|
||||||
|
- {name: neutron,
|
||||||
|
repository: 'git://git.openstack.org/openstack/neutron',
|
||||||
|
branch: stable/juno}"
|
||||||
|
|
||||||
|
Note that there are only two 'name' values the charm knows about: 'requirements'
|
||||||
|
and 'neutron'. These repositories must correspond to these 'name' values.
|
||||||
|
Additionally, the requirements repository must be specified first and the
|
||||||
|
neutron repository must be specified last. All other repostories are installed
|
||||||
|
in the order in which they are specified.
|
||||||
|
|
||||||
|
The following is a full list of current tip repos (may not be up-to-date):
|
||||||
|
|
||||||
|
openstack-origin-git:
|
||||||
|
"repositories:
|
||||||
|
- {name: requirements,
|
||||||
|
repository: 'git://git.openstack.org/openstack/requirements',
|
||||||
|
branch: master}
|
||||||
|
- {name: oslo-concurrency,
|
||||||
|
repository: 'git://git.openstack.org/openstack/oslo.concurrency',
|
||||||
|
branch: master}
|
||||||
|
- {name: oslo-config,
|
||||||
|
repository: 'git://git.openstack.org/openstack/oslo.config',
|
||||||
|
branch: master}
|
||||||
|
- {name: oslo-context,
|
||||||
|
repository: 'git://git.openstack.org/openstack/oslo.context.git',
|
||||||
|
branch: master}
|
||||||
|
- {name: oslo-db,
|
||||||
|
repository: 'git://git.openstack.org/openstack/oslo.db',
|
||||||
|
branch: master}
|
||||||
|
- {name: oslo-i18n,
|
||||||
|
repository: 'git://git.openstack.org/openstack/oslo.i18n',
|
||||||
|
branch: master}
|
||||||
|
- {name: oslo-messaging,
|
||||||
|
repository: 'git://git.openstack.org/openstack/oslo.messaging.git',
|
||||||
|
branch: master}
|
||||||
|
- {name: oslo-middleware,
|
||||||
|
repository': 'git://git.openstack.org/openstack/oslo.middleware.git',
|
||||||
|
branch: master}
|
||||||
|
- {name: oslo-rootwrap',
|
||||||
|
repository: 'git://git.openstack.org/openstack/oslo.rootwrap.git',
|
||||||
|
branch: master}
|
||||||
|
- {name: oslo-serialization,
|
||||||
|
repository: 'git://git.openstack.org/openstack/oslo.serialization',
|
||||||
|
branch: master}
|
||||||
|
- {name: oslo-utils,
|
||||||
|
repository: 'git://git.openstack.org/openstack/oslo.utils',
|
||||||
|
branch: master}
|
||||||
|
- {name: pbr,
|
||||||
|
repository: 'git://git.openstack.org/openstack-dev/pbr',
|
||||||
|
branch: master}
|
||||||
|
- {name: stevedore,
|
||||||
|
repository: 'git://git.openstack.org/openstack/stevedore.git',
|
||||||
|
branch: 'master'}
|
||||||
|
- {name: python-keystoneclient,
|
||||||
|
repository: 'git://git.openstack.org/openstack/python-keystoneclient',
|
||||||
|
branch: master}
|
||||||
|
- {name: python-neutronclient,
|
||||||
|
repository: 'git://git.openstack.org/openstack/python-neutronclient.git',
|
||||||
|
branch: master}
|
||||||
|
- {name: python-novaclient,
|
||||||
|
repository': 'git://git.openstack.org/openstack/python-novaclient.git',
|
||||||
|
branch: master}
|
||||||
|
- {name: keystonemiddleware,
|
||||||
|
repository: 'git://git.openstack.org/openstack/keystonemiddleware',
|
||||||
|
branch: master}
|
||||||
|
- {name: neutron-fwaas,
|
||||||
|
repository': 'git://git.openstack.org/openstack/neutron-fwaas.git',
|
||||||
|
branch: master}
|
||||||
|
- {name: neutron-lbaas,
|
||||||
|
repository: 'git://git.openstack.org/openstack/neutron-lbaas.git',
|
||||||
|
branch: master}
|
||||||
|
- {name: neutron-vpnaas,
|
||||||
|
repository: 'git://git.openstack.org/openstack/neutron-vpnaas.git',
|
||||||
|
branch: master}
|
||||||
|
- {name: neutron,
|
||||||
|
repository: 'git://git.openstack.org/openstack/neutron',
|
||||||
|
branch: master}"
|
||||||
|
2
actions.yaml
Normal file
2
actions.yaml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
git-reinstall:
|
||||||
|
description: Reinstall neutron-openvswitch from the openstack-origin-git repositories.
|
1
actions/git-reinstall
Symbolic link
1
actions/git-reinstall
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
git_reinstall.py
|
40
actions/git_reinstall.py
Executable file
40
actions/git_reinstall.py
Executable file
@ -0,0 +1,40 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
sys.path.append('hooks/')
|
||||||
|
|
||||||
|
from charmhelpers.contrib.openstack.utils import (
|
||||||
|
git_install_requested,
|
||||||
|
)
|
||||||
|
|
||||||
|
from charmhelpers.core.hookenv import (
|
||||||
|
action_set,
|
||||||
|
action_fail,
|
||||||
|
config,
|
||||||
|
)
|
||||||
|
|
||||||
|
from neutron_ovs_utils import (
|
||||||
|
git_install,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def git_reinstall():
|
||||||
|
"""Reinstall from source and restart services.
|
||||||
|
|
||||||
|
If the openstack-origin-git config option was used to install openstack
|
||||||
|
from source git repositories, then this action can be used to reinstall
|
||||||
|
from updated git repositories, followed by a restart of services."""
|
||||||
|
if not git_install_requested():
|
||||||
|
action_fail('openstack-origin-git is not configured')
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
git_install(config('openstack-origin-git'))
|
||||||
|
except:
|
||||||
|
action_set({'traceback': traceback.format_exc()})
|
||||||
|
action_fail('git-reinstall resulted in an unexpected error')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
git_reinstall()
|
26
config.yaml
26
config.yaml
@ -1,4 +1,21 @@
|
|||||||
options:
|
options:
|
||||||
|
openstack-origin-git:
|
||||||
|
default:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Specifies a YAML-formatted dictionary listing the git
|
||||||
|
repositories and branches from which to install OpenStack and
|
||||||
|
its dependencies.
|
||||||
|
|
||||||
|
When openstack-origin-git is specified, openstack-specific
|
||||||
|
packages will be installed from source rather than from the
|
||||||
|
the nova-compute charm's openstack-origin repository.
|
||||||
|
|
||||||
|
Note that the installed config files will be determined based on
|
||||||
|
the OpenStack release of the nova-compute charm's openstack-origin
|
||||||
|
option.
|
||||||
|
|
||||||
|
For more details see README.md.
|
||||||
rabbit-user:
|
rabbit-user:
|
||||||
default: neutron
|
default: neutron
|
||||||
type: string
|
type: string
|
||||||
@ -60,3 +77,12 @@ options:
|
|||||||
.
|
.
|
||||||
This network will be used for tenant network traffic in overlay
|
This network will be used for tenant network traffic in overlay
|
||||||
networks.
|
networks.
|
||||||
|
ext-port:
|
||||||
|
type: string
|
||||||
|
default:
|
||||||
|
description: |
|
||||||
|
A space-separated list of external ports to use for routing of instance
|
||||||
|
traffic to the external public network. Valid values are either MAC
|
||||||
|
addresses (in which case only MAC addresses for interfaces without an IP
|
||||||
|
address already assigned will be used), or interfaces (eth0)
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
|
# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
from collections import OrderedDict
|
||||||
from charmhelpers.contrib.amulet.deployment import (
|
from charmhelpers.contrib.amulet.deployment import (
|
||||||
AmuletDeployment
|
AmuletDeployment
|
||||||
)
|
)
|
||||||
@ -100,12 +101,34 @@ class OpenStackAmuletDeployment(AmuletDeployment):
|
|||||||
"""
|
"""
|
||||||
(self.precise_essex, self.precise_folsom, self.precise_grizzly,
|
(self.precise_essex, self.precise_folsom, self.precise_grizzly,
|
||||||
self.precise_havana, self.precise_icehouse,
|
self.precise_havana, self.precise_icehouse,
|
||||||
self.trusty_icehouse) = range(6)
|
self.trusty_icehouse, self.trusty_juno, self.trusty_kilo) = range(8)
|
||||||
releases = {
|
releases = {
|
||||||
('precise', None): self.precise_essex,
|
('precise', None): self.precise_essex,
|
||||||
('precise', 'cloud:precise-folsom'): self.precise_folsom,
|
('precise', 'cloud:precise-folsom'): self.precise_folsom,
|
||||||
('precise', 'cloud:precise-grizzly'): self.precise_grizzly,
|
('precise', 'cloud:precise-grizzly'): self.precise_grizzly,
|
||||||
('precise', 'cloud:precise-havana'): self.precise_havana,
|
('precise', 'cloud:precise-havana'): self.precise_havana,
|
||||||
('precise', 'cloud:precise-icehouse'): self.precise_icehouse,
|
('precise', 'cloud:precise-icehouse'): self.precise_icehouse,
|
||||||
('trusty', None): self.trusty_icehouse}
|
('trusty', None): self.trusty_icehouse,
|
||||||
|
('trusty', 'cloud:trusty-juno'): self.trusty_juno,
|
||||||
|
('trusty', 'cloud:trusty-kilo'): self.trusty_kilo}
|
||||||
return releases[(self.series, self.openstack)]
|
return releases[(self.series, self.openstack)]
|
||||||
|
|
||||||
|
def _get_openstack_release_string(self):
|
||||||
|
"""Get openstack release string.
|
||||||
|
|
||||||
|
Return a string representing the openstack release.
|
||||||
|
"""
|
||||||
|
releases = OrderedDict([
|
||||||
|
('precise', 'essex'),
|
||||||
|
('quantal', 'folsom'),
|
||||||
|
('raring', 'grizzly'),
|
||||||
|
('saucy', 'havana'),
|
||||||
|
('trusty', 'icehouse'),
|
||||||
|
('utopic', 'juno'),
|
||||||
|
('vivid', 'kilo'),
|
||||||
|
])
|
||||||
|
if self.openstack:
|
||||||
|
os_origin = self.openstack.split(':')[1]
|
||||||
|
return os_origin.split('%s-' % self.series)[1].split('/')[0]
|
||||||
|
else:
|
||||||
|
return releases[self.series]
|
||||||
|
@ -47,6 +47,7 @@ from charmhelpers.core.hookenv import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from charmhelpers.core.sysctl import create as sysctl_create
|
from charmhelpers.core.sysctl import create as sysctl_create
|
||||||
|
from charmhelpers.core.strutils import bool_from_string
|
||||||
|
|
||||||
from charmhelpers.core.host import (
|
from charmhelpers.core.host import (
|
||||||
list_nics,
|
list_nics,
|
||||||
@ -67,6 +68,7 @@ from charmhelpers.contrib.hahelpers.apache import (
|
|||||||
)
|
)
|
||||||
from charmhelpers.contrib.openstack.neutron import (
|
from charmhelpers.contrib.openstack.neutron import (
|
||||||
neutron_plugin_attribute,
|
neutron_plugin_attribute,
|
||||||
|
parse_data_port_mappings,
|
||||||
)
|
)
|
||||||
from charmhelpers.contrib.openstack.ip import (
|
from charmhelpers.contrib.openstack.ip import (
|
||||||
resolve_address,
|
resolve_address,
|
||||||
@ -82,7 +84,6 @@ from charmhelpers.contrib.network.ip import (
|
|||||||
is_bridge_member,
|
is_bridge_member,
|
||||||
)
|
)
|
||||||
from charmhelpers.contrib.openstack.utils import get_host_ip
|
from charmhelpers.contrib.openstack.utils import get_host_ip
|
||||||
|
|
||||||
CA_CERT_PATH = '/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt'
|
CA_CERT_PATH = '/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt'
|
||||||
ADDRESS_TYPES = ['admin', 'internal', 'public']
|
ADDRESS_TYPES = ['admin', 'internal', 'public']
|
||||||
|
|
||||||
@ -319,14 +320,15 @@ def db_ssl(rdata, ctxt, ssl_dir):
|
|||||||
|
|
||||||
|
|
||||||
class IdentityServiceContext(OSContextGenerator):
|
class IdentityServiceContext(OSContextGenerator):
|
||||||
interfaces = ['identity-service']
|
|
||||||
|
|
||||||
def __init__(self, service=None, service_user=None):
|
def __init__(self, service=None, service_user=None, rel_name='identity-service'):
|
||||||
self.service = service
|
self.service = service
|
||||||
self.service_user = service_user
|
self.service_user = service_user
|
||||||
|
self.rel_name = rel_name
|
||||||
|
self.interfaces = [self.rel_name]
|
||||||
|
|
||||||
def __call__(self):
|
def __call__(self):
|
||||||
log('Generating template context for identity-service', level=DEBUG)
|
log('Generating template context for ' + self.rel_name, level=DEBUG)
|
||||||
ctxt = {}
|
ctxt = {}
|
||||||
|
|
||||||
if self.service and self.service_user:
|
if self.service and self.service_user:
|
||||||
@ -340,7 +342,7 @@ class IdentityServiceContext(OSContextGenerator):
|
|||||||
|
|
||||||
ctxt['signing_dir'] = cachedir
|
ctxt['signing_dir'] = cachedir
|
||||||
|
|
||||||
for rid in relation_ids('identity-service'):
|
for rid in relation_ids(self.rel_name):
|
||||||
for unit in related_units(rid):
|
for unit in related_units(rid):
|
||||||
rdata = relation_get(rid=rid, unit=unit)
|
rdata = relation_get(rid=rid, unit=unit)
|
||||||
serv_host = rdata.get('service_host')
|
serv_host = rdata.get('service_host')
|
||||||
@ -1162,3 +1164,145 @@ class SysctlContext(OSContextGenerator):
|
|||||||
sysctl_create(sysctl_dict,
|
sysctl_create(sysctl_dict,
|
||||||
'/etc/sysctl.d/50-{0}.conf'.format(charm_name()))
|
'/etc/sysctl.d/50-{0}.conf'.format(charm_name()))
|
||||||
return {'sysctl': sysctl_dict}
|
return {'sysctl': sysctl_dict}
|
||||||
|
|
||||||
|
|
||||||
|
class NeutronAPIContext(OSContextGenerator):
|
||||||
|
'''
|
||||||
|
Inspects current neutron-plugin-api relation for neutron settings. Return
|
||||||
|
defaults if it is not present.
|
||||||
|
'''
|
||||||
|
interfaces = ['neutron-plugin-api']
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
self.neutron_defaults = {
|
||||||
|
'l2_population': {
|
||||||
|
'rel_key': 'l2-population',
|
||||||
|
'default': False,
|
||||||
|
},
|
||||||
|
'overlay_network_type': {
|
||||||
|
'rel_key': 'overlay-network-type',
|
||||||
|
'default': 'gre',
|
||||||
|
},
|
||||||
|
'neutron_security_groups': {
|
||||||
|
'rel_key': 'neutron-security-groups',
|
||||||
|
'default': False,
|
||||||
|
},
|
||||||
|
'network_device_mtu': {
|
||||||
|
'rel_key': 'network-device-mtu',
|
||||||
|
'default': None,
|
||||||
|
},
|
||||||
|
'enable_dvr': {
|
||||||
|
'rel_key': 'enable-dvr',
|
||||||
|
'default': False,
|
||||||
|
},
|
||||||
|
'enable_l3ha': {
|
||||||
|
'rel_key': 'enable-l3ha',
|
||||||
|
'default': False,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ctxt = self.get_neutron_options({})
|
||||||
|
for rid in relation_ids('neutron-plugin-api'):
|
||||||
|
for unit in related_units(rid):
|
||||||
|
rdata = relation_get(rid=rid, unit=unit)
|
||||||
|
if 'l2-population' in rdata:
|
||||||
|
ctxt.update(self.get_neutron_options(rdata))
|
||||||
|
|
||||||
|
return ctxt
|
||||||
|
|
||||||
|
def get_neutron_options(self, rdata):
|
||||||
|
settings = {}
|
||||||
|
for nkey in self.neutron_defaults.keys():
|
||||||
|
defv = self.neutron_defaults[nkey]['default']
|
||||||
|
rkey = self.neutron_defaults[nkey]['rel_key']
|
||||||
|
if rkey in rdata.keys():
|
||||||
|
if type(defv) is bool:
|
||||||
|
settings[nkey] = bool_from_string(rdata[rkey])
|
||||||
|
else:
|
||||||
|
settings[nkey] = rdata[rkey]
|
||||||
|
else:
|
||||||
|
settings[nkey] = defv
|
||||||
|
return settings
|
||||||
|
|
||||||
|
|
||||||
|
class ExternalPortContext(NeutronPortContext):
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
ctxt = {}
|
||||||
|
ports = config('ext-port')
|
||||||
|
if ports:
|
||||||
|
ports = [p.strip() for p in ports.split()]
|
||||||
|
ports = self.resolve_ports(ports)
|
||||||
|
if ports:
|
||||||
|
ctxt = {"ext_port": ports[0]}
|
||||||
|
napi_settings = NeutronAPIContext()()
|
||||||
|
mtu = napi_settings.get('network_device_mtu')
|
||||||
|
if mtu:
|
||||||
|
ctxt['ext_port_mtu'] = mtu
|
||||||
|
|
||||||
|
return ctxt
|
||||||
|
|
||||||
|
|
||||||
|
class DataPortContext(NeutronPortContext):
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
ports = config('data-port')
|
||||||
|
if ports:
|
||||||
|
portmap = parse_data_port_mappings(ports)
|
||||||
|
ports = portmap.values()
|
||||||
|
resolved = self.resolve_ports(ports)
|
||||||
|
normalized = {get_nic_hwaddr(port): port for port in resolved
|
||||||
|
if port not in ports}
|
||||||
|
normalized.update({port: port for port in resolved
|
||||||
|
if port in ports})
|
||||||
|
if resolved:
|
||||||
|
return {bridge: normalized[port] for bridge, port in
|
||||||
|
six.iteritems(portmap) if port in normalized.keys()}
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class PhyNICMTUContext(DataPortContext):
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
ctxt = {}
|
||||||
|
mappings = super(PhyNICMTUContext, self).__call__()
|
||||||
|
if mappings and mappings.values():
|
||||||
|
ports = mappings.values()
|
||||||
|
napi_settings = NeutronAPIContext()()
|
||||||
|
mtu = napi_settings.get('network_device_mtu')
|
||||||
|
if mtu:
|
||||||
|
ctxt["devs"] = '\\n'.join(ports)
|
||||||
|
ctxt['mtu'] = mtu
|
||||||
|
|
||||||
|
return ctxt
|
||||||
|
|
||||||
|
|
||||||
|
class NetworkServiceContext(OSContextGenerator):
|
||||||
|
|
||||||
|
def __init__(self, rel_name='quantum-network-service'):
|
||||||
|
self.rel_name = rel_name
|
||||||
|
self.interfaces = [rel_name]
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
for rid in relation_ids(self.rel_name):
|
||||||
|
for unit in related_units(rid):
|
||||||
|
rdata = relation_get(rid=rid, unit=unit)
|
||||||
|
ctxt = {
|
||||||
|
'keystone_host': rdata.get('keystone_host'),
|
||||||
|
'service_port': rdata.get('service_port'),
|
||||||
|
'auth_port': rdata.get('auth_port'),
|
||||||
|
'service_tenant': rdata.get('service_tenant'),
|
||||||
|
'service_username': rdata.get('service_username'),
|
||||||
|
'service_password': rdata.get('service_password'),
|
||||||
|
'quantum_host': rdata.get('quantum_host'),
|
||||||
|
'quantum_port': rdata.get('quantum_port'),
|
||||||
|
'quantum_url': rdata.get('quantum_url'),
|
||||||
|
'region': rdata.get('region'),
|
||||||
|
'service_protocol':
|
||||||
|
rdata.get('service_protocol') or 'http',
|
||||||
|
'auth_protocol':
|
||||||
|
rdata.get('auth_protocol') or 'http',
|
||||||
|
}
|
||||||
|
if context_complete(ctxt):
|
||||||
|
return ctxt
|
||||||
|
return {}
|
||||||
|
13
hooks/charmhelpers/contrib/openstack/templates/git.upstart
Normal file
13
hooks/charmhelpers/contrib/openstack/templates/git.upstart
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
description "{{ service_description }}"
|
||||||
|
author "Juju {{ service_name }} Charm <juju@localhost>"
|
||||||
|
|
||||||
|
start on runlevel [2345]
|
||||||
|
stop on runlevel [!2345]
|
||||||
|
|
||||||
|
respawn
|
||||||
|
|
||||||
|
exec start-stop-daemon --start --chuid {{ user_name }} \
|
||||||
|
--chdir {{ start_dir }} --name {{ process_name }} \
|
||||||
|
--exec {{ executable_name }} -- \
|
||||||
|
--config-file={{ config_file }} \
|
||||||
|
--log-file={{ log_file }}
|
@ -0,0 +1,9 @@
|
|||||||
|
{% if auth_host -%}
|
||||||
|
[keystone_authtoken]
|
||||||
|
identity_uri = {{ auth_protocol }}://{{ auth_host }}:{{ auth_port }}/{{ auth_admin_prefix }}
|
||||||
|
auth_uri = {{ service_protocol }}://{{ service_host }}:{{ service_port }}/{{ service_admin_prefix }}
|
||||||
|
admin_tenant_name = {{ admin_tenant_name }}
|
||||||
|
admin_user = {{ admin_user }}
|
||||||
|
admin_password = {{ admin_password }}
|
||||||
|
signing_dir = {{ signing_dir }}
|
||||||
|
{% endif -%}
|
@ -0,0 +1,22 @@
|
|||||||
|
{% if rabbitmq_host or rabbitmq_hosts -%}
|
||||||
|
[oslo_messaging_rabbit]
|
||||||
|
rabbit_userid = {{ rabbitmq_user }}
|
||||||
|
rabbit_virtual_host = {{ rabbitmq_virtual_host }}
|
||||||
|
rabbit_password = {{ rabbitmq_password }}
|
||||||
|
{% if rabbitmq_hosts -%}
|
||||||
|
rabbit_hosts = {{ rabbitmq_hosts }}
|
||||||
|
{% if rabbitmq_ha_queues -%}
|
||||||
|
rabbit_ha_queues = True
|
||||||
|
rabbit_durable_queues = False
|
||||||
|
{% endif -%}
|
||||||
|
{% else -%}
|
||||||
|
rabbit_host = {{ rabbitmq_host }}
|
||||||
|
{% endif -%}
|
||||||
|
{% if rabbit_ssl_port -%}
|
||||||
|
rabbit_use_ssl = True
|
||||||
|
rabbit_port = {{ rabbit_ssl_port }}
|
||||||
|
{% if rabbit_ssl_ca -%}
|
||||||
|
kombu_ssl_ca_certs = {{ rabbit_ssl_ca }}
|
||||||
|
{% endif -%}
|
||||||
|
{% endif -%}
|
||||||
|
{% endif -%}
|
@ -3,12 +3,12 @@
|
|||||||
rpc_backend = zmq
|
rpc_backend = zmq
|
||||||
rpc_zmq_host = {{ zmq_host }}
|
rpc_zmq_host = {{ zmq_host }}
|
||||||
{% if zmq_redis_address -%}
|
{% if zmq_redis_address -%}
|
||||||
rpc_zmq_matchmaker = oslo.messaging._drivers.matchmaker_redis.MatchMakerRedis
|
rpc_zmq_matchmaker = redis
|
||||||
matchmaker_heartbeat_freq = 15
|
matchmaker_heartbeat_freq = 15
|
||||||
matchmaker_heartbeat_ttl = 30
|
matchmaker_heartbeat_ttl = 30
|
||||||
[matchmaker_redis]
|
[matchmaker_redis]
|
||||||
host = {{ zmq_redis_address }}
|
host = {{ zmq_redis_address }}
|
||||||
{% else -%}
|
{% else -%}
|
||||||
rpc_zmq_matchmaker = oslo.messaging._drivers.matchmaker_ring.MatchMakerRing
|
rpc_zmq_matchmaker = ring
|
||||||
{% endif -%}
|
{% endif -%}
|
||||||
{% endif -%}
|
{% endif -%}
|
@ -30,6 +30,10 @@ import yaml
|
|||||||
|
|
||||||
from charmhelpers.contrib.network import ip
|
from charmhelpers.contrib.network import ip
|
||||||
|
|
||||||
|
from charmhelpers.core import (
|
||||||
|
unitdata,
|
||||||
|
)
|
||||||
|
|
||||||
from charmhelpers.core.hookenv import (
|
from charmhelpers.core.hookenv import (
|
||||||
config,
|
config,
|
||||||
log as juju_log,
|
log as juju_log,
|
||||||
@ -330,6 +334,21 @@ def configure_installation_source(rel):
|
|||||||
error_out("Invalid openstack-release specified: %s" % rel)
|
error_out("Invalid openstack-release specified: %s" % rel)
|
||||||
|
|
||||||
|
|
||||||
|
def config_value_changed(option):
|
||||||
|
"""
|
||||||
|
Determine if config value changed since last call to this function.
|
||||||
|
"""
|
||||||
|
hook_data = unitdata.HookData()
|
||||||
|
with hook_data():
|
||||||
|
db = unitdata.kv()
|
||||||
|
current = config(option)
|
||||||
|
saved = db.get(option)
|
||||||
|
db.set(option, current)
|
||||||
|
if saved is None:
|
||||||
|
return False
|
||||||
|
return current != saved
|
||||||
|
|
||||||
|
|
||||||
def save_script_rc(script_path="scripts/scriptrc", **env_vars):
|
def save_script_rc(script_path="scripts/scriptrc", **env_vars):
|
||||||
"""
|
"""
|
||||||
Write an rc file in the charm-delivered directory containing
|
Write an rc file in the charm-delivered directory containing
|
||||||
@ -469,82 +488,103 @@ def os_requires_version(ostack_release, pkg):
|
|||||||
|
|
||||||
|
|
||||||
def git_install_requested():
|
def git_install_requested():
|
||||||
"""Returns true if openstack-origin-git is specified."""
|
"""
|
||||||
return config('openstack-origin-git') != "None"
|
Returns true if openstack-origin-git is specified.
|
||||||
|
"""
|
||||||
|
return config('openstack-origin-git') is not None
|
||||||
|
|
||||||
|
|
||||||
requirements_dir = None
|
requirements_dir = None
|
||||||
|
|
||||||
|
|
||||||
def git_clone_and_install(file_name, core_project):
|
def git_clone_and_install(projects_yaml, core_project):
|
||||||
"""Clone/install all OpenStack repos specified in yaml config file."""
|
"""
|
||||||
global requirements_dir
|
Clone/install all specified OpenStack repositories.
|
||||||
|
|
||||||
if file_name == "None":
|
The expected format of projects_yaml is:
|
||||||
|
repositories:
|
||||||
|
- {name: keystone,
|
||||||
|
repository: 'git://git.openstack.org/openstack/keystone.git',
|
||||||
|
branch: 'stable/icehouse'}
|
||||||
|
- {name: requirements,
|
||||||
|
repository: 'git://git.openstack.org/openstack/requirements.git',
|
||||||
|
branch: 'stable/icehouse'}
|
||||||
|
directory: /mnt/openstack-git
|
||||||
|
http_proxy: http://squid.internal:3128
|
||||||
|
https_proxy: https://squid.internal:3128
|
||||||
|
|
||||||
|
The directory, http_proxy, and https_proxy keys are optional.
|
||||||
|
"""
|
||||||
|
global requirements_dir
|
||||||
|
parent_dir = '/mnt/openstack-git'
|
||||||
|
|
||||||
|
if not projects_yaml:
|
||||||
return
|
return
|
||||||
|
|
||||||
yaml_file = os.path.join(charm_dir(), file_name)
|
projects = yaml.load(projects_yaml)
|
||||||
|
_git_validate_projects_yaml(projects, core_project)
|
||||||
|
|
||||||
# clone/install the requirements project first
|
if 'http_proxy' in projects.keys():
|
||||||
installed = _git_clone_and_install_subset(yaml_file,
|
os.environ['http_proxy'] = projects['http_proxy']
|
||||||
whitelist=['requirements'])
|
|
||||||
if 'requirements' not in installed:
|
|
||||||
error_out('requirements git repository must be specified')
|
|
||||||
|
|
||||||
# clone/install all other projects except requirements and the core project
|
if 'https_proxy' in projects.keys():
|
||||||
blacklist = ['requirements', core_project]
|
os.environ['https_proxy'] = projects['https_proxy']
|
||||||
_git_clone_and_install_subset(yaml_file, blacklist=blacklist,
|
|
||||||
update_requirements=True)
|
|
||||||
|
|
||||||
# clone/install the core project
|
if 'directory' in projects.keys():
|
||||||
whitelist = [core_project]
|
parent_dir = projects['directory']
|
||||||
installed = _git_clone_and_install_subset(yaml_file, whitelist=whitelist,
|
|
||||||
update_requirements=True)
|
|
||||||
if core_project not in installed:
|
|
||||||
error_out('{} git repository must be specified'.format(core_project))
|
|
||||||
|
|
||||||
|
for p in projects['repositories']:
|
||||||
def _git_clone_and_install_subset(yaml_file, whitelist=[], blacklist=[],
|
repo = p['repository']
|
||||||
update_requirements=False):
|
branch = p['branch']
|
||||||
"""Clone/install subset of OpenStack repos specified in yaml config file."""
|
if p['name'] == 'requirements':
|
||||||
global requirements_dir
|
repo_dir = _git_clone_and_install_single(repo, branch, parent_dir,
|
||||||
installed = []
|
update_requirements=False)
|
||||||
|
|
||||||
with open(yaml_file, 'r') as fd:
|
|
||||||
projects = yaml.load(fd)
|
|
||||||
for proj, val in projects.items():
|
|
||||||
# The project subset is chosen based on the following 3 rules:
|
|
||||||
# 1) If project is in blacklist, we don't clone/install it, period.
|
|
||||||
# 2) If whitelist is empty, we clone/install everything else.
|
|
||||||
# 3) If whitelist is not empty, we clone/install everything in the
|
|
||||||
# whitelist.
|
|
||||||
if proj in blacklist:
|
|
||||||
continue
|
|
||||||
if whitelist and proj not in whitelist:
|
|
||||||
continue
|
|
||||||
repo = val['repository']
|
|
||||||
branch = val['branch']
|
|
||||||
repo_dir = _git_clone_and_install_single(repo, branch,
|
|
||||||
update_requirements)
|
|
||||||
if proj == 'requirements':
|
|
||||||
requirements_dir = repo_dir
|
requirements_dir = repo_dir
|
||||||
installed.append(proj)
|
else:
|
||||||
return installed
|
repo_dir = _git_clone_and_install_single(repo, branch, parent_dir,
|
||||||
|
update_requirements=True)
|
||||||
|
|
||||||
|
|
||||||
def _git_clone_and_install_single(repo, branch, update_requirements=False):
|
def _git_validate_projects_yaml(projects, core_project):
|
||||||
"""Clone and install a single git repository."""
|
"""
|
||||||
dest_parent_dir = "/mnt/openstack-git/"
|
Validate the projects yaml.
|
||||||
dest_dir = os.path.join(dest_parent_dir, os.path.basename(repo))
|
"""
|
||||||
|
_git_ensure_key_exists('repositories', projects)
|
||||||
|
|
||||||
if not os.path.exists(dest_parent_dir):
|
for project in projects['repositories']:
|
||||||
juju_log('Host dir not mounted at {}. '
|
_git_ensure_key_exists('name', project.keys())
|
||||||
'Creating directory there instead.'.format(dest_parent_dir))
|
_git_ensure_key_exists('repository', project.keys())
|
||||||
os.mkdir(dest_parent_dir)
|
_git_ensure_key_exists('branch', project.keys())
|
||||||
|
|
||||||
|
if projects['repositories'][0]['name'] != 'requirements':
|
||||||
|
error_out('{} git repo must be specified first'.format('requirements'))
|
||||||
|
|
||||||
|
if projects['repositories'][-1]['name'] != core_project:
|
||||||
|
error_out('{} git repo must be specified last'.format(core_project))
|
||||||
|
|
||||||
|
|
||||||
|
def _git_ensure_key_exists(key, keys):
|
||||||
|
"""
|
||||||
|
Ensure that key exists in keys.
|
||||||
|
"""
|
||||||
|
if key not in keys:
|
||||||
|
error_out('openstack-origin-git key \'{}\' is missing'.format(key))
|
||||||
|
|
||||||
|
|
||||||
|
def _git_clone_and_install_single(repo, branch, parent_dir, update_requirements):
|
||||||
|
"""
|
||||||
|
Clone and install a single git repository.
|
||||||
|
"""
|
||||||
|
dest_dir = os.path.join(parent_dir, os.path.basename(repo))
|
||||||
|
|
||||||
|
if not os.path.exists(parent_dir):
|
||||||
|
juju_log('Directory already exists at {}. '
|
||||||
|
'No need to create directory.'.format(parent_dir))
|
||||||
|
os.mkdir(parent_dir)
|
||||||
|
|
||||||
if not os.path.exists(dest_dir):
|
if not os.path.exists(dest_dir):
|
||||||
juju_log('Cloning git repo: {}, branch: {}'.format(repo, branch))
|
juju_log('Cloning git repo: {}, branch: {}'.format(repo, branch))
|
||||||
repo_dir = install_remote(repo, dest=dest_parent_dir, branch=branch)
|
repo_dir = install_remote(repo, dest=parent_dir, branch=branch)
|
||||||
else:
|
else:
|
||||||
repo_dir = dest_dir
|
repo_dir = dest_dir
|
||||||
|
|
||||||
@ -561,16 +601,39 @@ def _git_clone_and_install_single(repo, branch, update_requirements=False):
|
|||||||
|
|
||||||
|
|
||||||
def _git_update_requirements(package_dir, reqs_dir):
|
def _git_update_requirements(package_dir, reqs_dir):
|
||||||
"""Update from global requirements.
|
"""
|
||||||
|
Update from global requirements.
|
||||||
|
|
||||||
Update an OpenStack git directory's requirements.txt and
|
Update an OpenStack git directory's requirements.txt and
|
||||||
test-requirements.txt from global-requirements.txt."""
|
test-requirements.txt from global-requirements.txt.
|
||||||
|
"""
|
||||||
orig_dir = os.getcwd()
|
orig_dir = os.getcwd()
|
||||||
os.chdir(reqs_dir)
|
os.chdir(reqs_dir)
|
||||||
cmd = "python update.py {}".format(package_dir)
|
cmd = ['python', 'update.py', package_dir]
|
||||||
try:
|
try:
|
||||||
subprocess.check_call(cmd.split(' '))
|
subprocess.check_call(cmd)
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
package = os.path.basename(package_dir)
|
package = os.path.basename(package_dir)
|
||||||
error_out("Error updating {} from global-requirements.txt".format(package))
|
error_out("Error updating {} from global-requirements.txt".format(package))
|
||||||
os.chdir(orig_dir)
|
os.chdir(orig_dir)
|
||||||
|
|
||||||
|
|
||||||
|
def git_src_dir(projects_yaml, project):
|
||||||
|
"""
|
||||||
|
Return the directory where the specified project's source is located.
|
||||||
|
"""
|
||||||
|
parent_dir = '/mnt/openstack-git'
|
||||||
|
|
||||||
|
if not projects_yaml:
|
||||||
|
return
|
||||||
|
|
||||||
|
projects = yaml.load(projects_yaml)
|
||||||
|
|
||||||
|
if 'directory' in projects.keys():
|
||||||
|
parent_dir = projects['directory']
|
||||||
|
|
||||||
|
for p in projects['repositories']:
|
||||||
|
if p['name'] == project:
|
||||||
|
return os.path.join(parent_dir, os.path.basename(p['repository']))
|
||||||
|
|
||||||
|
return None
|
||||||
|
@ -443,7 +443,7 @@ class HookData(object):
|
|||||||
data = hookenv.execution_environment()
|
data = hookenv.execution_environment()
|
||||||
self.conf = conf_delta = self.kv.delta(data['conf'], 'config')
|
self.conf = conf_delta = self.kv.delta(data['conf'], 'config')
|
||||||
self.rels = rels_delta = self.kv.delta(data['rels'], 'rels')
|
self.rels = rels_delta = self.kv.delta(data['rels'], 'rels')
|
||||||
self.kv.set('env', data['env'])
|
self.kv.set('env', dict(data['env']))
|
||||||
self.kv.set('unit', data['unit'])
|
self.kv.set('unit', data['unit'])
|
||||||
self.kv.set('relid', data.get('relid'))
|
self.kv.set('relid', data.get('relid'))
|
||||||
return conf_delta, rels_delta
|
return conf_delta, rels_delta
|
||||||
|
@ -1,80 +1,23 @@
|
|||||||
|
import os
|
||||||
|
import uuid
|
||||||
from charmhelpers.core.hookenv import (
|
from charmhelpers.core.hookenv import (
|
||||||
|
config,
|
||||||
|
relation_get,
|
||||||
relation_ids,
|
relation_ids,
|
||||||
related_units,
|
related_units,
|
||||||
relation_get,
|
|
||||||
config,
|
|
||||||
unit_get,
|
unit_get,
|
||||||
)
|
)
|
||||||
from charmhelpers.core.strutils import bool_from_string
|
from charmhelpers.contrib.openstack.ip import resolve_address
|
||||||
from charmhelpers.contrib.openstack import context
|
from charmhelpers.contrib.openstack import context
|
||||||
from charmhelpers.core.host import (
|
|
||||||
service_running,
|
|
||||||
service_start,
|
|
||||||
service_restart,
|
|
||||||
)
|
|
||||||
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
|
||||||
from charmhelpers.contrib.network.ip import get_address_in_network
|
from charmhelpers.contrib.network.ip import get_address_in_network
|
||||||
|
from charmhelpers.contrib.openstack.context import (
|
||||||
|
OSContextGenerator,
|
||||||
|
NeutronAPIContext,
|
||||||
|
)
|
||||||
from charmhelpers.contrib.openstack.neutron import (
|
from charmhelpers.contrib.openstack.neutron import (
|
||||||
parse_bridge_mappings,
|
|
||||||
parse_data_port_mappings,
|
|
||||||
parse_vlan_range_mappings,
|
parse_vlan_range_mappings,
|
||||||
)
|
)
|
||||||
from charmhelpers.core.host import (
|
|
||||||
get_nic_hwaddr,
|
|
||||||
)
|
|
||||||
OVS_BRIDGE = 'br-int'
|
|
||||||
|
|
||||||
|
|
||||||
def _neutron_api_settings():
|
|
||||||
'''
|
|
||||||
Inspects current neutron-plugin relation
|
|
||||||
'''
|
|
||||||
neutron_settings = {
|
|
||||||
'neutron_security_groups': False,
|
|
||||||
'l2_population': True,
|
|
||||||
'overlay_network_type': 'gre',
|
|
||||||
}
|
|
||||||
|
|
||||||
for rid in relation_ids('neutron-plugin-api'):
|
|
||||||
for unit in related_units(rid):
|
|
||||||
rdata = relation_get(rid=rid, unit=unit)
|
|
||||||
if 'l2-population' in rdata:
|
|
||||||
neutron_settings.update({
|
|
||||||
'l2_population': bool_from_string(rdata['l2-population']),
|
|
||||||
'overlay_network_type': rdata['overlay-network-type'],
|
|
||||||
'neutron_security_groups':
|
|
||||||
bool_from_string(rdata['neutron-security-groups'])
|
|
||||||
})
|
|
||||||
|
|
||||||
# Override with configuration if set to true
|
|
||||||
if config('disable-security-groups'):
|
|
||||||
neutron_settings['neutron_security_groups'] = False
|
|
||||||
|
|
||||||
net_dev_mtu = rdata.get('network-device-mtu')
|
|
||||||
if net_dev_mtu:
|
|
||||||
neutron_settings['network_device_mtu'] = net_dev_mtu
|
|
||||||
|
|
||||||
return neutron_settings
|
|
||||||
|
|
||||||
|
|
||||||
class DataPortContext(context.NeutronPortContext):
|
|
||||||
|
|
||||||
def __call__(self):
|
|
||||||
ports = config('data-port')
|
|
||||||
if ports:
|
|
||||||
portmap = parse_data_port_mappings(ports)
|
|
||||||
ports = portmap.values()
|
|
||||||
resolved = self.resolve_ports(ports)
|
|
||||||
normalized = {get_nic_hwaddr(port): port for port in resolved
|
|
||||||
if port not in ports}
|
|
||||||
normalized.update({port: port for port in resolved
|
|
||||||
if port in ports})
|
|
||||||
if resolved:
|
|
||||||
return {bridge: normalized[port] for bridge, port in
|
|
||||||
portmap.iteritems() if port in normalized.keys()}
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
class OVSPluginContext(context.NeutronContext):
|
class OVSPluginContext(context.NeutronContext):
|
||||||
@ -90,27 +33,11 @@ class OVSPluginContext(context.NeutronContext):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def neutron_security_groups(self):
|
def neutron_security_groups(self):
|
||||||
neutron_api_settings = _neutron_api_settings()
|
if config('disable-security-groups'):
|
||||||
|
return False
|
||||||
|
neutron_api_settings = NeutronAPIContext()()
|
||||||
return neutron_api_settings['neutron_security_groups']
|
return neutron_api_settings['neutron_security_groups']
|
||||||
|
|
||||||
def _ensure_bridge(self):
|
|
||||||
if not service_running('openvswitch-switch'):
|
|
||||||
service_start('openvswitch-switch')
|
|
||||||
|
|
||||||
add_bridge(OVS_BRIDGE)
|
|
||||||
|
|
||||||
portmaps = DataPortContext()()
|
|
||||||
bridgemaps = parse_bridge_mappings(config('bridge-mappings'))
|
|
||||||
for provider, br in bridgemaps.iteritems():
|
|
||||||
add_bridge(br)
|
|
||||||
|
|
||||||
if not portmaps or br not in portmaps:
|
|
||||||
continue
|
|
||||||
|
|
||||||
add_bridge_port(br, portmaps[br], promisc=True)
|
|
||||||
|
|
||||||
service_restart('os-charm-phy-nic-mtu')
|
|
||||||
|
|
||||||
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
|
||||||
# is running and the OVS bridge exists. Also need to ensure
|
# is running and the OVS bridge exists. Also need to ensure
|
||||||
@ -119,15 +46,14 @@ class OVSPluginContext(context.NeutronContext):
|
|||||||
if not ovs_ctxt:
|
if not ovs_ctxt:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
self._ensure_bridge()
|
|
||||||
|
|
||||||
conf = config()
|
conf = config()
|
||||||
ovs_ctxt['local_ip'] = \
|
ovs_ctxt['local_ip'] = \
|
||||||
get_address_in_network(config('os-data-network'),
|
get_address_in_network(config('os-data-network'),
|
||||||
get_host_ip(unit_get('private-address')))
|
get_host_ip(unit_get('private-address')))
|
||||||
neutron_api_settings = _neutron_api_settings()
|
neutron_api_settings = NeutronAPIContext()()
|
||||||
ovs_ctxt['neutron_security_groups'] = self.neutron_security_groups
|
ovs_ctxt['neutron_security_groups'] = self.neutron_security_groups
|
||||||
ovs_ctxt['l2_population'] = neutron_api_settings['l2_population']
|
ovs_ctxt['l2_population'] = neutron_api_settings['l2_population']
|
||||||
|
ovs_ctxt['distributed_routing'] = neutron_api_settings['enable_dvr']
|
||||||
ovs_ctxt['overlay_network_type'] = \
|
ovs_ctxt['overlay_network_type'] = \
|
||||||
neutron_api_settings['overlay_network_type']
|
neutron_api_settings['overlay_network_type']
|
||||||
# TODO: We need to sort out the syslog and debug/verbose options as a
|
# TODO: We need to sort out the syslog and debug/verbose options as a
|
||||||
@ -157,18 +83,60 @@ class OVSPluginContext(context.NeutronContext):
|
|||||||
return ovs_ctxt
|
return ovs_ctxt
|
||||||
|
|
||||||
|
|
||||||
class PhyNICMTUContext(DataPortContext):
|
class L3AgentContext(OSContextGenerator):
|
||||||
"""Context used to apply settings to neutron data-port devices"""
|
|
||||||
|
|
||||||
def __call__(self):
|
def __call__(self):
|
||||||
|
neutron_api_settings = NeutronAPIContext()()
|
||||||
ctxt = {}
|
ctxt = {}
|
||||||
mappings = super(PhyNICMTUContext, self).__call__()
|
if neutron_api_settings['enable_dvr']:
|
||||||
if mappings and mappings.values():
|
ctxt['agent_mode'] = 'dvr'
|
||||||
ports = mappings.values()
|
else:
|
||||||
neutron_api_settings = _neutron_api_settings()
|
ctxt['agent_mode'] = 'legacy'
|
||||||
mtu = neutron_api_settings.get('network_device_mtu')
|
return ctxt
|
||||||
if mtu:
|
|
||||||
ctxt['devs'] = '\\n'.join(ports)
|
|
||||||
ctxt['mtu'] = mtu
|
SHARED_SECRET = "/etc/neutron/secret.txt"
|
||||||
|
|
||||||
|
|
||||||
|
def get_shared_secret():
|
||||||
|
secret = None
|
||||||
|
if not os.path.exists(SHARED_SECRET):
|
||||||
|
secret = str(uuid.uuid4())
|
||||||
|
with open(SHARED_SECRET, 'w') as secret_file:
|
||||||
|
secret_file.write(secret)
|
||||||
|
else:
|
||||||
|
with open(SHARED_SECRET, 'r') as secret_file:
|
||||||
|
secret = secret_file.read().strip()
|
||||||
|
return secret
|
||||||
|
|
||||||
|
|
||||||
|
class DVRSharedSecretContext(OSContextGenerator):
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
if NeutronAPIContext()()['enable_dvr']:
|
||||||
|
ctxt = {
|
||||||
|
'shared_secret': get_shared_secret(),
|
||||||
|
'local_ip': resolve_address(),
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
ctxt = {}
|
||||||
|
return ctxt
|
||||||
|
|
||||||
|
|
||||||
|
class APIIdentityServiceContext(context.IdentityServiceContext):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(APIIdentityServiceContext,
|
||||||
|
self).__init__(rel_name='neutron-plugin-api')
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
ctxt = super(APIIdentityServiceContext, self).__call__()
|
||||||
|
if not ctxt:
|
||||||
|
return
|
||||||
|
for rid in relation_ids('neutron-plugin-api'):
|
||||||
|
for unit in related_units(rid):
|
||||||
|
rdata = relation_get(rid=rid, unit=unit)
|
||||||
|
ctxt['region'] = rdata.get('region')
|
||||||
|
if ctxt['region']:
|
||||||
|
return ctxt
|
||||||
return ctxt
|
return ctxt
|
||||||
|
@ -2,12 +2,18 @@
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from charmhelpers.contrib.openstack.utils import (
|
||||||
|
config_value_changed,
|
||||||
|
git_install_requested,
|
||||||
|
)
|
||||||
|
|
||||||
from charmhelpers.core.hookenv import (
|
from charmhelpers.core.hookenv import (
|
||||||
Hooks,
|
Hooks,
|
||||||
UnregisteredHookError,
|
UnregisteredHookError,
|
||||||
config,
|
config,
|
||||||
log,
|
log,
|
||||||
relation_set,
|
relation_set,
|
||||||
|
relation_ids,
|
||||||
)
|
)
|
||||||
|
|
||||||
from charmhelpers.core.host import (
|
from charmhelpers.core.host import (
|
||||||
@ -15,13 +21,24 @@ from charmhelpers.core.host import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from charmhelpers.fetch import (
|
from charmhelpers.fetch import (
|
||||||
apt_install, apt_update
|
apt_install, apt_update, apt_purge
|
||||||
|
)
|
||||||
|
|
||||||
|
from charmhelpers.contrib.openstack.utils import (
|
||||||
|
os_requires_version,
|
||||||
)
|
)
|
||||||
|
|
||||||
from neutron_ovs_utils import (
|
from neutron_ovs_utils import (
|
||||||
|
DVR_PACKAGES,
|
||||||
|
configure_ovs,
|
||||||
determine_packages,
|
determine_packages,
|
||||||
|
git_install,
|
||||||
|
get_topics,
|
||||||
|
determine_dvr_packages,
|
||||||
|
get_shared_secret,
|
||||||
register_configs,
|
register_configs,
|
||||||
restart_map,
|
restart_map,
|
||||||
|
use_dvr,
|
||||||
)
|
)
|
||||||
|
|
||||||
hooks = Hooks()
|
hooks = Hooks()
|
||||||
@ -35,13 +52,49 @@ def install():
|
|||||||
for pkg in pkgs:
|
for pkg in pkgs:
|
||||||
apt_install(pkg, fatal=True)
|
apt_install(pkg, fatal=True)
|
||||||
|
|
||||||
|
git_install(config('openstack-origin-git'))
|
||||||
|
|
||||||
|
|
||||||
@hooks.hook('neutron-plugin-relation-changed')
|
@hooks.hook('neutron-plugin-relation-changed')
|
||||||
@hooks.hook('neutron-plugin-api-relation-changed')
|
|
||||||
@hooks.hook('config-changed')
|
@hooks.hook('config-changed')
|
||||||
@restart_on_change(restart_map())
|
@restart_on_change(restart_map())
|
||||||
def config_changed():
|
def config_changed():
|
||||||
|
if determine_dvr_packages():
|
||||||
|
apt_update()
|
||||||
|
apt_install(determine_dvr_packages(), fatal=True)
|
||||||
|
|
||||||
|
if git_install_requested():
|
||||||
|
if config_value_changed('openstack-origin-git'):
|
||||||
|
git_install(config('openstack-origin-git'))
|
||||||
|
|
||||||
|
configure_ovs()
|
||||||
CONFIGS.write_all()
|
CONFIGS.write_all()
|
||||||
|
for rid in relation_ids('zeromq-configuration'):
|
||||||
|
zeromq_configuration_relation_joined(rid)
|
||||||
|
|
||||||
|
|
||||||
|
@hooks.hook('neutron-plugin-api-relation-changed')
|
||||||
|
@restart_on_change(restart_map())
|
||||||
|
def neutron_plugin_api_changed():
|
||||||
|
if use_dvr():
|
||||||
|
apt_update()
|
||||||
|
apt_install(DVR_PACKAGES, fatal=True)
|
||||||
|
else:
|
||||||
|
apt_purge(DVR_PACKAGES, fatal=True)
|
||||||
|
configure_ovs()
|
||||||
|
CONFIGS.write_all()
|
||||||
|
# If dvr setting has changed, need to pass that on
|
||||||
|
for rid in relation_ids('neutron-plugin'):
|
||||||
|
neutron_plugin_joined(relation_id=rid)
|
||||||
|
|
||||||
|
|
||||||
|
@hooks.hook('neutron-plugin-relation-joined')
|
||||||
|
def neutron_plugin_joined(relation_id=None):
|
||||||
|
secret = get_shared_secret() if use_dvr() else None
|
||||||
|
rel_data = {
|
||||||
|
'metadata-shared-secret': secret,
|
||||||
|
}
|
||||||
|
relation_set(relation_id=relation_id, **rel_data)
|
||||||
|
|
||||||
|
|
||||||
@hooks.hook('amqp-relation-joined')
|
@hooks.hook('amqp-relation-joined')
|
||||||
@ -61,6 +114,20 @@ def amqp_changed():
|
|||||||
CONFIGS.write_all()
|
CONFIGS.write_all()
|
||||||
|
|
||||||
|
|
||||||
|
@hooks.hook('zeromq-configuration-relation-joined')
|
||||||
|
@os_requires_version('kilo', 'neutron-common')
|
||||||
|
def zeromq_configuration_relation_joined(relid=None):
|
||||||
|
relation_set(relation_id=relid,
|
||||||
|
topics=" ".join(get_topics()),
|
||||||
|
users="neutron")
|
||||||
|
|
||||||
|
|
||||||
|
@hooks.hook('zeromq-configuration-relation-changed')
|
||||||
|
@restart_on_change(restart_map(), stopstart=True)
|
||||||
|
def zeromq_configuration_relation_changed():
|
||||||
|
CONFIGS.write_all()
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
try:
|
try:
|
||||||
hooks.execute(sys.argv)
|
hooks.execute(sys.argv)
|
||||||
|
@ -1,18 +1,76 @@
|
|||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
|
||||||
from charmhelpers.contrib.openstack.neutron import neutron_plugin_attribute
|
from charmhelpers.contrib.openstack.neutron import neutron_plugin_attribute
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
from charmhelpers.contrib.openstack import context, templating
|
from charmhelpers.contrib.openstack import context, templating
|
||||||
|
from charmhelpers.contrib.openstack.utils import (
|
||||||
|
git_install_requested,
|
||||||
|
git_clone_and_install,
|
||||||
|
git_src_dir,
|
||||||
|
)
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from charmhelpers.contrib.openstack.utils import (
|
from charmhelpers.contrib.openstack.utils import (
|
||||||
os_release,
|
os_release,
|
||||||
)
|
)
|
||||||
import neutron_ovs_context
|
import neutron_ovs_context
|
||||||
|
from charmhelpers.contrib.network.ovs import (
|
||||||
|
add_bridge,
|
||||||
|
add_bridge_port,
|
||||||
|
full_restart,
|
||||||
|
)
|
||||||
|
from charmhelpers.core.hookenv import (
|
||||||
|
config,
|
||||||
|
)
|
||||||
|
from charmhelpers.contrib.openstack.neutron import (
|
||||||
|
parse_bridge_mappings,
|
||||||
|
)
|
||||||
|
from charmhelpers.contrib.openstack.context import (
|
||||||
|
ExternalPortContext,
|
||||||
|
DataPortContext,
|
||||||
|
)
|
||||||
|
from charmhelpers.core.host import (
|
||||||
|
adduser,
|
||||||
|
add_group,
|
||||||
|
add_user_to_group,
|
||||||
|
mkdir,
|
||||||
|
service_restart,
|
||||||
|
service_running,
|
||||||
|
write_file,
|
||||||
|
)
|
||||||
|
|
||||||
|
from charmhelpers.core.templating import render
|
||||||
|
|
||||||
|
BASE_GIT_PACKAGES = [
|
||||||
|
'libxml2-dev',
|
||||||
|
'libxslt1-dev',
|
||||||
|
'openvswitch-switch',
|
||||||
|
'python-dev',
|
||||||
|
'python-pip',
|
||||||
|
'python-setuptools',
|
||||||
|
'zlib1g-dev',
|
||||||
|
]
|
||||||
|
|
||||||
|
# ubuntu packages that should not be installed when deploying from git
|
||||||
|
GIT_PACKAGE_BLACKLIST = [
|
||||||
|
'neutron-l3-agent',
|
||||||
|
'neutron-metadata-agent',
|
||||||
|
'neutron-server',
|
||||||
|
'neutron-plugin-openvswitch',
|
||||||
|
'neutron-plugin-openvswitch-agent',
|
||||||
|
]
|
||||||
|
|
||||||
NOVA_CONF_DIR = "/etc/nova"
|
NOVA_CONF_DIR = "/etc/nova"
|
||||||
NEUTRON_CONF_DIR = "/etc/neutron"
|
NEUTRON_CONF_DIR = "/etc/neutron"
|
||||||
NEUTRON_CONF = '%s/neutron.conf' % NEUTRON_CONF_DIR
|
NEUTRON_CONF = '%s/neutron.conf' % NEUTRON_CONF_DIR
|
||||||
NEUTRON_DEFAULT = '/etc/default/neutron-server'
|
NEUTRON_DEFAULT = '/etc/default/neutron-server'
|
||||||
|
NEUTRON_L3_AGENT_CONF = "/etc/neutron/l3_agent.ini"
|
||||||
|
NEUTRON_FWAAS_CONF = "/etc/neutron/fwaas_driver.ini"
|
||||||
ML2_CONF = '%s/plugins/ml2/ml2_conf.ini' % NEUTRON_CONF_DIR
|
ML2_CONF = '%s/plugins/ml2/ml2_conf.ini' % NEUTRON_CONF_DIR
|
||||||
|
EXT_PORT_CONF = '/etc/init/ext-port.conf'
|
||||||
|
NEUTRON_METADATA_AGENT_CONF = "/etc/neutron/metadata_agent.ini"
|
||||||
|
DVR_PACKAGES = ['neutron-l3-agent']
|
||||||
PHY_NIC_MTU_CONF = '/etc/init/os-charm-phy-nic-mtu.conf'
|
PHY_NIC_MTU_CONF = '/etc/init/os-charm-phy-nic-mtu.conf'
|
||||||
TEMPLATES = 'templates/'
|
TEMPLATES = 'templates/'
|
||||||
|
|
||||||
@ -20,7 +78,9 @@ BASE_RESOURCE_MAP = OrderedDict([
|
|||||||
(NEUTRON_CONF, {
|
(NEUTRON_CONF, {
|
||||||
'services': ['neutron-plugin-openvswitch-agent'],
|
'services': ['neutron-plugin-openvswitch-agent'],
|
||||||
'contexts': [neutron_ovs_context.OVSPluginContext(),
|
'contexts': [neutron_ovs_context.OVSPluginContext(),
|
||||||
context.AMQPContext(ssl_dir=NEUTRON_CONF_DIR)],
|
context.AMQPContext(ssl_dir=NEUTRON_CONF_DIR),
|
||||||
|
context.ZeroMQContext(),
|
||||||
|
context.NotificationDriverContext()],
|
||||||
}),
|
}),
|
||||||
(ML2_CONF, {
|
(ML2_CONF, {
|
||||||
'services': ['neutron-plugin-openvswitch-agent'],
|
'services': ['neutron-plugin-openvswitch-agent'],
|
||||||
@ -28,13 +88,53 @@ BASE_RESOURCE_MAP = OrderedDict([
|
|||||||
}),
|
}),
|
||||||
(PHY_NIC_MTU_CONF, {
|
(PHY_NIC_MTU_CONF, {
|
||||||
'services': ['os-charm-phy-nic-mtu'],
|
'services': ['os-charm-phy-nic-mtu'],
|
||||||
'contexts': [neutron_ovs_context.PhyNICMTUContext()],
|
'contexts': [context.PhyNICMTUContext()],
|
||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
|
DVR_RESOURCE_MAP = OrderedDict([
|
||||||
|
(NEUTRON_L3_AGENT_CONF, {
|
||||||
|
'services': ['neutron-l3-agent'],
|
||||||
|
'contexts': [neutron_ovs_context.L3AgentContext()],
|
||||||
|
}),
|
||||||
|
(NEUTRON_FWAAS_CONF, {
|
||||||
|
'services': ['neutron-l3-agent'],
|
||||||
|
'contexts': [neutron_ovs_context.L3AgentContext()],
|
||||||
|
}),
|
||||||
|
(EXT_PORT_CONF, {
|
||||||
|
'services': ['neutron-l3-agent'],
|
||||||
|
'contexts': [context.ExternalPortContext()],
|
||||||
|
}),
|
||||||
|
(NEUTRON_METADATA_AGENT_CONF, {
|
||||||
|
'services': ['neutron-metadata-agent'],
|
||||||
|
'contexts': [neutron_ovs_context.DVRSharedSecretContext(),
|
||||||
|
neutron_ovs_context.APIIdentityServiceContext()],
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
TEMPLATES = 'templates/'
|
||||||
|
INT_BRIDGE = "br-int"
|
||||||
|
EXT_BRIDGE = "br-ex"
|
||||||
|
DATA_BRIDGE = 'br-data'
|
||||||
|
|
||||||
|
|
||||||
|
def determine_dvr_packages():
|
||||||
|
if not git_install_requested():
|
||||||
|
if use_dvr():
|
||||||
|
return DVR_PACKAGES
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
def determine_packages():
|
def determine_packages():
|
||||||
return neutron_plugin_attribute('ovs', 'packages', 'neutron')
|
pkgs = neutron_plugin_attribute('ovs', 'packages', 'neutron')
|
||||||
|
pkgs.extend(determine_dvr_packages())
|
||||||
|
|
||||||
|
if git_install_requested():
|
||||||
|
pkgs.extend(BASE_GIT_PACKAGES)
|
||||||
|
# don't include packages that will be installed from git
|
||||||
|
for p in GIT_PACKAGE_BLACKLIST:
|
||||||
|
if p in pkgs:
|
||||||
|
pkgs.remove(p)
|
||||||
|
|
||||||
|
return pkgs
|
||||||
|
|
||||||
|
|
||||||
def register_configs(release=None):
|
def register_configs(release=None):
|
||||||
@ -52,6 +152,10 @@ def resource_map():
|
|||||||
hook execution.
|
hook execution.
|
||||||
'''
|
'''
|
||||||
resource_map = deepcopy(BASE_RESOURCE_MAP)
|
resource_map = deepcopy(BASE_RESOURCE_MAP)
|
||||||
|
if use_dvr():
|
||||||
|
resource_map.update(DVR_RESOURCE_MAP)
|
||||||
|
dvr_services = ['neutron-metadata-agent', 'neutron-l3-agent']
|
||||||
|
resource_map[NEUTRON_CONF]['services'] += dvr_services
|
||||||
return resource_map
|
return resource_map
|
||||||
|
|
||||||
|
|
||||||
@ -61,3 +165,134 @@ def restart_map():
|
|||||||
state.
|
state.
|
||||||
'''
|
'''
|
||||||
return {k: v['services'] for k, v in resource_map().iteritems()}
|
return {k: v['services'] for k, v in resource_map().iteritems()}
|
||||||
|
|
||||||
|
|
||||||
|
def get_topics():
|
||||||
|
topics = []
|
||||||
|
topics.append('q-agent-notifier-port-update')
|
||||||
|
topics.append('q-agent-notifier-network-delete')
|
||||||
|
topics.append('q-agent-notifier-tunnel-update')
|
||||||
|
topics.append('q-agent-notifier-security_group-update')
|
||||||
|
topics.append('q-agent-notifier-dvr-update')
|
||||||
|
if context.NeutronAPIContext()()['l2_population']:
|
||||||
|
topics.append('q-agent-notifier-l2population-update')
|
||||||
|
return topics
|
||||||
|
|
||||||
|
|
||||||
|
def configure_ovs():
|
||||||
|
if not service_running('openvswitch-switch'):
|
||||||
|
full_restart()
|
||||||
|
add_bridge(INT_BRIDGE)
|
||||||
|
add_bridge(EXT_BRIDGE)
|
||||||
|
ext_port_ctx = None
|
||||||
|
if use_dvr():
|
||||||
|
ext_port_ctx = ExternalPortContext()()
|
||||||
|
if ext_port_ctx and ext_port_ctx['ext_port']:
|
||||||
|
add_bridge_port(EXT_BRIDGE, ext_port_ctx['ext_port'])
|
||||||
|
|
||||||
|
portmaps = DataPortContext()()
|
||||||
|
bridgemaps = parse_bridge_mappings(config('bridge-mappings'))
|
||||||
|
for provider, br in bridgemaps.iteritems():
|
||||||
|
add_bridge(br)
|
||||||
|
if not portmaps or br not in portmaps:
|
||||||
|
continue
|
||||||
|
|
||||||
|
add_bridge_port(br, portmaps[br], promisc=True)
|
||||||
|
|
||||||
|
# Ensure this runs so that mtu is applied to data-port interfaces if
|
||||||
|
# provided.
|
||||||
|
service_restart('os-charm-phy-nic-mtu')
|
||||||
|
|
||||||
|
|
||||||
|
def get_shared_secret():
|
||||||
|
ctxt = neutron_ovs_context.DVRSharedSecretContext()()
|
||||||
|
if 'shared_secret' in ctxt:
|
||||||
|
return ctxt['shared_secret']
|
||||||
|
|
||||||
|
|
||||||
|
def use_dvr():
|
||||||
|
return context.NeutronAPIContext()()['enable_dvr']
|
||||||
|
|
||||||
|
|
||||||
|
def git_install(projects_yaml):
|
||||||
|
"""Perform setup, and install git repos specified in yaml parameter."""
|
||||||
|
if git_install_requested():
|
||||||
|
git_pre_install()
|
||||||
|
git_clone_and_install(projects_yaml, core_project='neutron')
|
||||||
|
git_post_install(projects_yaml)
|
||||||
|
|
||||||
|
|
||||||
|
def git_pre_install():
|
||||||
|
"""Perform pre-install setup."""
|
||||||
|
dirs = [
|
||||||
|
'/etc/neutron',
|
||||||
|
'/etc/neutron/rootwrap.d',
|
||||||
|
'/var/lib/neutron',
|
||||||
|
'/var/lib/neutron/lock',
|
||||||
|
'/var/log/neutron',
|
||||||
|
]
|
||||||
|
|
||||||
|
logs = [
|
||||||
|
'/var/log/neutron/server.log',
|
||||||
|
]
|
||||||
|
|
||||||
|
adduser('neutron', shell='/bin/bash', system_user=True)
|
||||||
|
add_group('neutron', system_group=True)
|
||||||
|
add_user_to_group('neutron', 'neutron')
|
||||||
|
|
||||||
|
for d in dirs:
|
||||||
|
mkdir(d, owner='neutron', group='neutron', perms=0700, force=False)
|
||||||
|
|
||||||
|
for l in logs:
|
||||||
|
write_file(l, '', owner='neutron', group='neutron', perms=0600)
|
||||||
|
|
||||||
|
|
||||||
|
def git_post_install(projects_yaml):
|
||||||
|
"""Perform post-install setup."""
|
||||||
|
src_etc = os.path.join(git_src_dir(projects_yaml, 'neutron'), 'etc')
|
||||||
|
configs = {
|
||||||
|
'debug-filters': {
|
||||||
|
'src': os.path.join(src_etc, 'neutron/rootwrap.d/debug.filters'),
|
||||||
|
'dest': '/etc/neutron/rootwrap.d/debug.filters',
|
||||||
|
},
|
||||||
|
'policy': {
|
||||||
|
'src': os.path.join(src_etc, 'policy.json'),
|
||||||
|
'dest': '/etc/neutron/policy.json',
|
||||||
|
},
|
||||||
|
'rootwrap': {
|
||||||
|
'src': os.path.join(src_etc, 'rootwrap.conf'),
|
||||||
|
'dest': '/etc/neutron/rootwrap.conf',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for conf, files in configs.iteritems():
|
||||||
|
shutil.copyfile(files['src'], files['dest'])
|
||||||
|
|
||||||
|
render('neutron_sudoers', '/etc/sudoers.d/neutron_sudoers', {},
|
||||||
|
perms=0o440)
|
||||||
|
|
||||||
|
neutron_ovs_agent_context = {
|
||||||
|
'service_description': 'Neutron OpenvSwitch Plugin Agent',
|
||||||
|
'charm_name': 'neutron-openvswitch',
|
||||||
|
'process_name': 'neutron-openvswitch-agent',
|
||||||
|
'cleanup_process_name': 'neutron-ovs-cleanup',
|
||||||
|
'plugin_config': '/etc/neutron/plugins/ml2/ml2_conf.ini',
|
||||||
|
'log_file': '/var/log/neutron/openvswitch-agent.log',
|
||||||
|
}
|
||||||
|
|
||||||
|
neutron_ovs_cleanup_context = {
|
||||||
|
'service_description': 'Neutron OpenvSwitch Cleanup',
|
||||||
|
'charm_name': 'neutron-openvswitch',
|
||||||
|
'process_name': 'neutron-ovs-cleanup',
|
||||||
|
'log_file': '/var/log/neutron/ovs-cleanup.log',
|
||||||
|
}
|
||||||
|
|
||||||
|
# NOTE(coreycb): Needs systemd support
|
||||||
|
render('upstart/neutron-plugin-openvswitch-agent.upstart',
|
||||||
|
'/etc/init/neutron-plugin-openvswitch-agent.conf',
|
||||||
|
neutron_ovs_agent_context, perms=0o644)
|
||||||
|
render('upstart/neutron-ovs-cleanup.upstart',
|
||||||
|
'/etc/init/neutron-ovs-cleanup.conf',
|
||||||
|
neutron_ovs_cleanup_context, perms=0o644)
|
||||||
|
|
||||||
|
service_restart('neutron-plugin-openvswitch-agent')
|
||||||
|
1
hooks/zeromq-configuration-relation-changed
Symbolic link
1
hooks/zeromq-configuration-relation-changed
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
neutron_ovs_hooks.py
|
1
hooks/zeromq-configuration-relation-joined
Symbolic link
1
hooks/zeromq-configuration-relation-joined
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
neutron_ovs_hooks.py
|
@ -28,3 +28,7 @@ requires:
|
|||||||
scope: container
|
scope: container
|
||||||
neutron-plugin-api:
|
neutron-plugin-api:
|
||||||
interface: neutron-plugin-api
|
interface: neutron-plugin-api
|
||||||
|
zeromq-configuration:
|
||||||
|
interface: zeromq-configuration
|
||||||
|
scope: container
|
||||||
|
|
||||||
|
16
templates/ext-port.conf
Normal file
16
templates/ext-port.conf
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
description "Enabling Neutron external networking port"
|
||||||
|
|
||||||
|
start on runlevel [2345]
|
||||||
|
|
||||||
|
task
|
||||||
|
|
||||||
|
script
|
||||||
|
EXT_PORT="{{ ext_port }}"
|
||||||
|
MTU="{{ ext_port_mtu }}"
|
||||||
|
if [ -n "$EXT_PORT" ]; then
|
||||||
|
ip link set $EXT_PORT up
|
||||||
|
if [ -n "$MTU" ]; then
|
||||||
|
ip link set $EXT_PORT mtu $MTU
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
end script
|
@ -12,15 +12,18 @@ state_path = /var/lib/neutron
|
|||||||
lock_path = $state_path/lock
|
lock_path = $state_path/lock
|
||||||
bind_host = 0.0.0.0
|
bind_host = 0.0.0.0
|
||||||
bind_port = 9696
|
bind_port = 9696
|
||||||
|
{% if network_device_mtu -%}
|
||||||
network_device_mtu = {{ network_device_mtu }}
|
network_device_mtu = {{ network_device_mtu }}
|
||||||
|
{% endif -%}
|
||||||
{% if core_plugin -%}
|
{% if core_plugin -%}
|
||||||
core_plugin = {{ core_plugin }}
|
core_plugin = {{ core_plugin }}
|
||||||
{% endif -%}
|
{% endif -%}
|
||||||
|
|
||||||
api_paste_config = /etc/neutron/api-paste.ini
|
api_paste_config = /etc/neutron/api-paste.ini
|
||||||
auth_strategy = keystone
|
auth_strategy = keystone
|
||||||
|
{% if notifications == 'True' -%}
|
||||||
notification_driver = neutron.openstack.common.notifier.rpc_notifier
|
notification_driver = neutron.openstack.common.notifier.rpc_notifier
|
||||||
|
{% endif -%}
|
||||||
default_notification_level = INFO
|
default_notification_level = INFO
|
||||||
notification_topics = notifications
|
notification_topics = notifications
|
||||||
|
|
||||||
@ -35,4 +38,3 @@ root_helper = sudo neutron-rootwrap /etc/neutron/rootwrap.conf
|
|||||||
|
|
||||||
[keystone_authtoken]
|
[keystone_authtoken]
|
||||||
signing_dir = /var/lib/neutron/keystone-signing
|
signing_dir = /var/lib/neutron/keystone-signing
|
||||||
|
|
||||||
|
7
templates/juno/fwaas_driver.ini
Normal file
7
templates/juno/fwaas_driver.ini
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
###############################################################################
|
||||||
|
# [ WARNING ]
|
||||||
|
# Configuration file maintained by Juju. Local changes may be overwritten.
|
||||||
|
###############################################################################
|
||||||
|
[fwaas]
|
||||||
|
driver = neutron.services.firewall.drivers.linux.iptables_fwaas.IptablesFwaasDriver
|
||||||
|
enabled = True
|
7
templates/juno/l3_agent.ini
Normal file
7
templates/juno/l3_agent.ini
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
###############################################################################
|
||||||
|
# [ WARNING ]
|
||||||
|
# Configuration file maintained by Juju. Local changes may be overwritten.
|
||||||
|
###############################################################################
|
||||||
|
[DEFAULT]
|
||||||
|
interface_driver = neutron.agent.linux.interface.OVSInterfaceDriver
|
||||||
|
agent_mode = {{ agent_mode }}
|
20
templates/juno/metadata_agent.ini
Normal file
20
templates/juno/metadata_agent.ini
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
###############################################################################
|
||||||
|
# [ WARNING ]
|
||||||
|
# Configuration file maintained by Juju. Local changes may be overwritten.
|
||||||
|
###############################################################################
|
||||||
|
# Metadata service seems to cache neutron api url from keystone so trigger
|
||||||
|
# restart if it changes: {{ quantum_url }}
|
||||||
|
|
||||||
|
[DEFAULT]
|
||||||
|
auth_url = {{ service_protocol }}://{{ service_host }}:{{ service_port }}/v2.0
|
||||||
|
auth_region = {{ region }}
|
||||||
|
admin_tenant_name = {{ admin_tenant_name }}
|
||||||
|
admin_user = {{ admin_user }}
|
||||||
|
admin_password = {{ admin_password }}
|
||||||
|
root_helper = sudo neutron-rootwrap /etc/neutron/rootwrap.conf
|
||||||
|
state_path = /var/lib/neutron
|
||||||
|
# Gateway runs a metadata API server locally
|
||||||
|
#nova_metadata_ip = {{ local_ip }}
|
||||||
|
nova_metadata_port = 8775
|
||||||
|
metadata_proxy_shared_secret = {{ shared_secret }}
|
||||||
|
cache_url = memory://?default_ttl=5
|
43
templates/juno/ml2_conf.ini
Normal file
43
templates/juno/ml2_conf.ini
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# juno
|
||||||
|
###############################################################################
|
||||||
|
# [ WARNING ]
|
||||||
|
# Configuration file maintained by Juju. Local changes may be overwritten.
|
||||||
|
# Config managed by neutron-openvswitch charm
|
||||||
|
###############################################################################
|
||||||
|
[ml2]
|
||||||
|
type_drivers = gre,vxlan,vlan,flat
|
||||||
|
tenant_network_types = gre,vxlan,vlan,flat
|
||||||
|
mechanism_drivers = openvswitch,hyperv,l2population
|
||||||
|
|
||||||
|
[ml2_type_gre]
|
||||||
|
tunnel_id_ranges = 1:1000
|
||||||
|
|
||||||
|
[ml2_type_vxlan]
|
||||||
|
vni_ranges = 1001:2000
|
||||||
|
|
||||||
|
[ml2_type_vlan]
|
||||||
|
network_vlan_ranges = {{ vlan_ranges }}
|
||||||
|
|
||||||
|
[ml2_type_flat]
|
||||||
|
flat_networks = {{ network_providers }}
|
||||||
|
|
||||||
|
[ovs]
|
||||||
|
enable_tunneling = True
|
||||||
|
local_ip = {{ local_ip }}
|
||||||
|
bridge_mappings = {{ bridge_mappings }}
|
||||||
|
|
||||||
|
[agent]
|
||||||
|
tunnel_types = {{ overlay_network_type }}
|
||||||
|
l2_population = {{ l2_population }}
|
||||||
|
enable_distributed_routing = {{ distributed_routing }}
|
||||||
|
{% if veth_mtu -%}
|
||||||
|
veth_mtu = {{ veth_mtu }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
[securitygroup]
|
||||||
|
{% if neutron_security_groups -%}
|
||||||
|
enable_security_group = True
|
||||||
|
firewall_driver = neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver
|
||||||
|
{% else -%}
|
||||||
|
enable_security_group = False
|
||||||
|
{% endif -%}
|
8
templates/kilo/fwaas_driver.ini
Normal file
8
templates/kilo/fwaas_driver.ini
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# kilo
|
||||||
|
###############################################################################
|
||||||
|
# [ WARNING ]
|
||||||
|
# Configuration file maintained by Juju. Local changes may be overwritten.
|
||||||
|
###############################################################################
|
||||||
|
[fwaas]
|
||||||
|
driver = neutron_fwaas.services.firewall.drivers.linux.iptables_fwaas.IptablesFwaasDriver
|
||||||
|
enabled = True
|
42
templates/kilo/neutron.conf
Normal file
42
templates/kilo/neutron.conf
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# icehouse
|
||||||
|
###############################################################################
|
||||||
|
# [ WARNING ]
|
||||||
|
# Configuration file maintained by Juju. Local changes may be overwritten.
|
||||||
|
# Config managed by neutron-openvswitch charm
|
||||||
|
###############################################################################
|
||||||
|
[DEFAULT]
|
||||||
|
verbose = {{ verbose }}
|
||||||
|
debug = {{ debug }}
|
||||||
|
use_syslog = {{ use_syslog }}
|
||||||
|
state_path = /var/lib/neutron
|
||||||
|
bind_host = 0.0.0.0
|
||||||
|
bind_port = 9696
|
||||||
|
{% if network_device_mtu -%}
|
||||||
|
network_device_mtu = {{ network_device_mtu }}
|
||||||
|
{% endif -%}
|
||||||
|
{% if core_plugin -%}
|
||||||
|
core_plugin = {{ core_plugin }}
|
||||||
|
{% endif -%}
|
||||||
|
|
||||||
|
api_paste_config = /etc/neutron/api-paste.ini
|
||||||
|
auth_strategy = keystone
|
||||||
|
notification_driver = neutron.openstack.common.notifier.rpc_notifier
|
||||||
|
default_notification_level = INFO
|
||||||
|
notification_topics = notifications
|
||||||
|
|
||||||
|
{% include "section-zeromq" %}
|
||||||
|
|
||||||
|
{% include "section-rabbitmq-oslo" %}
|
||||||
|
|
||||||
|
[QUOTAS]
|
||||||
|
|
||||||
|
[DEFAULT_SERVICETYPE]
|
||||||
|
|
||||||
|
[AGENT]
|
||||||
|
root_helper = sudo neutron-rootwrap /etc/neutron/rootwrap.conf
|
||||||
|
|
||||||
|
[keystone_authtoken]
|
||||||
|
signing_dir = /var/lib/neutron/keystone-signing
|
||||||
|
|
||||||
|
[oslo_concurrency]
|
||||||
|
lock_path = $state_path/lock
|
4
templates/neutron_sudoers
Normal file
4
templates/neutron_sudoers
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
Defaults:neutron !requiretty
|
||||||
|
|
||||||
|
neutron ALL = (root) NOPASSWD: /usr/local/bin/neutron-rootwrap /etc/neutron/rootwrap.conf *
|
||||||
|
|
17
templates/upstart/neutron-ovs-cleanup.upstart
Normal file
17
templates/upstart/neutron-ovs-cleanup.upstart
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
description "{{ service_description }}"
|
||||||
|
author "Juju {{ charm_name }} Charm <juju@localhost>"
|
||||||
|
|
||||||
|
start on started openvswitch-switch
|
||||||
|
stop on runlevel [!2345]
|
||||||
|
|
||||||
|
pre-start script
|
||||||
|
mkdir -p /var/run/neutron
|
||||||
|
chown neutron:root /var/run/neutron
|
||||||
|
end script
|
||||||
|
|
||||||
|
pre-start script
|
||||||
|
[ ! -x /usr/bin/{{ process_name }} ] && exit 0
|
||||||
|
start-stop-daemon --start --chuid neutron --exec /usr/local/bin/{{ process_name }} -- \
|
||||||
|
--log-file /var/log/neutron/{{ log_file }} \
|
||||||
|
--config-file /etc/neutron/neutron.conf --verbose
|
||||||
|
end script
|
18
templates/upstart/neutron-plugin-openvswitch-agent.upstart
Normal file
18
templates/upstart/neutron-plugin-openvswitch-agent.upstart
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
description "{{ service_description }}"
|
||||||
|
author "Juju {{ charm_name }} Charm <juju@localhost>"
|
||||||
|
|
||||||
|
start on runlevel [2345] and started {{ cleanup_process_name}}
|
||||||
|
stop on runlevel [!2345]
|
||||||
|
|
||||||
|
respawn
|
||||||
|
|
||||||
|
chdir /var/run
|
||||||
|
|
||||||
|
pre-start script
|
||||||
|
mkdir -p /var/run/neutron
|
||||||
|
chown neutron:root /var/run/neutron
|
||||||
|
end script
|
||||||
|
|
||||||
|
exec start-stop-daemon --start --chuid neutron --exec /usr/local/bin/{{ process_name }} -- \
|
||||||
|
--config-file=/etc/neutron/neutron.conf --config-file={{ plugin_config }} \
|
||||||
|
--log-file={{ log_file }}
|
9
tests/16-basic-trusty-icehouse-git
Executable file
9
tests/16-basic-trusty-icehouse-git
Executable file
@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
"""Amulet tests on a basic neutron-openvswitch git deployment on trusty-icehouse."""
|
||||||
|
|
||||||
|
from basic_deployment import NeutronOVSBasicDeployment
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
deployment = NeutronOVSBasicDeployment(series='trusty', git=True)
|
||||||
|
deployment.run_tests()
|
12
tests/18-basic-trusty-juno-git
Executable file
12
tests/18-basic-trusty-juno-git
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
"""Amulet tests on a basic neutron-openvswitch git deployment on trusty-juno."""
|
||||||
|
|
||||||
|
from basic_deployment import NeutronOVSBasicDeployment
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
deployment = NeutronOVSBasicDeployment(series='trusty',
|
||||||
|
openstack='cloud:trusty-juno',
|
||||||
|
source='cloud:trusty-updates/juno',
|
||||||
|
git=True)
|
||||||
|
deployment.run_tests()
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import amulet
|
import amulet
|
||||||
import time
|
import time
|
||||||
|
import yaml
|
||||||
|
|
||||||
from charmhelpers.contrib.openstack.amulet.deployment import (
|
from charmhelpers.contrib.openstack.amulet.deployment import (
|
||||||
OpenStackAmuletDeployment
|
OpenStackAmuletDeployment
|
||||||
@ -24,10 +25,12 @@ u = OpenStackAmuletUtils(ERROR)
|
|||||||
class NeutronOVSBasicDeployment(OpenStackAmuletDeployment):
|
class NeutronOVSBasicDeployment(OpenStackAmuletDeployment):
|
||||||
"""Amulet tests on a basic neutron-openvswtich deployment."""
|
"""Amulet tests on a basic neutron-openvswtich deployment."""
|
||||||
|
|
||||||
def __init__(self, series, openstack=None, source=None, stable=False):
|
def __init__(self, series, openstack=None, source=None, git=False,
|
||||||
|
stable=False):
|
||||||
"""Deploy the entire test environment."""
|
"""Deploy the entire test environment."""
|
||||||
super(NeutronOVSBasicDeployment, self).__init__(series, openstack,
|
super(NeutronOVSBasicDeployment, self).__init__(series, openstack,
|
||||||
source, stable)
|
source, stable)
|
||||||
|
self.git = git
|
||||||
self._add_services()
|
self._add_services()
|
||||||
self._add_relations()
|
self._add_relations()
|
||||||
self._configure_services()
|
self._configure_services()
|
||||||
@ -61,7 +64,24 @@ class NeutronOVSBasicDeployment(OpenStackAmuletDeployment):
|
|||||||
|
|
||||||
def _configure_services(self):
|
def _configure_services(self):
|
||||||
"""Configure all of the services."""
|
"""Configure all of the services."""
|
||||||
configs = {}
|
neutron_ovs_config = {}
|
||||||
|
if self.git:
|
||||||
|
branch = 'stable/' + self._get_openstack_release_string()
|
||||||
|
openstack_origin_git = {
|
||||||
|
'repositories': [
|
||||||
|
{'name': 'requirements',
|
||||||
|
'repository': 'git://git.openstack.org/openstack/requirements',
|
||||||
|
'branch': branch},
|
||||||
|
{'name': 'neutron',
|
||||||
|
'repository': 'git://git.openstack.org/openstack/neutron',
|
||||||
|
'branch': branch},
|
||||||
|
],
|
||||||
|
'directory': '/mnt/openstack-git',
|
||||||
|
'http_proxy': 'http://squid.internal:3128',
|
||||||
|
'https_proxy': 'https://squid.internal:3128',
|
||||||
|
}
|
||||||
|
neutron_ovs_config['openstack-origin-git'] = yaml.dump(openstack_origin_git)
|
||||||
|
configs = {'neutron-openvswitch': neutron_ovs_config}
|
||||||
super(NeutronOVSBasicDeployment, self)._configure_services(configs)
|
super(NeutronOVSBasicDeployment, self)._configure_services(configs)
|
||||||
|
|
||||||
def _initialize_tests(self):
|
def _initialize_tests(self):
|
||||||
@ -76,7 +96,8 @@ class NeutronOVSBasicDeployment(OpenStackAmuletDeployment):
|
|||||||
service units."""
|
service units."""
|
||||||
|
|
||||||
commands = {
|
commands = {
|
||||||
self.compute_sentry: ['status nova-compute'],
|
self.compute_sentry: ['status nova-compute',
|
||||||
|
'status neutron-plugin-openvswitch-agent'],
|
||||||
self.rabbitmq_sentry: ['service rabbitmq-server status'],
|
self.rabbitmq_sentry: ['service rabbitmq-server status'],
|
||||||
self.neutron_api_sentry: ['status neutron-server'],
|
self.neutron_api_sentry: ['status neutron-server'],
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
|
# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
from collections import OrderedDict
|
||||||
from charmhelpers.contrib.amulet.deployment import (
|
from charmhelpers.contrib.amulet.deployment import (
|
||||||
AmuletDeployment
|
AmuletDeployment
|
||||||
)
|
)
|
||||||
@ -100,12 +101,34 @@ class OpenStackAmuletDeployment(AmuletDeployment):
|
|||||||
"""
|
"""
|
||||||
(self.precise_essex, self.precise_folsom, self.precise_grizzly,
|
(self.precise_essex, self.precise_folsom, self.precise_grizzly,
|
||||||
self.precise_havana, self.precise_icehouse,
|
self.precise_havana, self.precise_icehouse,
|
||||||
self.trusty_icehouse) = range(6)
|
self.trusty_icehouse, self.trusty_juno, self.trusty_kilo) = range(8)
|
||||||
releases = {
|
releases = {
|
||||||
('precise', None): self.precise_essex,
|
('precise', None): self.precise_essex,
|
||||||
('precise', 'cloud:precise-folsom'): self.precise_folsom,
|
('precise', 'cloud:precise-folsom'): self.precise_folsom,
|
||||||
('precise', 'cloud:precise-grizzly'): self.precise_grizzly,
|
('precise', 'cloud:precise-grizzly'): self.precise_grizzly,
|
||||||
('precise', 'cloud:precise-havana'): self.precise_havana,
|
('precise', 'cloud:precise-havana'): self.precise_havana,
|
||||||
('precise', 'cloud:precise-icehouse'): self.precise_icehouse,
|
('precise', 'cloud:precise-icehouse'): self.precise_icehouse,
|
||||||
('trusty', None): self.trusty_icehouse}
|
('trusty', None): self.trusty_icehouse,
|
||||||
|
('trusty', 'cloud:trusty-juno'): self.trusty_juno,
|
||||||
|
('trusty', 'cloud:trusty-kilo'): self.trusty_kilo}
|
||||||
return releases[(self.series, self.openstack)]
|
return releases[(self.series, self.openstack)]
|
||||||
|
|
||||||
|
def _get_openstack_release_string(self):
|
||||||
|
"""Get openstack release string.
|
||||||
|
|
||||||
|
Return a string representing the openstack release.
|
||||||
|
"""
|
||||||
|
releases = OrderedDict([
|
||||||
|
('precise', 'essex'),
|
||||||
|
('quantal', 'folsom'),
|
||||||
|
('raring', 'grizzly'),
|
||||||
|
('saucy', 'havana'),
|
||||||
|
('trusty', 'icehouse'),
|
||||||
|
('utopic', 'juno'),
|
||||||
|
('vivid', 'kilo'),
|
||||||
|
])
|
||||||
|
if self.openstack:
|
||||||
|
os_origin = self.openstack.split(':')[1]
|
||||||
|
return os_origin.split('%s-' % self.series)[1].split('/')[0]
|
||||||
|
else:
|
||||||
|
return releases[self.series]
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
sys.path.append('actions/')
|
||||||
sys.path.append('hooks/')
|
sys.path.append('hooks/')
|
||||||
|
85
unit_tests/test_actions_git_reinstall.py
Normal file
85
unit_tests/test_actions_git_reinstall.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
from mock import patch
|
||||||
|
|
||||||
|
with patch('charmhelpers.core.hookenv.config') as config:
|
||||||
|
config.return_value = 'neutron'
|
||||||
|
import neutron_ovs_utils as utils # noqa
|
||||||
|
|
||||||
|
import git_reinstall
|
||||||
|
|
||||||
|
from test_utils import (
|
||||||
|
CharmTestCase
|
||||||
|
)
|
||||||
|
|
||||||
|
TO_PATCH = [
|
||||||
|
'config',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
openstack_origin_git = \
|
||||||
|
"""repositories:
|
||||||
|
- {name: requirements,
|
||||||
|
repository: 'git://git.openstack.org/openstack/requirements',
|
||||||
|
branch: stable/juno}
|
||||||
|
- {name: neutron,
|
||||||
|
repository: 'git://git.openstack.org/openstack/neutron',
|
||||||
|
branch: stable/juno}"""
|
||||||
|
|
||||||
|
|
||||||
|
class TestNeutronOVSActions(CharmTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestNeutronOVSActions, self).setUp(git_reinstall, TO_PATCH)
|
||||||
|
self.config.side_effect = self.test_config.get
|
||||||
|
|
||||||
|
@patch.object(git_reinstall, 'action_set')
|
||||||
|
@patch.object(git_reinstall, 'action_fail')
|
||||||
|
@patch.object(git_reinstall, 'git_install')
|
||||||
|
def test_git_reinstall(self, git_install, action_fail, action_set):
|
||||||
|
self.test_config.set('openstack-origin-git', openstack_origin_git)
|
||||||
|
|
||||||
|
git_reinstall.git_reinstall()
|
||||||
|
|
||||||
|
git_install.assert_called_with(openstack_origin_git)
|
||||||
|
self.assertTrue(git_install.called)
|
||||||
|
self.assertFalse(action_set.called)
|
||||||
|
self.assertFalse(action_fail.called)
|
||||||
|
|
||||||
|
@patch.object(git_reinstall, 'action_set')
|
||||||
|
@patch.object(git_reinstall, 'action_fail')
|
||||||
|
@patch.object(git_reinstall, 'git_install')
|
||||||
|
@patch('charmhelpers.contrib.openstack.utils.config')
|
||||||
|
def test_git_reinstall_not_configured(self, _config, git_install,
|
||||||
|
action_fail, action_set):
|
||||||
|
_config.return_value = None
|
||||||
|
|
||||||
|
git_reinstall.git_reinstall()
|
||||||
|
|
||||||
|
msg = 'openstack-origin-git is not configured'
|
||||||
|
action_fail.assert_called_with(msg)
|
||||||
|
self.assertFalse(git_install.called)
|
||||||
|
self.assertFalse(action_set.called)
|
||||||
|
|
||||||
|
@patch.object(git_reinstall, 'action_set')
|
||||||
|
@patch.object(git_reinstall, 'action_fail')
|
||||||
|
@patch.object(git_reinstall, 'git_install')
|
||||||
|
@patch('charmhelpers.contrib.openstack.utils.config')
|
||||||
|
def test_git_reinstall_exception(self, _config, git_install,
|
||||||
|
action_fail, action_set):
|
||||||
|
_config.return_value = openstack_origin_git
|
||||||
|
e = OSError('something bad happened')
|
||||||
|
git_install.side_effect = e
|
||||||
|
traceback = (
|
||||||
|
"Traceback (most recent call last):\n"
|
||||||
|
" File \"actions/git_reinstall.py\", line 33, in git_reinstall\n"
|
||||||
|
" git_install(config(\'openstack-origin-git\'))\n"
|
||||||
|
" File \"/usr/lib/python2.7/dist-packages/mock.py\", line 964, in __call__\n" # noqa
|
||||||
|
" return _mock_self._mock_call(*args, **kwargs)\n"
|
||||||
|
" File \"/usr/lib/python2.7/dist-packages/mock.py\", line 1019, in _mock_call\n" # noqa
|
||||||
|
" raise effect\n"
|
||||||
|
"OSError: something bad happened\n")
|
||||||
|
|
||||||
|
git_reinstall.git_reinstall()
|
||||||
|
|
||||||
|
msg = 'git-reinstall resulted in an unexpected error'
|
||||||
|
action_fail.assert_called_with(msg)
|
||||||
|
action_set.assert_called_with({'traceback': traceback})
|
@ -1,27 +1,30 @@
|
|||||||
|
|
||||||
from test_utils import CharmTestCase
|
from test_utils import CharmTestCase
|
||||||
|
from test_utils import patch_open
|
||||||
from mock import patch
|
from mock import patch
|
||||||
import neutron_ovs_context as context
|
import neutron_ovs_context as context
|
||||||
import charmhelpers
|
import charmhelpers
|
||||||
|
|
||||||
TO_PATCH = [
|
TO_PATCH = [
|
||||||
'relation_get',
|
'resolve_address',
|
||||||
'relation_ids',
|
|
||||||
'related_units',
|
|
||||||
'config',
|
'config',
|
||||||
'unit_get',
|
'unit_get',
|
||||||
'add_bridge',
|
|
||||||
'add_bridge_port',
|
|
||||||
'service_running',
|
|
||||||
'service_start',
|
|
||||||
'get_host_ip',
|
'get_host_ip',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def fake_context(settings):
|
||||||
|
def outer():
|
||||||
|
def inner():
|
||||||
|
return settings
|
||||||
|
return inner
|
||||||
|
return outer
|
||||||
|
|
||||||
|
|
||||||
class OVSPluginContextTest(CharmTestCase):
|
class OVSPluginContextTest(CharmTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(OVSPluginContextTest, self).setUp(context, TO_PATCH)
|
super(OVSPluginContextTest, self).setUp(context, TO_PATCH)
|
||||||
self.relation_get.side_effect = self.test_relation.get
|
|
||||||
self.config.side_effect = self.test_config.get
|
self.config.side_effect = self.test_config.get
|
||||||
self.test_config.set('debug', True)
|
self.test_config.set('debug', True)
|
||||||
self.test_config.set('verbose', True)
|
self.test_config.set('verbose', True)
|
||||||
@ -30,49 +33,41 @@ class OVSPluginContextTest(CharmTestCase):
|
|||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
super(OVSPluginContextTest, self).tearDown()
|
super(OVSPluginContextTest, self).tearDown()
|
||||||
|
|
||||||
|
@patch('charmhelpers.contrib.openstack.context.config')
|
||||||
@patch('charmhelpers.contrib.openstack.context.NeutronPortContext.'
|
@patch('charmhelpers.contrib.openstack.context.NeutronPortContext.'
|
||||||
'resolve_ports')
|
'resolve_ports')
|
||||||
def test_data_port_name(self, mock_resolve_ports):
|
def test_data_port_name(self, mock_resolve_ports, config):
|
||||||
self.test_config.set('data-port', 'br-data:em1')
|
self.test_config.set('data-port', 'br-data:em1')
|
||||||
|
config.side_effect = self.test_config.get
|
||||||
mock_resolve_ports.side_effect = lambda ports: ports
|
mock_resolve_ports.side_effect = lambda ports: ports
|
||||||
self.assertEquals(context.DataPortContext()(),
|
self.assertEquals(
|
||||||
{'br-data': 'em1'})
|
charmhelpers.contrib.openstack.context.DataPortContext()(),
|
||||||
|
{'br-data': 'em1'}
|
||||||
|
)
|
||||||
|
|
||||||
@patch.object(context, 'get_nic_hwaddr')
|
@patch('charmhelpers.contrib.openstack.context.config')
|
||||||
@patch('charmhelpers.contrib.openstack.context.get_nic_hwaddr')
|
@patch('charmhelpers.contrib.openstack.context.get_nic_hwaddr')
|
||||||
@patch('charmhelpers.contrib.openstack.context.list_nics')
|
@patch('charmhelpers.contrib.openstack.context.list_nics')
|
||||||
def test_data_port_mac(self, list_nics, get_nic_hwaddr, get_nic_hwaddr2):
|
def test_data_port_mac(self, list_nics, get_nic_hwaddr, config):
|
||||||
machine_machs = {
|
machine_machs = {
|
||||||
'em1': 'aa:aa:aa:aa:aa:aa',
|
'em1': 'aa:aa:aa:aa:aa:aa',
|
||||||
'eth0': 'bb:bb:bb:bb:bb:bb',
|
'eth0': 'bb:bb:bb:bb:bb:bb',
|
||||||
}
|
}
|
||||||
get_nic_hwaddr2.side_effect = lambda nic: machine_machs[nic]
|
|
||||||
absent_mac = "cc:cc:cc:cc:cc:cc"
|
absent_mac = "cc:cc:cc:cc:cc:cc"
|
||||||
config_macs = ("br-d1:%s br-d2:%s" %
|
config_macs = ("br-d1:%s br-d2:%s" %
|
||||||
(absent_mac, machine_machs['em1']))
|
(absent_mac, machine_machs['em1']))
|
||||||
self.test_config.set('data-port', config_macs)
|
self.test_config.set('data-port', config_macs)
|
||||||
|
config.side_effect = self.test_config.get
|
||||||
list_nics.return_value = machine_machs.keys()
|
list_nics.return_value = machine_machs.keys()
|
||||||
get_nic_hwaddr.side_effect = lambda nic: machine_machs[nic]
|
get_nic_hwaddr.side_effect = lambda nic: machine_machs[nic]
|
||||||
self.assertEquals(context.DataPortContext()(),
|
self.assertEquals(
|
||||||
{'br-d2': 'em1'})
|
charmhelpers.contrib.openstack.context.DataPortContext()(),
|
||||||
|
{'br-d2': 'em1'}
|
||||||
@patch('charmhelpers.contrib.openstack.context.NeutronPortContext.'
|
)
|
||||||
'resolve_ports')
|
|
||||||
def test_ensure_bridge_data_port_present(self, mock_resolve_ports):
|
|
||||||
self.test_config.set('data-port', 'br-data:em1')
|
|
||||||
self.test_config.set('bridge-mappings', 'phybr1:br-data')
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
mock_resolve_ports.side_effect = lambda ports: ports
|
|
||||||
self.add_bridge_port.side_effect = add_port
|
|
||||||
context.OVSPluginContext()._ensure_bridge()
|
|
||||||
self.assertEquals(self.bridge_added, True)
|
|
||||||
|
|
||||||
|
@patch.object(charmhelpers.contrib.openstack.context, 'relation_get')
|
||||||
|
@patch.object(charmhelpers.contrib.openstack.context, 'relation_ids')
|
||||||
|
@patch.object(charmhelpers.contrib.openstack.context, 'related_units')
|
||||||
@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')
|
||||||
@ -84,7 +79,7 @@ class OVSPluginContextTest(CharmTestCase):
|
|||||||
@patch.object(charmhelpers.contrib.openstack.context, 'unit_private_ip')
|
@patch.object(charmhelpers.contrib.openstack.context, 'unit_private_ip')
|
||||||
def test_neutroncc_context_api_rel(self, _unit_priv_ip, _npa, _ens_pkgs,
|
def test_neutroncc_context_api_rel(self, _unit_priv_ip, _npa, _ens_pkgs,
|
||||||
_save_ff, _https, _is_clus, _unit_get,
|
_save_ff, _https, _is_clus, _unit_get,
|
||||||
_config):
|
_config, _runits, _rids, _rget):
|
||||||
def mock_npa(plugin, section, manager):
|
def mock_npa(plugin, section, manager):
|
||||||
if section == "driver":
|
if section == "driver":
|
||||||
return "neutron.randomdriver"
|
return "neutron.randomdriver"
|
||||||
@ -95,19 +90,22 @@ class OVSPluginContextTest(CharmTestCase):
|
|||||||
_unit_get.return_value = '127.0.0.13'
|
_unit_get.return_value = '127.0.0.13'
|
||||||
_unit_priv_ip.return_value = '127.0.0.14'
|
_unit_priv_ip.return_value = '127.0.0.14'
|
||||||
_is_clus.return_value = False
|
_is_clus.return_value = False
|
||||||
self.related_units.return_value = ['unit1']
|
_runits.return_value = ['unit1']
|
||||||
self.relation_ids.return_value = ['rid2']
|
_rids.return_value = ['rid2']
|
||||||
self.test_relation.set({'neutron-security-groups': 'True',
|
rdata = {
|
||||||
|
'neutron-security-groups': 'True',
|
||||||
'l2-population': 'True',
|
'l2-population': 'True',
|
||||||
'network-device-mtu': 1500,
|
'network-device-mtu': 1500,
|
||||||
'overlay-network-type': 'gre',
|
'overlay-network-type': 'gre',
|
||||||
})
|
'enable-dvr': 'True',
|
||||||
|
}
|
||||||
|
_rget.side_effect = lambda *args, **kwargs: rdata
|
||||||
self.get_host_ip.return_value = '127.0.0.15'
|
self.get_host_ip.return_value = '127.0.0.15'
|
||||||
self.service_running.return_value = False
|
|
||||||
napi_ctxt = context.OVSPluginContext()
|
napi_ctxt = context.OVSPluginContext()
|
||||||
expect = {
|
expect = {
|
||||||
'neutron_alchemy_flags': {},
|
'neutron_alchemy_flags': {},
|
||||||
'neutron_security_groups': True,
|
'neutron_security_groups': True,
|
||||||
|
'distributed_routing': True,
|
||||||
'verbose': True,
|
'verbose': True,
|
||||||
'local_ip': '127.0.0.15',
|
'local_ip': '127.0.0.15',
|
||||||
'network_device_mtu': 1500,
|
'network_device_mtu': 1500,
|
||||||
@ -126,8 +124,10 @@ class OVSPluginContextTest(CharmTestCase):
|
|||||||
'vlan_ranges': 'physnet1:1000:2000',
|
'vlan_ranges': 'physnet1:1000:2000',
|
||||||
}
|
}
|
||||||
self.assertEquals(expect, napi_ctxt())
|
self.assertEquals(expect, napi_ctxt())
|
||||||
self.service_start.assertCalled()
|
|
||||||
|
|
||||||
|
@patch.object(charmhelpers.contrib.openstack.context, 'relation_get')
|
||||||
|
@patch.object(charmhelpers.contrib.openstack.context, 'relation_ids')
|
||||||
|
@patch.object(charmhelpers.contrib.openstack.context, 'related_units')
|
||||||
@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')
|
||||||
@ -142,7 +142,8 @@ class OVSPluginContextTest(CharmTestCase):
|
|||||||
_ens_pkgs, _save_ff,
|
_ens_pkgs, _save_ff,
|
||||||
_https, _is_clus,
|
_https, _is_clus,
|
||||||
_unit_get,
|
_unit_get,
|
||||||
_config):
|
_config, _runits,
|
||||||
|
_rids, _rget):
|
||||||
def mock_npa(plugin, section, manager):
|
def mock_npa(plugin, section, manager):
|
||||||
if section == "driver":
|
if section == "driver":
|
||||||
return "neutron.randomdriver"
|
return "neutron.randomdriver"
|
||||||
@ -155,17 +156,19 @@ class OVSPluginContextTest(CharmTestCase):
|
|||||||
_unit_priv_ip.return_value = '127.0.0.14'
|
_unit_priv_ip.return_value = '127.0.0.14'
|
||||||
_is_clus.return_value = False
|
_is_clus.return_value = False
|
||||||
self.test_config.set('disable-security-groups', True)
|
self.test_config.set('disable-security-groups', True)
|
||||||
self.related_units.return_value = ['unit1']
|
_runits.return_value = ['unit1']
|
||||||
self.relation_ids.return_value = ['rid2']
|
_rids.return_value = ['rid2']
|
||||||
self.test_relation.set({'neutron-security-groups': 'True',
|
rdata = {
|
||||||
|
'neutron-security-groups': 'True',
|
||||||
'l2-population': 'True',
|
'l2-population': 'True',
|
||||||
'network-device-mtu': 1500,
|
'network-device-mtu': 1500,
|
||||||
'overlay-network-type': 'gre',
|
'overlay-network-type': 'gre',
|
||||||
})
|
}
|
||||||
|
_rget.side_effect = lambda *args, **kwargs: rdata
|
||||||
self.get_host_ip.return_value = '127.0.0.15'
|
self.get_host_ip.return_value = '127.0.0.15'
|
||||||
self.service_running.return_value = False
|
|
||||||
napi_ctxt = context.OVSPluginContext()
|
napi_ctxt = context.OVSPluginContext()
|
||||||
expect = {
|
expect = {
|
||||||
|
'distributed_routing': False,
|
||||||
'neutron_alchemy_flags': {},
|
'neutron_alchemy_flags': {},
|
||||||
'neutron_security_groups': False,
|
'neutron_security_groups': False,
|
||||||
'verbose': True,
|
'verbose': True,
|
||||||
@ -186,4 +189,95 @@ class OVSPluginContextTest(CharmTestCase):
|
|||||||
'vlan_ranges': 'physnet1:1000:2000',
|
'vlan_ranges': 'physnet1:1000:2000',
|
||||||
}
|
}
|
||||||
self.assertEquals(expect, napi_ctxt())
|
self.assertEquals(expect, napi_ctxt())
|
||||||
self.service_start.assertCalled()
|
|
||||||
|
|
||||||
|
class L3AgentContextTest(CharmTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(L3AgentContextTest, self).setUp(context, TO_PATCH)
|
||||||
|
self.config.side_effect = self.test_config.get
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super(L3AgentContextTest, self).tearDown()
|
||||||
|
|
||||||
|
@patch.object(charmhelpers.contrib.openstack.context, 'relation_get')
|
||||||
|
@patch.object(charmhelpers.contrib.openstack.context, 'relation_ids')
|
||||||
|
@patch.object(charmhelpers.contrib.openstack.context, 'related_units')
|
||||||
|
def test_dvr_enabled(self, _runits, _rids, _rget):
|
||||||
|
_runits.return_value = ['unit1']
|
||||||
|
_rids.return_value = ['rid2']
|
||||||
|
rdata = {
|
||||||
|
'neutron-security-groups': 'True',
|
||||||
|
'enable-dvr': 'True',
|
||||||
|
'l2-population': 'True',
|
||||||
|
'overlay-network-type': 'vxlan',
|
||||||
|
'network-device-mtu': 1500,
|
||||||
|
}
|
||||||
|
_rget.side_effect = lambda *args, **kwargs: rdata
|
||||||
|
self.assertEquals(context.L3AgentContext()(), {'agent_mode': 'dvr'})
|
||||||
|
|
||||||
|
@patch.object(charmhelpers.contrib.openstack.context, 'relation_get')
|
||||||
|
@patch.object(charmhelpers.contrib.openstack.context, 'relation_ids')
|
||||||
|
@patch.object(charmhelpers.contrib.openstack.context, 'related_units')
|
||||||
|
def test_dvr_disabled(self, _runits, _rids, _rget):
|
||||||
|
_runits.return_value = ['unit1']
|
||||||
|
_rids.return_value = ['rid2']
|
||||||
|
rdata = {
|
||||||
|
'neutron-security-groups': 'True',
|
||||||
|
'enable-dvr': 'False',
|
||||||
|
'l2-population': 'True',
|
||||||
|
'overlay-network-type': 'vxlan',
|
||||||
|
'network-device-mtu': 1500,
|
||||||
|
}
|
||||||
|
_rget.side_effect = lambda *args, **kwargs: rdata
|
||||||
|
self.assertEquals(context.L3AgentContext()(), {'agent_mode': 'legacy'})
|
||||||
|
|
||||||
|
|
||||||
|
class DVRSharedSecretContext(CharmTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(DVRSharedSecretContext, self).setUp(context,
|
||||||
|
TO_PATCH)
|
||||||
|
self.config.side_effect = self.test_config.get
|
||||||
|
|
||||||
|
@patch('os.path')
|
||||||
|
@patch('uuid.uuid4')
|
||||||
|
def test_secret_created_stored(self, _uuid4, _path):
|
||||||
|
_path.exists.return_value = False
|
||||||
|
_uuid4.return_value = 'secret_thing'
|
||||||
|
with patch_open() as (_open, _file):
|
||||||
|
self.assertEquals(context.get_shared_secret(),
|
||||||
|
'secret_thing')
|
||||||
|
_open.assert_called_with(
|
||||||
|
context.SHARED_SECRET.format('quantum'), 'w')
|
||||||
|
_file.write.assert_called_with('secret_thing')
|
||||||
|
|
||||||
|
@patch('os.path')
|
||||||
|
def test_secret_retrieved(self, _path):
|
||||||
|
_path.exists.return_value = True
|
||||||
|
with patch_open() as (_open, _file):
|
||||||
|
_file.read.return_value = 'secret_thing\n'
|
||||||
|
self.assertEquals(context.get_shared_secret(),
|
||||||
|
'secret_thing')
|
||||||
|
_open.assert_called_with(
|
||||||
|
context.SHARED_SECRET.format('quantum'), 'r')
|
||||||
|
|
||||||
|
@patch.object(context, 'NeutronAPIContext')
|
||||||
|
@patch.object(context, 'get_shared_secret')
|
||||||
|
def test_shared_secretcontext_dvr(self, _shared_secret,
|
||||||
|
_NeutronAPIContext):
|
||||||
|
_NeutronAPIContext.side_effect = fake_context({'enable_dvr': True})
|
||||||
|
_shared_secret.return_value = 'secret_thing'
|
||||||
|
self.resolve_address.return_value = '10.0.0.10'
|
||||||
|
self.assertEquals(context.DVRSharedSecretContext()(),
|
||||||
|
{'shared_secret': 'secret_thing',
|
||||||
|
'local_ip': '10.0.0.10'})
|
||||||
|
|
||||||
|
@patch.object(context, 'NeutronAPIContext')
|
||||||
|
@patch.object(context, 'get_shared_secret')
|
||||||
|
def test_shared_secretcontext_nodvr(self, _shared_secret,
|
||||||
|
_NeutronAPIContext):
|
||||||
|
_NeutronAPIContext.side_effect = fake_context({'enable_dvr': False})
|
||||||
|
_shared_secret.return_value = 'secret_thing'
|
||||||
|
self.resolve_address.return_value = '10.0.0.10'
|
||||||
|
self.assertEquals(context.DVRSharedSecretContext()(), {})
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
from mock import MagicMock, patch, call
|
from mock import MagicMock, patch, call
|
||||||
from test_utils import CharmTestCase
|
import yaml
|
||||||
|
|
||||||
|
from test_utils import CharmTestCase
|
||||||
|
|
||||||
with patch('charmhelpers.core.hookenv.config') as config:
|
with patch('charmhelpers.core.hookenv.config') as config:
|
||||||
config.return_value = 'neutron'
|
config.return_value = 'neutron'
|
||||||
@ -21,11 +21,18 @@ utils.restart_map = _map
|
|||||||
TO_PATCH = [
|
TO_PATCH = [
|
||||||
'apt_update',
|
'apt_update',
|
||||||
'apt_install',
|
'apt_install',
|
||||||
|
'apt_purge',
|
||||||
'config',
|
'config',
|
||||||
'CONFIGS',
|
'CONFIGS',
|
||||||
'determine_packages',
|
'determine_packages',
|
||||||
|
'determine_dvr_packages',
|
||||||
|
'get_shared_secret',
|
||||||
|
'git_install',
|
||||||
'log',
|
'log',
|
||||||
|
'relation_ids',
|
||||||
'relation_set',
|
'relation_set',
|
||||||
|
'configure_ovs',
|
||||||
|
'use_dvr',
|
||||||
]
|
]
|
||||||
NEUTRON_CONF_DIR = "/etc/neutron"
|
NEUTRON_CONF_DIR = "/etc/neutron"
|
||||||
|
|
||||||
@ -44,7 +51,9 @@ class NeutronOVSHooksTests(CharmTestCase):
|
|||||||
hooks.hooks.execute([
|
hooks.hooks.execute([
|
||||||
'hooks/{}'.format(hookname)])
|
'hooks/{}'.format(hookname)])
|
||||||
|
|
||||||
def test_install_hook(self):
|
@patch.object(hooks, 'git_install_requested')
|
||||||
|
def test_install_hook(self, git_requested):
|
||||||
|
git_requested.return_value = False
|
||||||
_pkgs = ['foo', 'bar']
|
_pkgs = ['foo', 'bar']
|
||||||
self.determine_packages.return_value = [_pkgs]
|
self.determine_packages.return_value = [_pkgs]
|
||||||
self._call_hook('install')
|
self._call_hook('install')
|
||||||
@ -53,9 +62,97 @@ class NeutronOVSHooksTests(CharmTestCase):
|
|||||||
call(_pkgs, fatal=True),
|
call(_pkgs, fatal=True),
|
||||||
])
|
])
|
||||||
|
|
||||||
def test_config_changed(self):
|
@patch.object(hooks, 'git_install_requested')
|
||||||
|
def test_install_hook_git(self, git_requested):
|
||||||
|
git_requested.return_value = True
|
||||||
|
_pkgs = ['foo', 'bar']
|
||||||
|
self.determine_packages.return_value = _pkgs
|
||||||
|
openstack_origin_git = {
|
||||||
|
'repositories': [
|
||||||
|
{'name': 'requirements',
|
||||||
|
'repository': 'git://git.openstack.org/openstack/requirements', # noqa
|
||||||
|
'branch': 'stable/juno'},
|
||||||
|
{'name': 'neutron',
|
||||||
|
'repository': 'git://git.openstack.org/openstack/neutron',
|
||||||
|
'branch': 'stable/juno'}
|
||||||
|
],
|
||||||
|
'directory': '/mnt/openstack-git',
|
||||||
|
}
|
||||||
|
projects_yaml = yaml.dump(openstack_origin_git)
|
||||||
|
self.test_config.set('openstack-origin-git', projects_yaml)
|
||||||
|
self._call_hook('install')
|
||||||
|
self.apt_update.assert_called_with()
|
||||||
|
self.assertTrue(self.determine_packages)
|
||||||
|
self.git_install.assert_called_with(projects_yaml)
|
||||||
|
|
||||||
|
@patch.object(hooks, 'git_install_requested')
|
||||||
|
def test_config_changed(self, git_requested):
|
||||||
|
git_requested.return_value = False
|
||||||
|
self.relation_ids.return_value = ['relid']
|
||||||
|
_zmq_joined = self.patch('zeromq_configuration_relation_joined')
|
||||||
self._call_hook('config-changed')
|
self._call_hook('config-changed')
|
||||||
self.assertTrue(self.CONFIGS.write_all.called)
|
self.assertTrue(self.CONFIGS.write_all.called)
|
||||||
|
self.assertTrue(_zmq_joined.called_with('relid'))
|
||||||
|
self.configure_ovs.assert_called_with()
|
||||||
|
|
||||||
|
@patch.object(hooks, 'git_install_requested')
|
||||||
|
@patch.object(hooks, 'config_value_changed')
|
||||||
|
def test_config_changed_git(self, config_val_changed, git_requested):
|
||||||
|
git_requested.return_value = True
|
||||||
|
self.relation_ids.return_value = ['relid']
|
||||||
|
_zmq_joined = self.patch('zeromq_configuration_relation_joined')
|
||||||
|
openstack_origin_git = {
|
||||||
|
'repositories': [
|
||||||
|
{'name': 'requirements',
|
||||||
|
'repository':
|
||||||
|
'git://git.openstack.org/openstack/requirements',
|
||||||
|
'branch': 'stable/juno'},
|
||||||
|
{'name': 'neutron',
|
||||||
|
'repository': 'git://git.openstack.org/openstack/neutron',
|
||||||
|
'branch': 'stable/juno'}
|
||||||
|
],
|
||||||
|
'directory': '/mnt/openstack-git',
|
||||||
|
}
|
||||||
|
projects_yaml = yaml.dump(openstack_origin_git)
|
||||||
|
self.test_config.set('openstack-origin-git', projects_yaml)
|
||||||
|
self._call_hook('config-changed')
|
||||||
|
self.git_install.assert_called_with(projects_yaml)
|
||||||
|
self.assertTrue(self.CONFIGS.write_all.called)
|
||||||
|
self.assertTrue(_zmq_joined.called_with('relid'))
|
||||||
|
self.configure_ovs.assert_called_with()
|
||||||
|
|
||||||
|
@patch.object(hooks, 'git_install_requested')
|
||||||
|
def test_config_changed_dvr(self, git_requested):
|
||||||
|
git_requested.return_value = False
|
||||||
|
self.determine_dvr_packages.return_value = ['dvr']
|
||||||
|
self._call_hook('config-changed')
|
||||||
|
self.apt_update.assert_called_with()
|
||||||
|
self.assertTrue(self.CONFIGS.write_all.called)
|
||||||
|
self.apt_install.assert_has_calls([
|
||||||
|
call(['dvr'], fatal=True),
|
||||||
|
])
|
||||||
|
self.configure_ovs.assert_called_with()
|
||||||
|
|
||||||
|
@patch.object(hooks, 'neutron_plugin_joined')
|
||||||
|
def test_neutron_plugin_api(self, _plugin_joined):
|
||||||
|
self.relation_ids.return_value = ['rid']
|
||||||
|
self._call_hook('neutron-plugin-api-relation-changed')
|
||||||
|
self.configure_ovs.assert_called_with()
|
||||||
|
self.assertTrue(self.CONFIGS.write_all.called)
|
||||||
|
_plugin_joined.assert_called_with(relation_id='rid')
|
||||||
|
|
||||||
|
@patch.object(hooks, 'git_install_requested')
|
||||||
|
def test_neutron_plugin_joined(self, git_requested):
|
||||||
|
git_requested.return_value = False
|
||||||
|
self.get_shared_secret.return_value = 'secret'
|
||||||
|
self._call_hook('neutron-plugin-relation-joined')
|
||||||
|
rel_data = {
|
||||||
|
'metadata-shared-secret': 'secret',
|
||||||
|
}
|
||||||
|
self.relation_set.assert_called_with(
|
||||||
|
relation_id=None,
|
||||||
|
**rel_data
|
||||||
|
)
|
||||||
|
|
||||||
def test_amqp_joined(self):
|
def test_amqp_joined(self):
|
||||||
self._call_hook('amqp-relation-joined')
|
self._call_hook('amqp-relation-joined')
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
|
|
||||||
from mock import MagicMock, patch
|
from mock import MagicMock, patch, call
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
import charmhelpers.contrib.openstack.templating as templating
|
import charmhelpers.contrib.openstack.templating as templating
|
||||||
|
|
||||||
templating.OSConfigRenderer = MagicMock()
|
templating.OSConfigRenderer = MagicMock()
|
||||||
|
|
||||||
import neutron_ovs_utils as nutils
|
import neutron_ovs_utils as nutils
|
||||||
|
import neutron_ovs_context
|
||||||
|
|
||||||
from test_utils import (
|
from test_utils import (
|
||||||
CharmTestCase,
|
CharmTestCase,
|
||||||
@ -15,12 +16,28 @@ import charmhelpers.core.hookenv as hookenv
|
|||||||
|
|
||||||
|
|
||||||
TO_PATCH = [
|
TO_PATCH = [
|
||||||
|
'add_bridge',
|
||||||
|
'add_bridge_port',
|
||||||
|
'config',
|
||||||
'os_release',
|
'os_release',
|
||||||
'neutron_plugin_attribute',
|
'neutron_plugin_attribute',
|
||||||
|
'full_restart',
|
||||||
|
'service_restart',
|
||||||
|
'service_running',
|
||||||
|
'ExternalPortContext',
|
||||||
]
|
]
|
||||||
|
|
||||||
head_pkg = 'linux-headers-3.15.0-5-generic'
|
head_pkg = 'linux-headers-3.15.0-5-generic'
|
||||||
|
|
||||||
|
openstack_origin_git = \
|
||||||
|
"""repositories:
|
||||||
|
- {name: requirements,
|
||||||
|
repository: 'git://git.openstack.org/openstack/requirements',
|
||||||
|
branch: stable/juno}
|
||||||
|
- {name: neutron,
|
||||||
|
repository: 'git://git.openstack.org/openstack/neutron',
|
||||||
|
branch: stable/juno}"""
|
||||||
|
|
||||||
|
|
||||||
def _mock_npa(plugin, attr, net_manager=None):
|
def _mock_npa(plugin, attr, net_manager=None):
|
||||||
plugins = {
|
plugins = {
|
||||||
@ -38,26 +55,42 @@ def _mock_npa(plugin, attr, net_manager=None):
|
|||||||
return plugins[plugin][attr]
|
return plugins[plugin][attr]
|
||||||
|
|
||||||
|
|
||||||
|
class DummyContext():
|
||||||
|
|
||||||
|
def __init__(self, return_value):
|
||||||
|
self.return_value = return_value
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
return self.return_value
|
||||||
|
|
||||||
|
|
||||||
class TestNeutronOVSUtils(CharmTestCase):
|
class TestNeutronOVSUtils(CharmTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestNeutronOVSUtils, self).setUp(nutils, TO_PATCH)
|
super(TestNeutronOVSUtils, self).setUp(nutils, TO_PATCH)
|
||||||
self.neutron_plugin_attribute.side_effect = _mock_npa
|
self.neutron_plugin_attribute.side_effect = _mock_npa
|
||||||
|
self.config.side_effect = self.test_config.get
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
# Reset cached cache
|
# Reset cached cache
|
||||||
hookenv.cache = {}
|
hookenv.cache = {}
|
||||||
|
|
||||||
|
@patch.object(nutils, 'use_dvr')
|
||||||
|
@patch.object(nutils, 'git_install_requested')
|
||||||
@patch.object(charmhelpers.contrib.openstack.neutron, 'os_release')
|
@patch.object(charmhelpers.contrib.openstack.neutron, 'os_release')
|
||||||
@patch.object(charmhelpers.contrib.openstack.neutron, 'headers_package')
|
@patch.object(charmhelpers.contrib.openstack.neutron, 'headers_package')
|
||||||
def test_determine_packages(self, _head_pkgs, _os_rel):
|
def test_determine_packages(self, _head_pkgs, _os_rel, _git_requested,
|
||||||
|
_use_dvr):
|
||||||
|
_git_requested.return_value = False
|
||||||
|
_use_dvr.return_value = False
|
||||||
_os_rel.return_value = 'trusty'
|
_os_rel.return_value = 'trusty'
|
||||||
_head_pkgs.return_value = head_pkg
|
_head_pkgs.return_value = head_pkg
|
||||||
pkg_list = nutils.determine_packages()
|
pkg_list = nutils.determine_packages()
|
||||||
expect = [['neutron-plugin-openvswitch-agent'], [head_pkg]]
|
expect = [['neutron-plugin-openvswitch-agent'], [head_pkg]]
|
||||||
self.assertItemsEqual(pkg_list, expect)
|
self.assertItemsEqual(pkg_list, expect)
|
||||||
|
|
||||||
def test_register_configs(self):
|
@patch.object(nutils, 'use_dvr')
|
||||||
|
def test_register_configs(self, _use_dvr):
|
||||||
class _mock_OSConfigRenderer():
|
class _mock_OSConfigRenderer():
|
||||||
def __init__(self, templates_dir=None, openstack_release=None):
|
def __init__(self, templates_dir=None, openstack_release=None):
|
||||||
self.configs = []
|
self.configs = []
|
||||||
@ -67,6 +100,7 @@ class TestNeutronOVSUtils(CharmTestCase):
|
|||||||
self.configs.append(config)
|
self.configs.append(config)
|
||||||
self.ctxts.append(ctxt)
|
self.ctxts.append(ctxt)
|
||||||
|
|
||||||
|
_use_dvr.return_value = False
|
||||||
self.os_release.return_value = 'trusty'
|
self.os_release.return_value = 'trusty'
|
||||||
templating.OSConfigRenderer.side_effect = _mock_OSConfigRenderer
|
templating.OSConfigRenderer.side_effect = _mock_OSConfigRenderer
|
||||||
_regconfs = nutils.register_configs()
|
_regconfs = nutils.register_configs()
|
||||||
@ -75,12 +109,28 @@ class TestNeutronOVSUtils(CharmTestCase):
|
|||||||
'/etc/init/os-charm-phy-nic-mtu.conf']
|
'/etc/init/os-charm-phy-nic-mtu.conf']
|
||||||
self.assertItemsEqual(_regconfs.configs, confs)
|
self.assertItemsEqual(_regconfs.configs, confs)
|
||||||
|
|
||||||
def test_resource_map(self):
|
@patch.object(nutils, 'use_dvr')
|
||||||
|
def test_resource_map(self, _use_dvr):
|
||||||
|
_use_dvr.return_value = False
|
||||||
_map = nutils.resource_map()
|
_map = nutils.resource_map()
|
||||||
|
svcs = ['neutron-plugin-openvswitch-agent']
|
||||||
confs = [nutils.NEUTRON_CONF]
|
confs = [nutils.NEUTRON_CONF]
|
||||||
[self.assertIn(q_conf, _map.keys()) for q_conf in confs]
|
[self.assertIn(q_conf, _map.keys()) for q_conf in confs]
|
||||||
|
self.assertEqual(_map[nutils.NEUTRON_CONF]['services'], svcs)
|
||||||
|
|
||||||
def test_restart_map(self):
|
@patch.object(nutils, 'use_dvr')
|
||||||
|
def test_resource_map_dvr(self, _use_dvr):
|
||||||
|
_use_dvr.return_value = True
|
||||||
|
_map = nutils.resource_map()
|
||||||
|
svcs = ['neutron-plugin-openvswitch-agent', 'neutron-metadata-agent',
|
||||||
|
'neutron-l3-agent']
|
||||||
|
confs = [nutils.NEUTRON_CONF]
|
||||||
|
[self.assertIn(q_conf, _map.keys()) for q_conf in confs]
|
||||||
|
self.assertEqual(_map[nutils.NEUTRON_CONF]['services'], svcs)
|
||||||
|
|
||||||
|
@patch.object(nutils, 'use_dvr')
|
||||||
|
def test_restart_map(self, _use_dvr):
|
||||||
|
_use_dvr.return_value = False
|
||||||
_restart_map = nutils.restart_map()
|
_restart_map = nutils.restart_map()
|
||||||
ML2CONF = "/etc/neutron/plugins/ml2/ml2_conf.ini"
|
ML2CONF = "/etc/neutron/plugins/ml2/ml2_conf.ini"
|
||||||
expect = OrderedDict([
|
expect = OrderedDict([
|
||||||
@ -92,3 +142,170 @@ class TestNeutronOVSUtils(CharmTestCase):
|
|||||||
for item in _restart_map:
|
for item in _restart_map:
|
||||||
self.assertTrue(item in _restart_map)
|
self.assertTrue(item in _restart_map)
|
||||||
self.assertTrue(expect[item] == _restart_map[item])
|
self.assertTrue(expect[item] == _restart_map[item])
|
||||||
|
|
||||||
|
@patch.object(nutils, 'use_dvr')
|
||||||
|
@patch('charmhelpers.contrib.openstack.context.config')
|
||||||
|
def test_configure_ovs_ovs_data_port(self, mock_config, _use_dvr):
|
||||||
|
_use_dvr.return_value = False
|
||||||
|
mock_config.side_effect = self.test_config.get
|
||||||
|
self.config.side_effect = self.test_config.get
|
||||||
|
self.ExternalPortContext.return_value = \
|
||||||
|
DummyContext(return_value=None)
|
||||||
|
# Test back-compatibility i.e. port but no bridge (so br-data is
|
||||||
|
# assumed)
|
||||||
|
self.test_config.set('data-port', 'eth0')
|
||||||
|
nutils.configure_ovs()
|
||||||
|
self.add_bridge.assert_has_calls([
|
||||||
|
call('br-int'),
|
||||||
|
call('br-ex'),
|
||||||
|
call('br-data')
|
||||||
|
])
|
||||||
|
self.assertTrue(self.add_bridge_port.called)
|
||||||
|
|
||||||
|
# Now test with bridge:port format
|
||||||
|
self.test_config.set('data-port', 'br-foo:eth0')
|
||||||
|
self.add_bridge.reset_mock()
|
||||||
|
self.add_bridge_port.reset_mock()
|
||||||
|
nutils.configure_ovs()
|
||||||
|
self.add_bridge.assert_has_calls([
|
||||||
|
call('br-int'),
|
||||||
|
call('br-ex'),
|
||||||
|
call('br-data')
|
||||||
|
])
|
||||||
|
# Not called since we have a bogus bridge in data-ports
|
||||||
|
self.assertFalse(self.add_bridge_port.called)
|
||||||
|
|
||||||
|
@patch.object(nutils, 'use_dvr')
|
||||||
|
@patch('charmhelpers.contrib.openstack.context.config')
|
||||||
|
def test_configure_ovs_starts_service_if_required(self, mock_config,
|
||||||
|
_use_dvr):
|
||||||
|
_use_dvr.return_value = False
|
||||||
|
mock_config.side_effect = self.test_config.get
|
||||||
|
self.config.return_value = 'ovs'
|
||||||
|
self.service_running.return_value = False
|
||||||
|
nutils.configure_ovs()
|
||||||
|
self.assertTrue(self.full_restart.called)
|
||||||
|
|
||||||
|
@patch.object(nutils, 'use_dvr')
|
||||||
|
@patch('charmhelpers.contrib.openstack.context.config')
|
||||||
|
def test_configure_ovs_doesnt_restart_service(self, mock_config, _use_dvr):
|
||||||
|
_use_dvr.return_value = False
|
||||||
|
mock_config.side_effect = self.test_config.get
|
||||||
|
self.config.side_effect = self.test_config.get
|
||||||
|
self.service_running.return_value = True
|
||||||
|
nutils.configure_ovs()
|
||||||
|
self.assertFalse(self.full_restart.called)
|
||||||
|
|
||||||
|
@patch.object(nutils, 'use_dvr')
|
||||||
|
@patch('charmhelpers.contrib.openstack.context.config')
|
||||||
|
def test_configure_ovs_ovs_ext_port(self, mock_config, _use_dvr):
|
||||||
|
_use_dvr.return_value = True
|
||||||
|
mock_config.side_effect = self.test_config.get
|
||||||
|
self.config.side_effect = self.test_config.get
|
||||||
|
self.test_config.set('ext-port', 'eth0')
|
||||||
|
self.ExternalPortContext.return_value = \
|
||||||
|
DummyContext(return_value={'ext_port': 'eth0'})
|
||||||
|
nutils.configure_ovs()
|
||||||
|
self.add_bridge.assert_has_calls([
|
||||||
|
call('br-int'),
|
||||||
|
call('br-ex'),
|
||||||
|
call('br-data')
|
||||||
|
])
|
||||||
|
self.add_bridge_port.assert_called_with('br-ex', 'eth0')
|
||||||
|
|
||||||
|
@patch.object(neutron_ovs_context, 'DVRSharedSecretContext')
|
||||||
|
def test_get_shared_secret(self, _dvr_secret_ctxt):
|
||||||
|
_dvr_secret_ctxt.return_value = \
|
||||||
|
DummyContext(return_value={'shared_secret': 'supersecret'})
|
||||||
|
self.assertEqual(nutils.get_shared_secret(), 'supersecret')
|
||||||
|
|
||||||
|
@patch.object(nutils, 'git_install_requested')
|
||||||
|
@patch.object(nutils, 'git_clone_and_install')
|
||||||
|
@patch.object(nutils, 'git_post_install')
|
||||||
|
@patch.object(nutils, 'git_pre_install')
|
||||||
|
def test_git_install(self, git_pre, git_post, git_clone_and_install,
|
||||||
|
git_requested):
|
||||||
|
projects_yaml = openstack_origin_git
|
||||||
|
git_requested.return_value = True
|
||||||
|
nutils.git_install(projects_yaml)
|
||||||
|
self.assertTrue(git_pre.called)
|
||||||
|
git_clone_and_install.assert_called_with(openstack_origin_git,
|
||||||
|
core_project='neutron')
|
||||||
|
self.assertTrue(git_post.called)
|
||||||
|
|
||||||
|
@patch.object(nutils, 'mkdir')
|
||||||
|
@patch.object(nutils, 'write_file')
|
||||||
|
@patch.object(nutils, 'add_user_to_group')
|
||||||
|
@patch.object(nutils, 'add_group')
|
||||||
|
@patch.object(nutils, 'adduser')
|
||||||
|
def test_git_pre_install(self, adduser, add_group, add_user_to_group,
|
||||||
|
write_file, mkdir):
|
||||||
|
nutils.git_pre_install()
|
||||||
|
adduser.assert_called_with('neutron', shell='/bin/bash',
|
||||||
|
system_user=True)
|
||||||
|
add_group.assert_called_with('neutron', system_group=True)
|
||||||
|
add_user_to_group.assert_called_with('neutron', 'neutron')
|
||||||
|
expected = [
|
||||||
|
call('/etc/neutron', owner='neutron',
|
||||||
|
group='neutron', perms=0700, force=False),
|
||||||
|
call('/etc/neutron/rootwrap.d', owner='neutron',
|
||||||
|
group='neutron', perms=0700, force=False),
|
||||||
|
call('/var/lib/neutron', owner='neutron',
|
||||||
|
group='neutron', perms=0700, force=False),
|
||||||
|
call('/var/lib/neutron/lock', owner='neutron',
|
||||||
|
group='neutron', perms=0700, force=False),
|
||||||
|
call('/var/log/neutron', owner='neutron',
|
||||||
|
group='neutron', perms=0700, force=False),
|
||||||
|
]
|
||||||
|
self.assertEquals(mkdir.call_args_list, expected)
|
||||||
|
expected = [
|
||||||
|
call('/var/log/neutron/server.log', '', owner='neutron',
|
||||||
|
group='neutron', perms=0600),
|
||||||
|
]
|
||||||
|
self.assertEquals(write_file.call_args_list, expected)
|
||||||
|
|
||||||
|
@patch.object(nutils, 'git_src_dir')
|
||||||
|
@patch.object(nutils, 'service_restart')
|
||||||
|
@patch.object(nutils, 'render')
|
||||||
|
@patch('os.path.join')
|
||||||
|
@patch('shutil.copyfile')
|
||||||
|
def test_git_post_install(self, copyfile, join, render,
|
||||||
|
service_restart, git_src_dir):
|
||||||
|
projects_yaml = openstack_origin_git
|
||||||
|
join.return_value = 'joined-string'
|
||||||
|
nutils.git_post_install(projects_yaml)
|
||||||
|
expected = [
|
||||||
|
call('joined-string', '/etc/neutron/rootwrap.d/debug.filters'),
|
||||||
|
call('joined-string', '/etc/neutron/policy.json'),
|
||||||
|
call('joined-string', '/etc/neutron/rootwrap.conf'),
|
||||||
|
]
|
||||||
|
copyfile.assert_has_calls(expected, any_order=True)
|
||||||
|
neutron_ovs_agent_context = {
|
||||||
|
'service_description': 'Neutron OpenvSwitch Plugin Agent',
|
||||||
|
'charm_name': 'neutron-openvswitch',
|
||||||
|
'process_name': 'neutron-openvswitch-agent',
|
||||||
|
'cleanup_process_name': 'neutron-ovs-cleanup',
|
||||||
|
'plugin_config': '/etc/neutron/plugins/ml2/ml2_conf.ini',
|
||||||
|
'log_file': '/var/log/neutron/openvswitch-agent.log',
|
||||||
|
}
|
||||||
|
neutron_ovs_cleanup_context = {
|
||||||
|
'service_description': 'Neutron OpenvSwitch Cleanup',
|
||||||
|
'charm_name': 'neutron-openvswitch',
|
||||||
|
'process_name': 'neutron-ovs-cleanup',
|
||||||
|
'log_file': '/var/log/neutron/ovs-cleanup.log',
|
||||||
|
}
|
||||||
|
expected = [
|
||||||
|
call('neutron_sudoers', '/etc/sudoers.d/neutron_sudoers', {},
|
||||||
|
perms=0o440),
|
||||||
|
call('upstart/neutron-plugin-openvswitch-agent.upstart',
|
||||||
|
'/etc/init/neutron-plugin-openvswitch-agent.conf',
|
||||||
|
neutron_ovs_agent_context, perms=0o644),
|
||||||
|
call('upstart/neutron-ovs-cleanup.upstart',
|
||||||
|
'/etc/init/neutron-ovs-cleanup.conf',
|
||||||
|
neutron_ovs_cleanup_context, perms=0o644),
|
||||||
|
]
|
||||||
|
self.assertEquals(render.call_args_list, expected)
|
||||||
|
expected = [
|
||||||
|
call('neutron-plugin-openvswitch-agent'),
|
||||||
|
]
|
||||||
|
self.assertEquals(service_restart.call_args_list, expected)
|
||||||
|
Loading…
Reference in New Issue
Block a user