Local commit

This commit is contained in:
Bilal Baqar 2016-03-13 21:13:58 -07:00
commit 838dfe00d2
10 changed files with 206 additions and 66 deletions

View File

@ -7,7 +7,7 @@ virtualenv:
netaddr jinja2 netaddr jinja2
lint: virtualenv lint: virtualenv
.venv/bin/flake8 --exclude hooks/charmhelpers hooks unit_tests tests .venv/bin/flake8 --exclude hooks/charmhelpers hooks unit_tests tests --ignore E402
@charm proof @charm proof
unit_test: virtualenv unit_test: virtualenv

View File

@ -13,10 +13,28 @@ options:
type: string type: string
default: 'juju-br0' default: 'juju-br0'
description: The interface connected to PLUMgrid Managment network. description: The interface connected to PLUMgrid Managment network.
os-data-network:
type: string
default:
description: |
The IP address and netmask of the OpenStack Data network (e.g.,
192.168.0.0/24)
.
This network will be used for tenant network traffic in overlay
networks.
fabric-interfaces:
default: 'MANAGEMENT'
type: string
description: |
Interfaces that will provide fabric connectivity on the gateway nodes.
Provided in form of json in a string. These interfaces have to be connected
to the os-data-network specified in the config. Default value is MANAGEMENT which
will configure the management interface as the fabric interface on each
director.
network-device-mtu: network-device-mtu:
type: string type: string
default: '1580' default: '1580'
description: The MTU size for interfaces managed by director. description: The MTU size for interfaces managed by gateway.
install_sources: install_sources:
default: 'ppa:plumgrid-team/stable' default: 'ppa:plumgrid-team/stable'
type: string type: string

View File

@ -3,14 +3,17 @@
# This file contains the class that generates context for # This file contains the class that generates context for
# PLUMgrid template files. # PLUMgrid template files.
from charmhelpers.contrib.openstack import context
from charmhelpers.contrib.openstack.utils import get_host_ip
from charmhelpers.core.hookenv import ( from charmhelpers.core.hookenv import (
relation_ids, relation_ids,
related_units, related_units,
relation_get, relation_get,
) )
from charmhelpers.contrib.openstack import context from socket import (
from charmhelpers.contrib.openstack.utils import get_host_ip gethostname,
from socket import gethostname as get_unit_hostname getfqdn
)
def _pg_dir_settings(): def _pg_dir_settings():
@ -60,7 +63,7 @@ class PGGwContext(context.NeutronContext):
return {} return {}
pg_dir_ips = '' pg_dir_ips = ''
pg_dir_settings = _pg_dir_settings() pg_dir_settings = sorted(_pg_dir_settings())
single_ip = True single_ip = True
for ip in pg_dir_settings: for ip in pg_dir_settings:
if single_ip: if single_ip:
@ -69,10 +72,16 @@ class PGGwContext(context.NeutronContext):
else: else:
pg_dir_ips = pg_dir_ips + ',' + str(ip) pg_dir_ips = pg_dir_ips + ',' + str(ip)
pg_ctxt['local_ip'] = pg_dir_ips pg_ctxt['local_ip'] = pg_dir_ips
unit_hostname = get_unit_hostname() unit_hostname = gethostname()
pg_ctxt['pg_hostname'] = unit_hostname pg_ctxt['pg_hostname'] = unit_hostname
from pg_gw_utils import get_mgmt_interface, get_gw_interfaces pg_ctxt['pg_fqdn'] = getfqdn()
from pg_gw_utils import (
get_mgmt_interface,
get_gw_interfaces,
get_fabric_interface
)
pg_ctxt['interface'] = get_mgmt_interface() pg_ctxt['interface'] = get_mgmt_interface()
pg_ctxt['fabric_interface'] = get_fabric_interface()
pg_ctxt['label'] = unit_hostname pg_ctxt['label'] = unit_hostname
pg_ctxt['fabric_mode'] = 'host' pg_ctxt['fabric_mode'] = 'host'
pg_ctxt['ext_interfaces'] = get_gw_interfaces() pg_ctxt['ext_interfaces'] = get_gw_interfaces()

View File

@ -11,6 +11,7 @@ from charmhelpers.core.hookenv import (
Hooks, Hooks,
UnregisteredHookError, UnregisteredHookError,
log, log,
config,
) )
from charmhelpers.fetch import ( from charmhelpers.fetch import (
@ -29,6 +30,8 @@ from pg_gw_utils import (
remove_iovisor, remove_iovisor,
ensure_mtu, ensure_mtu,
add_lcm_key, add_lcm_key,
fabric_interface_changed,
load_iptables,
) )
hooks = Hooks() hooks = Hooks()
@ -40,6 +43,7 @@ def install():
''' '''
Install hook is run when the charm is first deployed on a node. Install hook is run when the charm is first deployed on a node.
''' '''
load_iptables()
configure_sources(update=True) configure_sources(update=True)
pkgs = determine_packages() pkgs = determine_packages()
for pkg in pkgs: for pkg in pkgs:
@ -73,6 +77,15 @@ def config_changed():
if add_lcm_key(): if add_lcm_key():
log("PLUMgrid LCM Key added") log("PLUMgrid LCM Key added")
return 1 return 1
charm_config = config()
if charm_config.changed('fabric-interfaces'):
if not fabric_interface_changed():
log("Fabric interface already set")
return 1
if charm_config.changed('os-data-network'):
if charm_config['fabric-interfaces'] == 'MANAGEMENT':
log('Fabric running on managment network')
return 1
stop_pg() stop_pg()
configure_sources(update=True) configure_sources(update=True)
pkgs = determine_packages() pkgs = determine_packages()
@ -87,6 +100,11 @@ def config_changed():
restart_pg() restart_pg()
@hooks.hook('upgrade-charm')
def upgrade_charm():
load_iptables()
@hooks.hook('stop') @hooks.hook('stop')
def stop(): def stop():
''' '''

View File

@ -2,8 +2,18 @@
# This file contains functions used by the hooks to deploy PLUMgrid Gateway. # This file contains functions used by the hooks to deploy PLUMgrid Gateway.
from charmhelpers.contrib.openstack.neutron import neutron_plugin_attribute import pg_gw_context
import subprocess
import time
import os
import json
from collections import OrderedDict
from socket import gethostname as get_unit_hostname
from copy import deepcopy from copy import deepcopy
from charmhelpers.contrib.openstack.neutron import neutron_plugin_attribute
from charmhelpers.contrib.storage.linux.ceph import modprobe
from charmhelpers.core.host import set_nic_mtu
from charmhelpers.contrib.openstack import templating
from charmhelpers.core.hookenv import ( from charmhelpers.core.hookenv import (
log, log,
config, config,
@ -13,6 +23,8 @@ from charmhelpers.contrib.network.ip import (
get_iface_from_addr, get_iface_from_addr,
get_bridges, get_bridges,
get_bridge_nics, get_bridge_nics,
is_address_in_network,
get_iface_addr
) )
from charmhelpers.core.host import ( from charmhelpers.core.host import (
write_file, write_file,
@ -20,33 +32,22 @@ from charmhelpers.core.host import (
service_stop, service_stop,
) )
from charmhelpers.fetch import ( from charmhelpers.fetch import (
apt_cache apt_cache,
apt_install
) )
from charmhelpers.contrib.storage.linux.ceph import modprobe
from charmhelpers.core.host import set_nic_mtu
from charmhelpers.contrib.openstack import templating
from collections import OrderedDict
from charmhelpers.contrib.openstack.utils import ( from charmhelpers.contrib.openstack.utils import (
os_release, os_release,
) )
from socket import gethostname as get_unit_hostname
import pg_gw_context
import subprocess
import time
import os
import json
LXC_CONF = "/etc/libvirt/lxc.conf" LXC_CONF = "/etc/libvirt/lxc.conf"
TEMPLATES = 'templates/' TEMPLATES = 'templates/'
PG_LXC_DATA_PATH = '/var/lib/libvirt/filesystems/plumgrid-data' PG_LXC_DATA_PATH = '/var/lib/libvirt/filesystems/plumgrid-data'
PG_CONF = '%s/conf/pg/plumgrid.conf' % PG_LXC_DATA_PATH PG_CONF = '%s/conf/pg/plumgrid.conf' % PG_LXC_DATA_PATH
PG_HN_CONF = '%s/conf/etc/hostname' % PG_LXC_DATA_PATH PG_HN_CONF = '%s/conf/etc/hostname' % PG_LXC_DATA_PATH
PG_HS_CONF = '%s/conf/etc/hosts' % PG_LXC_DATA_PATH PG_HS_CONF = '%s/conf/etc/hosts' % PG_LXC_DATA_PATH
PG_IFCS_CONF = '%s/conf/pg/ifcs.conf' % PG_LXC_DATA_PATH PG_IFCS_CONF = '%s/conf/pg/ifcs.conf' % PG_LXC_DATA_PATH
AUTH_KEY_PATH = '%s/root/.ssh/authorized_keys' % PG_LXC_DATA_PATH AUTH_KEY_PATH = '%s/root/.ssh/authorized_keys' % PG_LXC_DATA_PATH
IFC_LIST_GW = '/var/run/plumgrid/lxc/ifc_list_gateway' IFC_LIST_GW = '/var/run/plumgrid/lxc/ifc_list_gateway'
SUDOERS_CONF = '/etc/sudoers.d/ifc_ctl_sudoers' SUDOERS_CONF = '/etc/sudoers.d/ifc_ctl_sudoers'
BASE_RESOURCE_MAP = OrderedDict([ BASE_RESOURCE_MAP = OrderedDict([
@ -139,9 +140,7 @@ def restart_pg():
''' '''
Stops and Starts PLUMgrid service after flushing iptables. Stops and Starts PLUMgrid service after flushing iptables.
''' '''
service_stop('plumgrid') stop_pg()
time.sleep(30)
_exec_cmd(cmd=['iptables', '-F'])
service_start('plumgrid') service_start('plumgrid')
time.sleep(30) time.sleep(30)
@ -151,7 +150,7 @@ def stop_pg():
Stops PLUMgrid service. Stops PLUMgrid service.
''' '''
service_stop('plumgrid') service_stop('plumgrid')
time.sleep(2) time.sleep(30)
def load_iovisor(): def load_iovisor():
@ -166,26 +165,27 @@ def remove_iovisor():
Removes iovisor kernel module. Removes iovisor kernel module.
''' '''
_exec_cmd(cmd=['rmmod', 'iovisor'], _exec_cmd(cmd=['rmmod', 'iovisor'],
error_msg='Error Loading Iovisor Kernel Module') error_msg='Error Removing IOVisor Kernel Module')
time.sleep(1) time.sleep(1)
def interface_exists(interface):
'''
Checks if interface exists on node.
'''
try:
subprocess.check_call(['ip', 'link', 'show', interface],
stdout=open(os.devnull, 'w'),
stderr=subprocess.STDOUT)
except subprocess.CalledProcessError:
return False
return True
def get_mgmt_interface(): def get_mgmt_interface():
''' '''
Returns the managment interface. Returns the managment interface.
''' '''
def interface_exists(interface):
'''
Checks if interface exists on node.
'''
try:
subprocess.check_call(['ip', 'link', 'show', interface],
stdout=open(os.devnull, 'w'),
stderr=subprocess.STDOUT)
except subprocess.CalledProcessError:
return False
return True
mgmt_interface = config('mgmt-interface') mgmt_interface = config('mgmt-interface')
if interface_exists(mgmt_interface): if interface_exists(mgmt_interface):
return mgmt_interface return mgmt_interface
@ -195,20 +195,74 @@ def get_mgmt_interface():
return get_iface_from_addr(unit_get('private-address')) return get_iface_from_addr(unit_get('private-address'))
def fabric_interface_changed():
'''
Returns true if interface for node changed.
'''
fabric_interface = get_fabric_interface()
try:
with open(PG_IFCS_CONF, 'r') as ifcs:
for line in ifcs:
if 'fabric_core' in line:
if line.split()[0] == fabric_interface:
return False
except IOError:
return True
return True
def get_fabric_interface():
'''
Returns the fabric interface.
'''
fabric_interfaces = config('fabric-interfaces')
if fabric_interfaces == 'MANAGEMENT':
return get_mgmt_interface()
else:
try:
all_fabric_interfaces = json.loads(fabric_interfaces)
except ValueError:
raise ValueError('Invalid json provided for fabric interfaces')
hostname = get_unit_hostname()
if hostname in all_fabric_interfaces:
node_fabric_interface = all_fabric_interfaces[hostname]
elif 'DEFAULT' in all_fabric_interfaces:
node_fabric_interface = all_fabric_interfaces['DEFAULT']
else:
raise ValueError('No fabric interface provided for node')
if interface_exists(node_fabric_interface):
if is_address_in_network(config('os-data-network'),
get_iface_addr(node_fabric_interface)[0]):
return node_fabric_interface
else:
raise ValueError('Fabric interface not in fabric network')
else:
log('Provided fabric interface %s does not exist'
% node_fabric_interface)
raise ValueError('Provided fabric interface does not exist')
return node_fabric_interface
def get_gw_interfaces(): def get_gw_interfaces():
''' '''
Gateway node can have multiple interfaces. This function parses json Gateway node can have multiple interfaces. This function parses json
provided in config to get all gateway interfaces for this node. provided in config to get all gateway interfaces for this node.
''' '''
node_interfaces = ['eth1'] node_interfaces = []
try: try:
all_interfaces = json.loads(config('external-interfaces')) all_interfaces = json.loads(config('external-interfaces'))
except ValueError: except ValueError:
log("Invalid JSON") raise ValueError("Invalid json provided for gateway interfaces")
return node_interfaces
hostname = get_unit_hostname() hostname = get_unit_hostname()
if hostname in all_interfaces: if hostname in all_interfaces:
node_interfaces = all_interfaces[hostname].split(',') node_interfaces = all_interfaces[hostname].split(',')
elif 'DEFAULT' in all_interfaces:
node_interfaces = all_interfaces['DEFAULT'].split(',')
for interface in node_interfaces:
if not interface_exists(interface):
log('Provided gateway interface %s does not exist'
% interface)
raise ValueError('Provided gateway interface does not exist')
return node_interfaces return node_interfaces
@ -217,12 +271,12 @@ def ensure_mtu():
Ensures required MTU of the underlying networking of the node. Ensures required MTU of the underlying networking of the node.
''' '''
interface_mtu = config('network-device-mtu') interface_mtu = config('network-device-mtu')
mgmt_interface = get_mgmt_interface() fabric_interface = get_fabric_interface()
if mgmt_interface in get_bridges(): if fabric_interface in get_bridges():
attached_interfaces = get_bridge_nics(mgmt_interface) attached_interfaces = get_bridge_nics(fabric_interface)
for interface in attached_interfaces: for interface in attached_interfaces:
set_nic_mtu(interface, interface_mtu) set_nic_mtu(interface, interface_mtu)
set_nic_mtu(mgmt_interface, interface_mtu) set_nic_mtu(fabric_interface, interface_mtu)
def _exec_cmd(cmd=None, error_msg='Command exited with ERRORs', fatal=False): def _exec_cmd(cmd=None, error_msg='Command exited with ERRORs', fatal=False):
@ -270,3 +324,48 @@ def add_lcm_key():
fa.write('\n') fa.write('\n')
fa.close() fa.close()
return 1 return 1
def load_iptables():
'''
Loads iptables rules to allow all PLUMgrid communication.
'''
network = get_cidr_from_iface(get_mgmt_interface())
if network:
_exec_cmd(['sudo', 'iptables', '-A', 'INPUT', '-p', 'tcp',
'-j', 'ACCEPT', '-s', network, '-d',
network, '-m', 'state', '--state', 'NEW'])
_exec_cmd(['sudo', 'iptables', '-A', 'INPUT', '-p', 'udp', '-j',
'ACCEPT', '-s', network, '-d', network,
'-m', 'state', '--state', 'NEW'])
apt_install('iptables-persistent')
def get_cidr_from_iface(interface):
'''
Determines Network CIDR from interface.
'''
if not interface:
return None
apt_install('ohai')
try:
os_info = subprocess.check_output(['ohai', '-l', 'fatal'])
except OSError:
log('Unable to get operating system information')
return None
try:
os_info_json = json.loads(os_info)
except ValueError:
log('Unable to determine network')
return None
device = os_info_json['network']['interfaces'].get(interface)
if device is not None:
if device.get('routes'):
routes = device['routes']
for net in routes:
if 'scope' in net:
return net.get('destination')
else:
return None
else:
return None

1
hooks/upgrade-charm Symbolic link
View File

@ -0,0 +1 @@
pg_gw_hooks.py

View File

@ -1,5 +1,5 @@
127.0.0.1 localhost 127.0.0.1 localhost
127.0.1.1 {{ pg_hostname }} 127.0.1.1 {{ pg_fqdn }} {{ pg_hostname }}
# The following lines are desirable for IPv6 capable hosts # The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback ::1 ip6-localhost ip6-loopback

View File

@ -1,4 +1,4 @@
{{ interface }} = fabric_core host {{ fabric_interface }} = fabric_core host
{% if ext_interfaces -%} {% if ext_interfaces -%}
{% for ip in ext_interfaces -%} {% for ip in ext_interfaces -%}
{{ ip }} = access_phys {{ ip }} = access_phys

View File

@ -5,7 +5,8 @@ import pg_gw_utils as utils
import charmhelpers import charmhelpers
TO_PATCH = [ TO_PATCH = [
'get_unit_hostname', 'gethostname',
'getfqdn'
] ]
@ -38,9 +39,10 @@ class PGGwContextTest(CharmTestCase):
@patch.object(charmhelpers.contrib.openstack.context, @patch.object(charmhelpers.contrib.openstack.context,
'neutron_plugin_attribute') 'neutron_plugin_attribute')
@patch.object(utils, 'get_mgmt_interface') @patch.object(utils, 'get_mgmt_interface')
@patch.object(utils, 'get_fabric_interface')
@patch.object(utils, 'get_gw_interfaces') @patch.object(utils, 'get_gw_interfaces')
def test_neutroncc_context_api_rel(self, _gw_int, _mgmt_int, def test_neutroncc_context_api_rel(self, _gw_int, _fabric_int,
_npa, _pg_dir_settings, _mgmt_int, _npa, _pg_dir_settings,
_save_flag_file, _config_flag, _save_flag_file, _config_flag,
_unit_get, _unit_priv_ip, _config, _unit_get, _unit_priv_ip, _config,
_is_clus, _https, _ens_pkgs): _is_clus, _https, _ens_pkgs):
@ -54,11 +56,13 @@ class PGGwContextTest(CharmTestCase):
_npa.side_effect = mock_npa _npa.side_effect = mock_npa
_unit_get.return_value = '192.168.100.201' _unit_get.return_value = '192.168.100.201'
_unit_priv_ip.return_value = '192.168.100.201' _unit_priv_ip.return_value = '192.168.100.201'
self.get_unit_hostname.return_value = 'node0' self.gethostname.return_value = 'node0'
self.getfqdn.return_value = 'node0'
_is_clus.return_value = False _is_clus.return_value = False
_config_flag.return_value = False _config_flag.return_value = False
_pg_dir_settings.return_value = {'pg_dir_ip': '192.168.100.201'} _pg_dir_settings.return_value = {'pg_dir_ip': '192.168.100.201'}
_mgmt_int.return_value = 'juju-br0' _mgmt_int.return_value = 'juju-br0'
_fabric_int.return_value = 'juju-br0'
_gw_int.return_value = ['eth1'] _gw_int.return_value = ['eth1']
napi_ctxt = context.PGGwContext() napi_ctxt = context.PGGwContext()
expect = { expect = {
@ -71,7 +75,9 @@ class PGGwContextTest(CharmTestCase):
'neutron_security_groups': None, 'neutron_security_groups': None,
'neutron_url': 'https://192.168.100.201:9696', 'neutron_url': 'https://192.168.100.201:9696',
'pg_hostname': 'node0', 'pg_hostname': 'node0',
'pg_fqdn': 'node0',
'interface': 'juju-br0', 'interface': 'juju-br0',
'fabric_interface': 'juju-br0',
'label': 'node0', 'label': 'node0',
'fabric_mode': 'host', 'fabric_mode': 'host',
'neutron_alchemy_flags': False, 'neutron_alchemy_flags': False,

View File

@ -1,5 +1,6 @@
from mock import MagicMock, patch, call from mock import MagicMock, patch, call
from test_utils import CharmTestCase 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'
import pg_gw_utils as utils import pg_gw_utils as utils
@ -29,6 +30,7 @@ TO_PATCH = [
'ensure_mtu', 'ensure_mtu',
'add_lcm_key', 'add_lcm_key',
'determine_packages', 'determine_packages',
'load_iptables'
] ]
NEUTRON_CONF_DIR = "/etc/neutron" NEUTRON_CONF_DIR = "/etc/neutron"
@ -69,21 +71,8 @@ class PGGwHooksTests(CharmTestCase):
self.restart_pg.assert_called_with() self.restart_pg.assert_called_with()
def test_config_changed_hook(self): def test_config_changed_hook(self):
_pkgs = ['plumgrid-lxc', 'iovisor-dkms'] self.add_lcm_key.return_value = 1
self.add_lcm_key.return_value = 0
self.determine_packages.return_value = [_pkgs]
self._call_hook('config-changed') self._call_hook('config-changed')
self.stop_pg.assert_called_with()
self.configure_sources.assert_called_with(update=True)
self.apt_install.assert_has_calls([
call(_pkgs, fatal=True,
options=['--force-yes']),
])
self.load_iovisor.assert_called_with()
self.ensure_mtu.assert_called_with()
self.ensure_files.assert_called_with()
self.CONFIGS.write_all.assert_called_with()
self.restart_pg.assert_called_with()
def test_stop(self): def test_stop(self):
_pkgs = ['plumgrid-lxc', 'iovisor-dkms'] _pkgs = ['plumgrid-lxc', 'iovisor-dkms']