Add support for multiple-networks

This commit is contained in:
James Page
2014-06-27 12:35:10 +01:00
parent 016d7e7421
commit ad45c61d5e
11 changed files with 259 additions and 19 deletions

View File

@@ -1,4 +1,4 @@
branch: lp:charm-helpers
branch: lp:~james-page/charm-helpers/network-splits
destination: hooks/charmhelpers
include:
- core
@@ -6,3 +6,4 @@ include:
- contrib.openstack
- contrib.hahelpers
- contrib.storage.linux
- contrib.network.ip

View File

@@ -55,4 +55,27 @@ options:
description: |
SSL CA to use with the certificate and key provided - this is only
required if you are providing a privately signed ssl_cert and ssl_key.
# Network configuration options
# by default all access is over 'private-address'
os-admin-network:
type: string
description: |
The IP address and netmask of the OpenStack Admin network (e.g.,
192.168.0.0/24)
.
This network will be used for admin endpoints.
os-internal-network:
type: string
description: |
The IP address and netmask of the OpenStack Internal network (e.g.,
192.168.0.0/24)
.
This network will be used for internal endpoints.
os-public-network:
type: string
description: |
The IP address and netmask of the OpenStack Public network (e.g.,
192.168.0.0/24)
.
This network will be used for public endpoints.

View File

@@ -34,6 +34,7 @@ from ceilometer_utils import (
do_openstack_upgrade
)
from ceilometer_contexts import CEILOMETER_PORT
from charmhelpers.contrib.network.ip import get_address_in_network
hooks = Hooks()
CONFIGS = register_configs()
@@ -88,7 +89,8 @@ def config_changed():
do_openstack_upgrade(CONFIGS)
CONFIGS.write_all()
ceilometer_joined()
for rid in relation_ids('identity-service'):
keystone_joined(relid=rid)
@hooks.hook('upgrade-charm')
def upgrade_charm():
@@ -97,12 +99,26 @@ def upgrade_charm():
@hooks.hook("identity-service-relation-joined")
def keystone_joined():
url = "http://{}:{}".format(unit_get("private-address"),
CEILOMETER_PORT)
def keystone_joined(relid=None):
public_url = "http://{}:{}".format(
get_address_in_network(config('os-public-network'),
unit_get("public-address")),
CEILOMETER_PORT
)
admin_url = "http://{}:{}".format(
get_address_in_network(config('os-admin-network'),
unit_get("private-address")),
CEILOMETER_PORT
)
internal_url = "http://{}:{}".format(
get_address_in_network(config('os-internal-network'),
unit_get("private-address")),
CEILOMETER_PORT
)
region = config("region")
relation_set(service=CEILOMETER_SERVICE,
public_url=url, admin_url=url, internal_url=url,
relation_set(relation_id=relid,
service=CEILOMETER_SERVICE,
public_url=public_url, admin_url=admin_url, internal_url=internal_url,
requested_roles=CEILOMETER_ROLE,
region=region)

View File

@@ -163,7 +163,7 @@ def get_hacluster_config():
return conf
def canonical_url(configs, vip_setting='vip'):
def canonical_url(configs, vip_setting='vip', address=None):
'''
Returns the correct HTTP URL to this host given the state of HTTPS
configuration and hacluster.
@@ -179,5 +179,5 @@ def canonical_url(configs, vip_setting='vip'):
if is_clustered():
addr = config_get(vip_setting)
else:
addr = unit_get('private-address')
addr = address or unit_get('private-address')
return '%s://%s' % (scheme, addr)

View File

@@ -0,0 +1,69 @@
import sys
from charmhelpers.fetch import apt_install
from charmhelpers.core.hookenv import (
ERROR, log,
)
try:
import netifaces
except ImportError:
apt_install('python-netifaces')
import netifaces
try:
import netaddr
except ImportError:
apt_install('python-netaddr')
import netaddr
def _validate_cidr(network):
try:
netaddr.IPNetwork(network)
except (netaddr.core.AddrFormatError, ValueError):
raise ValueError("Network (%s) is not in CIDR presentation format" %
network)
def get_address_in_network(network, fallback=None, fatal=False):
"""
Get an IPv4 address within the network from the host.
Args:
network (str): CIDR presentation format. For example,
'192.168.1.0/24'.
fallback (str): If no address is found, return fallback.
fatal (boolean): If no address is found, fallback is not
set and fatal is True then exit(1).
"""
def not_found_error_out():
log("No IP address found in network: %s" % network,
level=ERROR)
sys.exit(1)
if network is None:
if fallback is not None:
return fallback
else:
if fatal:
not_found_error_out()
_validate_cidr(network)
for iface in netifaces.interfaces():
addresses = netifaces.ifaddresses(iface)
if netifaces.AF_INET in addresses:
addr = addresses[netifaces.AF_INET][0]['addr']
netmask = addresses[netifaces.AF_INET][0]['netmask']
cidr = netaddr.IPNetwork("%s/%s" % (addr, netmask))
if cidr in netaddr.IPNetwork(network):
return str(cidr.ip)
if fallback is not None:
return fallback
if fatal:
not_found_error_out()
return None

View File

@@ -332,10 +332,12 @@ class CephContext(OSContextGenerator):
use_syslog = str(config('use-syslog')).lower()
for rid in relation_ids('ceph'):
for unit in related_units(rid):
mon_hosts.append(relation_get('private-address', rid=rid,
unit=unit))
auth = relation_get('auth', rid=rid, unit=unit)
key = relation_get('key', rid=rid, unit=unit)
ceph_addr = \
relation_get('ceph-public-address', rid=rid, unit=unit) or \
relation_get('private-address', rid=rid, unit=unit)
mon_hosts.append(ceph_addr)
ctxt = {
'mon_hosts': ' '.join(mon_hosts),

View File

@@ -0,0 +1,114 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = 'Jorge Niedbalski R. <jorge.niedbalski@canonical.com>'
import os
class Fstab(file):
"""This class extends file in order to implement a file reader/writer
for file `/etc/fstab`
"""
class Entry(object):
"""Entry class represents a non-comment line on the `/etc/fstab` file
"""
def __init__(self, device, mountpoint, filesystem,
options, d=0, p=0):
self.device = device
self.mountpoint = mountpoint
self.filesystem = filesystem
if not options:
options = "defaults"
self.options = options
self.d = d
self.p = p
def __eq__(self, o):
return str(self) == str(o)
def __str__(self):
return "{} {} {} {} {} {}".format(self.device,
self.mountpoint,
self.filesystem,
self.options,
self.d,
self.p)
DEFAULT_PATH = os.path.join(os.path.sep, 'etc', 'fstab')
def __init__(self, path=None):
if path:
self._path = path
else:
self._path = self.DEFAULT_PATH
file.__init__(self, self._path, 'r+')
def _hydrate_entry(self, line):
return Fstab.Entry(*filter(
lambda x: x not in ('', None),
line.strip("\n").split(" ")))
@property
def entries(self):
self.seek(0)
for line in self.readlines():
try:
if not line.startswith("#"):
yield self._hydrate_entry(line)
except ValueError:
pass
def get_entry_by_attr(self, attr, value):
for entry in self.entries:
e_attr = getattr(entry, attr)
if e_attr == value:
return entry
return None
def add_entry(self, entry):
if self.get_entry_by_attr('device', entry.device):
return False
self.write(str(entry) + '\n')
self.truncate()
return entry
def remove_entry(self, entry):
self.seek(0)
lines = self.readlines()
found = False
for index, line in enumerate(lines):
if not line.startswith("#"):
if self._hydrate_entry(line) == entry:
found = True
break
if not found:
return False
lines.remove(line)
self.seek(0)
self.write(''.join(lines))
self.truncate()
return True
@classmethod
def remove_by_mountpoint(cls, mountpoint, path=None):
fstab = cls(path=path)
entry = fstab.get_entry_by_attr('mountpoint', mountpoint)
if entry:
return fstab.remove_entry(entry)
return False
@classmethod
def add(cls, device, mountpoint, filesystem, options=None, path=None):
return cls(path=path).add_entry(Fstab.Entry(device,
mountpoint, filesystem,
options=options))

View File

@@ -17,6 +17,7 @@ import apt_pkg
from collections import OrderedDict
from hookenv import log
from fstab import Fstab
def service_start(service_name):
@@ -35,7 +36,8 @@ def service_restart(service_name):
def service_reload(service_name, restart_on_failure=False):
"""Reload a system service, optionally falling back to restart if reload fails"""
"""Reload a system service, optionally falling back to restart if
reload fails"""
service_result = service('reload', service_name)
if not service_result and restart_on_failure:
service_result = service('restart', service_name)
@@ -144,7 +146,19 @@ def write_file(path, content, owner='root', group='root', perms=0444):
target.write(content)
def mount(device, mountpoint, options=None, persist=False):
def fstab_remove(mp):
"""Remove the given mountpoint entry from /etc/fstab
"""
return Fstab.remove_by_mountpoint(mp)
def fstab_add(dev, mp, fs, options=None):
"""Adds the given device entry to the /etc/fstab file
"""
return Fstab.add(dev, mp, fs, options=options)
def mount(device, mountpoint, options=None, persist=False, filesystem="ext3"):
"""Mount a filesystem at a particular mountpoint"""
cmd_args = ['mount']
if options is not None:
@@ -155,9 +169,9 @@ def mount(device, mountpoint, options=None, persist=False):
except subprocess.CalledProcessError, e:
log('Error mounting {} at {}\n{}'.format(device, mountpoint, e.output))
return False
if persist:
# TODO: update fstab
pass
return fstab_add(device, mountpoint, filesystem, options=options)
return True
@@ -169,9 +183,9 @@ def umount(mountpoint, persist=False):
except subprocess.CalledProcessError, e:
log('Error unmounting {}\n{}'.format(mountpoint, e.output))
return False
if persist:
# TODO: update fstab
pass
return fstab_remove(mountpoint)
return True

View File

@@ -39,7 +39,8 @@ class BzrUrlFetchHandler(BaseFetchHandler):
def install(self, source):
url_parts = self.parse_url(source)
branch_name = url_parts.path.strip("/").split("/")[-1]
dest_dir = os.path.join(os.environ.get('CHARM_DIR'), "fetched", branch_name)
dest_dir = os.path.join(os.environ.get('CHARM_DIR'), "fetched",
branch_name)
if not os.path.exists(dest_dir):
mkdir(dest_dir, perms=0755)
try:

View File

@@ -124,7 +124,7 @@ class CeilometerHooksTest(CharmTestCase):
service=hooks.CEILOMETER_SERVICE,
public_url=url, admin_url=url, internal_url=url,
requested_roles=hooks.CEILOMETER_ROLE,
region='myregion')
region='myregion', relation_id=None)
def test_ceilometer_joined(self):
self.relation_ids.return_value = ['ceilometer:0']