Baseline eod pre-UDS

This commit is contained in:
James Page 2012-10-26 17:56:42 +02:00
commit 89a83cd8df
18 changed files with 753 additions and 0 deletions

17
.project Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>quantum</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.python.pydev.PyDevBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.python.pydev.pythonNature</nature>
</natures>
</projectDescription>

10
.pydevproject Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?eclipse-pydev version="1.0"?>
<pydev_project>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
<path>/quantum/hooks</path>
</pydev_pathproperty>
</pydev_project>

36
config.yaml Normal file
View File

@ -0,0 +1,36 @@
options:
plugin:
default: ovs
type: string
description: |
Network configuration plugin to use to manage
the quantum network across quantum and nova-compute
nodes. Supported values include:
.
ovs - OpenVSwitch
nvp - Nicera
ext-port:
type: string
description: |
External port to use for routing of instance
traffic to the external public network.
source:
type: string
description: |
Optional configuration to support use of additional sources such as:
.
- ppa:myteam/ppa
- cloud:folsom-proposed
- http://my.archive.com/ubuntu main
.
The last option should be used in conjunction with the key configuration
option.
.
Note that a minimum ceph version of 0.48.2 is required for use with this
charm which is NOT provided by the packages in the main Ubuntu archive
for precise.
key:
type: string
description: |
Key ID to import to the apt keyring to support use with arbitary source
configuration from outside of Launchpad archives or PPA's.

1
hooks/config-changed Symbolic link
View File

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

129
hooks/hooks.py Executable file
View File

@ -0,0 +1,129 @@
#!/usr/bin/python
import utils
import sys
import quantum_utils
PLUGIN_PKGS = {
"ovs": [ # TODO: Assumes Quantum Provider Gateway
"quantum-plugin-openvswitch",
"quantum-plugin-openvswitch-agent",
"quantum-l3-agent",
"quantum-dhcp-agent"
],
"nvp": ["quantum-plugin-nicira"] # No agent required
}
def install():
utils.configure_source()
# TODO: when using the nicira plugin /etc/default/quantum-server
# will also need to be updated to point to the correct configuration
plugin = utils.config_get('plugin')
if plugin in PLUGIN_PKGS.keys():
if plugin == "ovs":
# Install OVS DKMS first to ensure that the ovs module
# loaded supports GRE tunnels
utils.install('openvswitch-datapath-dkms')
utils.install(['quantum-server'].extend(PLUGIN_PKGS[plugin]))
else:
utils.juju_log('ERROR', 'Please provide a valid plugin config')
sys.exit(1)
def config_changed():
plugin = utils.config_get('plugin')
if plugin in PLUGIN_PKGS.keys():
if plugin == "ovs":
# TODO: Defaults to Quantum Provider Router
quantum_utils.add_bridge('br-int')
quantum_utils.add_bridge('br-ext')
ext_port = utils.config_get('ext-port')
if ext_port:
quantum_utils.add_bridge_port('br-ex', ext_port)
quantum_utils.configure_core_plugin(plugin)
quantum_utils.configure_local_ip(plugin,
utils.unit_get('private-address'))
else:
utils.juju_log('ERROR',
'Please provide a valid plugin config')
sys.exit(1)
def keystone_joined():
url = "http://{}:9696/".format(utils.unit_get('private-address'))
utils.relation_set(service="quantum",
region="RegionOne",
public_url=url,
admin_url=url,
internal_url=url)
def keystone_changed():
token = utils.relation_get('admin_token')
service_port = utils.relation_get('service_port')
auth_port = utils.relation_get('auth_port')
service_username = utils.relation_get('service_username')
service_password = utils.relation_get('service_password')
service_tenant = utils.relation_get('service_tenant')
if not (token and
service_port and
auth_port and
service_username and
service_password and
service_tenant):
utils.juju_log('INFO',
'keystone peer not ready yet')
return
if token == "-1":
utils.juju_log('ERROR',
'Admin token error')
sys.exit(1)
keystone_host = utils.relation_get('private-address')
utils.juju_log('INFO', 'Configuring quantum for keystone authentication')
quantum_utils.configure_keystone(keystone_host,
token,
service_port,
auth_port,
service_username,
service_password,
service_tenant)
def db_joined():
utils.relation_set(username=quantum_utils.DB_USER,
database=quantum_utils.QUANTUM_DB,
hostname=utils.unit_get('private-address'))
def db_changed():
host = utils.relation_get('private-address')
password = utils.relation_get('password')
if not (host and password):
return
else:
quantum_utils.configure_db_connection(utils.config_get('plugin'),
host, password)
def amqp_joined():
pass
def amqp_changed():
pass
utils.do_hooks({
"install": install,
"config-changed": config_changed,
"identity-service-relation-joined": keystone_joined,
"identity-service-relation-changed": keystone_changed,
"shared-db-relation-joined": db_joined,
"shared-db-relation-changed": db_changed,
"amqp-relation-joined": amqp_joined,
"amqp-relation-changed": amqp_changed
})
sys.exit(0)

View File

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

View File

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

1
hooks/install Symbolic link
View File

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

47
hooks/packages Normal file
View File

@ -0,0 +1,47 @@
Quantum Server
==============
quantum-server
# OVS
quantum-plugin-openvswitch quantum-plugin-openvswitch-agent
# Configure connection to the database
-> store in sql_connection in plugins.
# OVS
/etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini
enable_tunneling=True
tenant_network_type=gre
tunnel_id_ranges=1:1000
# only if node is running the agent
local_ip=$(unit-get private-address) # data network
service quantum-server restart
# Add internal bridge
ovs-vsctl add-br br-int
# restart openvswitch plugin
# Same everywhere
quantum-dhcp-agent
quantum-l3-agent
ovs-vsctl add-br br-ex
ovs-vsctl add-port br-ex $(config-get external-port)
# Keystone - need to review keystone charm to see how this works.
keystone service-create --name quantum --type network --description 'OpenStack Networking Service'
keystone endpoint-create --region $REGION --service-id $ID --publicurl \
'http://$IP:9696/' --adminurl 'http://$IP:9696/' --internalurl 'http://$IP:9696/'
keystone user-create --name=quantum --pass=$SERVICE_PASSWORD --tenant-id service
keystone user-role-add --user_id 45e9461fa61e48f99de1adcd0b38eae7 --role_id e45af7cf33be4dac8070aa8310144ce3 \
--tenant_id 950fe8e5ed5f4659a8556ac836e8943d

131
hooks/quantum-hooks Executable file
View File

@ -0,0 +1,131 @@
#!/bin/bash -e
# vim: set ts=2:set et:
[ -f hooks/openstack-common ] && . hooks/openstack-common
ARG0=${0##*/}
PLUGIN=$(config-get plugin)
EXT_PORT=$(config-get ext-port)
PRIVATE_ADDRESS=$(unit-get private-address)
DB_USER=quantum
QUANTUM_DB=quantum
OVS_PLUGIN_CONF="/etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini"
NVP_PLUGIN_CONF="/etc/quantum/plugins/nicira/nvp.ini"
case $PLUGIN in
"ovs") PLUGIN_CONF=$OVS_PLUGIN_CONF ;;
"nvp") PLUGIN_CONF=$NVP_PLUGIN_CONF ;;
esac
function install_hook() {
case $PLUGIN in
"ovs")
PLUGIN_PKGS="quantum-plugin-openvswitch quantum-plugin-openvswitch-agent"
;;
"nvp")
# XXX implement nvp
;;
*)
juju-log "Unsupported plugin specified"
exit 1
;;
esac
apt-get install quantum-server $PLUGIN_PKGS \
quantum-dhcp-agent \
quantum-l3-agent \
openvswitch-datapath-dkms
ovs-vsctl add-br br-int
ovs-vsctl add-br br-ex
}
function config_changed_hook() {
ovs-vsctl add-br br-int
ovs-vsctl add-br br-ex
if [ -n "$EXT_PORT" ]; then
juju-log "Adding $EXT_PORT to external bridge for external access"
ovs-vsctl add-port br-ex $EXT_PORT
fi
case $PLUGIN in
"ovs")
CORE_PLUGIN="quantum.plugins.openvswitch.ovs_quantum_plugin.OVSQuantumPluginV2"
;;
"nvp")
CORE_PLUGIN="quantum.plugins.nicira.nicira_nvp_plugin.QuantumPlugin.NvpPluginV2"
;;
esac
juju-log "Configuring quantum with core_plugin: $CORE_PLUGIN"
set_or_update core_plugin $CORE_PLUGIN /etc/quantum/quantum.conf
}
function start() {
}
function stop() {
}
function keystone_joined {
# advertise our API endpoint to keystone
url="http://$(unit-get private-address):9696/v1"
public_url="http://$(unit-get public-address):9696/v1"
relation-set service="quantum" \
region="RegionOne" public_url=$public_url admin_url=$url internal_url=$url
}
function keystone_changed {
# we hopefully get a token in return. configure middleware accordingly
token=$(relation-get admin_token)
service_port=$(relation-get service_port)
auth_port=$(relation-get auth_port)
service_username=$(relation-get service_username)
service_password=$(relation-get service_password)
service_tenant=$(relation-get service_tenant)
[[ -z "$token" ]] || [[ -z "$service_port" ]] || [[ -z "$auth_port" ]] ||
[[ -z "$service_username" ]] || [[ -z "$service_password" ]] ||
[[ -z "$service_tenant" ]] && juju-log "Peer not ready" &&
exit 0
[[ "$token" == "-1" ]] &&
juju-log "admin token error" && exit 1
juju-log "Acquired admin. token"
keystone_host=$(relation-get private-address)
}
function db_joined {
juju-log "Requesting access to $QUANTUM_DB for $DB_USER@$PRIVATE_ADDRESS"
relation-set database=$QUANTUM_DB username=$DB_USER hostname=$PRIVATE_ADDRESS
}
function db_changed {
DB_HOST=$(relation-get private-address)
DB_PASSWORD=$(relation-get password)
if [ -z "$DB_HOST" ] || [ -z "$DB_PASSWORD" ] ; then
echo "DB_HOST || DB_PASSWORD not yet set. Exit 0 and retry"
exit 0
else
echo "Received password from $DB_HOST"
fi
juju-log "Configuring registry for access to $QUANTUM_DB@$DB_HOST"
set_or_update sql_connection \
"mysql://$DB_USER:$DB_PASSWORD@$DB_HOST/$QUANTUM_DB?charset=utf8" \
$PLUGIN_CONF
}
case $ARG0 in
"install") install_hook ;;
"config-changed") config_changed_hook ;;
"identity-service-relation-joined") keystone_joined ;;
"identity-service-relation-changed") keystone_changed ;;
"shared-db-relation-joined") db_joined ;;
"shared-db-relation-changed") db_changed;;
esac
# TODO: Add hooks for rabbitmq integration - dhcpagent seems to require:
# /etc/quantum/quantum.conf:
# rabbit_password = openstack
# rabbit_host = 100.1.1.10

163
hooks/quantum_utils.py Normal file
View File

@ -0,0 +1,163 @@
import utils
import subprocess
import os
OVS_PLUGIN = \
"quantum.plugins.openvswitch.ovs_quantum_plugin.OVSQuantumPluginV2"
NVP_PLUGIN = \
"quantum.plugins.nicira.nicira_nvp_plugin.QuantumPlugin.NvpPluginV2"
CORE_PLUGIN = {
"ovs": OVS_PLUGIN,
"nvp": NVP_PLUGIN
}
OVS_PLUGIN_CONF = \
"/etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini"
NVP_PLUGIN_CONF = \
"/etc/quantum/plugins/nicira/nvp.ini"
PLUGIN_CONF = {
"ovs": OVS_PLUGIN_CONF,
"nvp": NVP_PLUGIN_CONF
}
DB_USER = "quantum"
QUANTUM_DB = "quantum"
QUANTUM_CONF = "/etc/quantum/quantum.conf"
L3_AGENT_CONF = "/etc/quantum/l3_agent.ini"
QUANTUM_API_CONF = "/etc/quantum/api-paste.ini"
MYSQL_CS = "mysql://%(user)s:%(password)s@%(host)s/%(db)s?charset=utf8"
def update_config_block(block, conf, **kwargs):
"""
Updates configuration file blocks given kwargs.
Can be used to update driver settings for a particular backend,
setting the sql connection, etc.
Parses block heading as '[block]'
If block does not exist, a new block will be created at end of file with
given kwargs
"""
f = open(conf, "r+")
orig = f.readlines()
new = []
heading = "[{}]\n".format(block)
lines = len(orig)
ln = 0
def update_block(block):
for k, v in kwargs.iteritems():
for l in block:
if l.strip().split(" ")[0] == k:
block[block.index(l)] = "{} = {}\n".format(k, v)
return
block.append('{} = {}\n'.format(k, v))
block.append('\n')
found = False
while ln < lines:
if orig[ln] != heading:
new.append(orig[ln])
ln += 1
else:
new.append(orig[ln])
ln += 1
block = []
while orig[ln].strip() != '':
block.append(orig[ln])
ln += 1
update_block(block)
new += block
found = True
if not found:
if new[(len(new) - 1)].strip() != '':
new.append('\n')
new.append('{}'.format(heading))
for k, v in kwargs.iteritems():
new.append('{} = {}\n'.format(k, v))
new.append('\n')
# backup original config
backup = open(conf + '.juju-back', 'w+')
for l in orig:
backup.write(l)
backup.close()
# update config
f.seek(0)
f.truncate()
for l in new:
f.write(l)
def configure_core_plugin(plugin):
update_config_block("DEFAULT", QUANTUM_CONF,
core_plugin=CORE_PLUGIN[plugin])
def configure_db_connection(plugin, host, password):
update_config_block(
"DATABASE", PLUGIN_CONF[plugin],
sql_connection=MYSQL_CS.format(host=host,
user=DB_USER,
password=password,
db=QUANTUM_DB)
)
def configure_local_ip(plugin, address):
update_config_block("OVS", PLUGIN_CONF[plugin], local_ip=address)
def configure_keystone(keystone_host,
token,
service_port,
auth_port,
username,
password,
tenant):
if os.path.exists(L3_AGENT_CONF): # Indicated OVS model is in use.
update_config_block("DEFAULT", L3_AGENT_CONF,
auth_url="http://{}:{}/v2.0".format(keystone_host,
auth_port),
auth_region="RegionOne",
admin_tenant_name=tenant,
admin_user=username,
admin_password=password)
update_config_block("filter:authtoken", QUANTUM_API_CONF,
auth_host=keystone_host,
auth_port=auth_port,
admin_tenant_name=tenant,
admin_user=username,
admin_password=password)
def add_bridge(name):
status = subprocess.check_output(["ovs-vsctl", "show"])
if "Bridge {}".format(name) not in status:
subprocess.check_call(["ovs-vsctl", "add-br", name])
def del_bridge(name):
status = subprocess.check_output(["ovs-vsctl", "show"])
if "Bridge {}".format(name) in status:
subprocess.check_call(["ovs-vsctl", "del-br", name])
def add_bridge_port(name, port):
status = subprocess.check_output(["ovs-vsctl", "show"])
if "Bridge {}".format(name) in status:
subprocess.check_call(["ovs-vsctl", "add-port", name, port])
def del_bridge_port(name, port):
status = subprocess.check_output(["ovs-vsctl", "show"])
if ("Bridge {}".format(name) in status and
"Interface \"{}\"".format(port) in status):
subprocess.check_call(["ovs-vsctl", "del-port", name, port])

View File

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

View File

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

1
hooks/start Symbolic link
View File

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

1
hooks/stop Symbolic link
View File

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

185
hooks/utils.py Normal file
View File

@ -0,0 +1,185 @@
#
# Copyright 2012 Canonical Ltd.
#
# Authors:
# James Page <james.page@ubuntu.com>
# Paul Collins <paul.collins@canonical.com>
#
import os
import subprocess
import socket
import sys
def do_hooks(hooks):
hook = os.path.basename(sys.argv[0])
try:
hooks[hook]()
except KeyError:
juju_log('INFO',
"This charm doesn't know how to handle '{}'.".format(hook))
def install(*pkgs):
cmd = [
'apt-get',
'-y',
'install'
]
for pkg in pkgs:
cmd.append(pkg)
subprocess.check_call(cmd)
TEMPLATES_DIR = 'templates'
try:
import jinja2
except ImportError:
install('python-jinja2')
import jinja2
def render_template(template_name, context, template_dir=TEMPLATES_DIR):
templates = jinja2.Environment(
loader=jinja2.FileSystemLoader(template_dir)
)
template = templates.get_template(template_name)
return template.render(context)
def configure_source():
source = config_get('source')
if not source:
return
if (source.startswith('ppa:') or
source.startswith('cloud:')):
cmd = [
'add-apt-repository',
source
]
subprocess.check_call(cmd)
if source.startswith('http:'):
with open('/etc/apt/sources.list.d/ceph.list', 'w') as apt:
apt.write("deb " + source + "\n")
key = config_get('key')
if key != "":
cmd = [
'apt-key',
'adv', '--keyserver keyserver.ubuntu.com',
'--recv-keys', key
]
subprocess.check_call(cmd)
cmd = [
'apt-get',
'update'
]
subprocess.check_call(cmd)
# Protocols
TCP = 'TCP'
UDP = 'UDP'
def expose(port, protocol='TCP'):
cmd = [
'open-port',
'{}/{}'.format(port, protocol)
]
subprocess.check_call(cmd)
def juju_log(severity, message):
cmd = [
'juju-log',
'--log-level', severity,
message
]
subprocess.check_call(cmd)
def relation_ids(relation):
cmd = [
'relation-ids',
relation
]
return subprocess.check_output(cmd).split() # IGNORE:E1103
def relation_list(rid):
cmd = [
'relation-list',
'-r', rid,
]
return subprocess.check_output(cmd).split() # IGNORE:E1103
def relation_get(attribute, unit=None, rid=None):
cmd = [
'relation-get',
]
if rid:
cmd.append('-r')
cmd.append(rid)
cmd.append(attribute)
if unit:
cmd.append(unit)
value = subprocess.check_output(cmd).strip() # IGNORE:E1103
if value == "":
return None
else:
return value
def relation_set(**kwargs):
cmd = [
'relation-set'
]
args = []
for k, v in kwargs.items():
if k == 'rid':
cmd.append('-r')
cmd.append(v)
else:
args.append('{}={}'.format(k, v))
cmd += args
subprocess.check_call(cmd)
def unit_get(attribute):
cmd = [
'unit-get',
attribute
]
value = subprocess.check_output(cmd).strip() # IGNORE:E1103
if value == "":
return None
else:
return value
def config_get(attribute):
cmd = [
'config-get',
attribute
]
value = subprocess.check_output(cmd).strip() # IGNORE:E1103
if value == "":
return None
else:
return value
def get_unit_hostname():
return socket.gethostname()
def get_host_ip(hostname=unit_get('private-address')):
cmd = [
'dig',
'+short',
hostname
]
return subprocess.check_output(cmd).strip() # IGNORE:E1103

26
metadata.yaml Normal file
View File

@ -0,0 +1,26 @@
name: quantum
summary: Virtual Networking for OpenStack
maintainer: James Page <james.page@ubuntu.com>
description: |
Quantum is a virtual network service for Openstack, and a part of
Netstack. Just like OpenStack Nova provides an API to dynamically
request and configure virtual servers, Quantum provides an API to
dynamically request and configure virtual networks. These networks
connect "interfaces" from other OpenStack services (e.g., virtual NICs
from Nova VMs). The Quantum API supports extensions to provide
advanced network capabilities (e.g., QoS, ACLs, network monitoring,
etc.)
provides:
network-manager:
interface: quantum
requires:
cloud-compute:
interface: nova-compute
shared-db:
interface: mysql-shared
amqp:
interface: rabbitmq
identity-service:
interface: keystone
cloud-controller:
interface: nova

1
revision Normal file
View File

@ -0,0 +1 @@
1