431 lines
13 KiB
Python
Raw Normal View History

2012-12-12 09:18:54 -08:00
#!/usr/bin/python
import os
import sys
import shutil
import uuid
2013-09-27 13:02:37 +01:00
import subprocess
import charmhelpers.contrib.openstack.utils as openstack
import charmhelpers.contrib.hahelpers.cluster as cluster
from swift_utils import (
register_configs,
restart_map,
determine_packages,
ensure_swift_dir,
SWIFT_RINGS, get_www_dir,
2013-09-27 13:02:37 +01:00
initialize_ring,
swift_user,
SWIFT_HA_RES,
balance_ring,
SWIFT_CONF_DIR,
get_zone,
exists_in_ring,
add_to_ring,
should_balance,
do_openstack_upgrade,
write_rc_script,
setup_ipv6
2013-09-27 13:02:37 +01:00
)
from swift_context import get_swift_hash
from charmhelpers.core.hookenv import (
config,
unit_get,
relation_set,
relation_ids,
relation_get,
2014-10-20 13:43:39 +01:00
log,
WARNING,
ERROR,
2013-09-27 13:02:37 +01:00
Hooks, UnregisteredHookError,
open_port
)
from charmhelpers.core.host import (
service_restart,
service_stop,
service_start,
2013-09-27 13:02:37 +01:00
restart_on_change
)
from charmhelpers.fetch import (
apt_install,
apt_update
)
2013-09-27 17:11:20 +01:00
from charmhelpers.payload.execd import execd_preinstall
from charmhelpers.contrib.openstack.ip import (
canonical_url,
PUBLIC, INTERNAL, ADMIN
)
from charmhelpers.contrib.network.ip import (
get_iface_for_address,
get_netmask_for_address,
2014-09-30 15:48:30 +08:00
get_address_in_network,
2014-09-20 23:49:25 +08:00
get_ipv6_addr,
2014-09-30 15:48:30 +08:00
format_ipv6_addr,
is_ipv6
)
2012-12-12 09:18:54 -08:00
from charmhelpers.contrib.openstack.context import ADDRESS_TYPES
2013-02-27 16:07:46 +00:00
extra_pkgs = [
"haproxy",
"python-jinja2"
2013-09-27 13:02:37 +01:00
]
hooks = Hooks()
2013-02-27 16:07:46 +00:00
2013-09-27 13:02:37 +01:00
CONFIGS = register_configs()
2013-09-27 13:02:37 +01:00
@hooks.hook('install')
2012-12-12 09:18:54 -08:00
def install():
2013-09-27 17:11:20 +01:00
execd_preinstall()
2013-09-27 13:02:37 +01:00
src = config('openstack-origin')
2012-12-12 09:18:54 -08:00
if src != 'distro':
openstack.configure_installation_source(src)
2013-09-27 13:02:37 +01:00
apt_update(fatal=True)
2012-12-12 09:18:54 -08:00
rel = openstack.get_os_codename_install_source(src)
2013-09-27 13:02:37 +01:00
pkgs = determine_packages(rel)
apt_install(pkgs, fatal=True)
apt_install(extra_pkgs, fatal=True)
ensure_swift_dir()
if cluster.is_elected_leader(SWIFT_HA_RES):
2014-10-20 13:43:39 +01:00
log("Leader established, generating ring builders")
# initialize new storage rings.
for ring in SWIFT_RINGS.iteritems():
initialize_ring(ring[1],
config('partition-power'),
config('replicas'),
config('min-hours'))
2012-12-12 09:18:54 -08:00
# configure a directory on webserver for distributing rings.
www_dir = get_www_dir()
if not os.path.isdir(www_dir):
2014-04-10 17:52:10 +01:00
os.mkdir(www_dir, 0o755)
2013-09-27 13:02:37 +01:00
uid, gid = swift_user()
os.chown(www_dir, uid, gid)
2012-12-12 09:18:54 -08:00
2013-09-27 13:02:37 +01:00
@hooks.hook('identity-service-relation-joined')
2012-12-12 09:18:54 -08:00
def keystone_joined(relid=None):
2013-09-27 13:02:37 +01:00
if not cluster.eligible_leader(SWIFT_HA_RES):
2013-02-27 21:25:38 +00:00
return
2013-09-27 13:02:37 +01:00
port = config('bind-port')
2014-07-14 15:42:25 +01:00
admin_url = '%s:%s' % (canonical_url(CONFIGS, ADMIN), port)
internal_url = '%s:%s/v1/AUTH_$(tenant_id)s' % \
(canonical_url(CONFIGS, INTERNAL), port)
2014-07-14 15:42:25 +01:00
public_url = '%s:%s/v1/AUTH_$(tenant_id)s' % \
(canonical_url(CONFIGS, PUBLIC), port)
2013-09-27 13:02:37 +01:00
relation_set(service='swift',
region=config('region'),
2014-06-27 11:56:24 +01:00
public_url=public_url,
internal_url=internal_url,
2013-09-27 13:02:37 +01:00
admin_url=admin_url,
requested_roles=config('operator-roles'),
relation_id=relid)
2012-12-12 09:18:54 -08:00
2013-09-27 13:02:37 +01:00
@hooks.hook('identity-service-relation-changed')
@restart_on_change(restart_map())
2012-12-12 09:18:54 -08:00
def keystone_changed():
2013-09-27 13:02:37 +01:00
configure_https()
2012-12-12 09:18:54 -08:00
def get_hostaddr():
if config('prefer-ipv6'):
return get_ipv6_addr(exc_list=[config('vip')])[0]
return unit_get('private-address')
def builders_synced():
for ring in SWIFT_RINGS.itervalues():
if not os.path.exists(ring):
return False
return True
2012-12-12 09:18:54 -08:00
def balance_rings():
'''handle doing ring balancing and distribution.'''
new_ring = False
2013-09-27 13:02:37 +01:00
for ring in SWIFT_RINGS.itervalues():
if balance_ring(ring):
log('Balanced ring %s' % ring)
2012-12-12 09:18:54 -08:00
new_ring = True
if not new_ring:
return
www_dir = get_www_dir()
2014-10-20 13:33:37 +01:00
for ring, builder_path in SWIFT_RINGS.iteritems():
ringfile = '%s.ring.gz' % ring
2014-10-20 14:23:12 +01:00
shutil.copyfile(os.path.join(SWIFT_CONF_DIR, ringfile),
os.path.join(www_dir, ringfile))
shutil.copyfile(builder_path,
os.path.join(www_dir, os.path.basename(builder_path)))
2012-12-12 09:18:54 -08:00
2013-09-27 13:02:37 +01:00
if cluster.eligible_leader(SWIFT_HA_RES):
2013-02-27 16:18:34 +00:00
msg = 'Broadcasting notification to all storage nodes that new '\
'ring is ready for consumption.'
2013-09-27 13:02:37 +01:00
log(msg)
path = os.path.basename(www_dir)
2013-02-27 16:18:34 +00:00
trigger = uuid.uuid4()
if cluster.is_clustered():
2013-09-27 13:02:37 +01:00
hostname = config('vip')
else:
hostname = get_hostaddr()
2014-09-20 23:49:25 +08:00
hostname = format_ipv6_addr(hostname) or hostname
rings_url = 'http://%s/%s' % (hostname, path)
2013-02-27 16:18:34 +00:00
# notify storage nodes that there is a new ring to fetch.
2013-09-27 13:02:37 +01:00
for relid in relation_ids('swift-storage'):
relation_set(relation_id=relid, swift_hash=get_swift_hash(),
rings_url=rings_url, trigger=trigger)
2013-02-27 16:07:46 +00:00
2013-09-27 13:02:37 +01:00
service_restart('swift-proxy')
2012-12-12 09:18:54 -08:00
2013-09-27 13:02:37 +01:00
@hooks.hook('swift-storage-relation-changed')
@restart_on_change(restart_map())
2012-12-18 11:59:19 -08:00
def storage_changed():
2014-08-13 20:15:41 +08:00
if config('prefer-ipv6'):
2014-08-15 15:06:25 +08:00
host_ip = '[%s]' % relation_get('private-address')
2014-08-13 20:15:41 +08:00
else:
host_ip = openstack.get_host_ip(relation_get('private-address'))
2014-10-20 19:27:50 +01:00
if cluster.is_elected_leader(SWIFT_HA_RES):
2014-10-20 19:41:37 +01:00
log("Leader established, updating ring builders")
2014-10-20 19:27:50 +01:00
zone = get_zone(config('zone-assignment'))
node_settings = {
'ip': host_ip,
'zone': zone,
'account_port': relation_get('account_port'),
'object_port': relation_get('object_port'),
'container_port': relation_get('container_port'),
}
if None in node_settings.itervalues():
log('storage_changed: Relation not ready.')
return None
for k in ['zone', 'account_port', 'object_port', 'container_port']:
node_settings[k] = int(node_settings[k])
2012-12-12 09:18:54 -08:00
2014-10-20 19:41:37 +01:00
CONFIGS.write_all()
# allow for multiple devs per unit, passed along as a : separated list
devs = relation_get('device').split(':')
for dev in devs:
node_settings['device'] = dev
for ring in SWIFT_RINGS.itervalues():
if not exists_in_ring(ring, node_settings):
add_to_ring(ring, node_settings)
if should_balance([r for r in SWIFT_RINGS.itervalues()]):
balance_rings()
2014-10-21 09:32:52 +01:00
# Notify peers that builders are available
for rid in relation_ids('cluster'):
log("Notifying peer that ring builder is ready (rid='%s')" % (rid))
relation_set(relation_id=rid,
relation_settings={'builder-broker': get_hostaddr()})
else:
2014-10-20 12:41:57 +01:00
log("New storage relation joined - stopping proxy until ring builder "
"synced")
service_stop('swift-proxy')
2012-12-12 09:18:54 -08:00
2013-09-27 13:02:37 +01:00
@hooks.hook('swift-storage-relation-broken')
@restart_on_change(restart_map())
2012-12-18 11:59:19 -08:00
def storage_broken():
2013-09-27 13:02:37 +01:00
CONFIGS.write_all()
2012-12-12 09:18:54 -08:00
2013-09-27 13:02:37 +01:00
@hooks.hook('config-changed')
@restart_on_change(restart_map())
2012-12-12 09:18:54 -08:00
def config_changed():
if config('prefer-ipv6'):
setup_ipv6()
2013-09-27 13:02:37 +01:00
configure_https()
open_port(config('bind-port'))
2013-05-22 14:30:39 -07:00
# Determine whether or not we should do an upgrade, based on the
# the version offered in keyston-release.
if (openstack.openstack_upgrade_available('python-swift')):
do_openstack_upgrade(CONFIGS)
2014-06-27 11:56:24 +01:00
for r_id in relation_ids('identity-service'):
2014-06-27 12:00:03 +01:00
keystone_joined(relid=r_id)
[cluster_joined(rid) for rid in relation_ids('cluster')]
@hooks.hook('cluster-relation-joined')
def cluster_joined(relation_id=None):
for addr_type in ADDRESS_TYPES:
netaddr_cfg = 'os-{}-network'.format(addr_type)
address = get_address_in_network(config(netaddr_cfg))
if address:
settings = {'{}-address'.format(addr_type): address}
relation_set(relation_id=relation_id, relation_settings=settings)
if config('prefer-ipv6'):
2014-09-30 15:48:30 +08:00
private_addr = get_ipv6_addr(exc_list=[config('vip')])[0]
2014-10-02 10:28:13 +01:00
relation_set(relation_id=relation_id,
relation_settings={'private-address': private_addr})
else:
private_addr = unit_get('private-address')
def fetch_swift_builders(broker_url):
log('Fetching swift builders from proxy @ %s.' % broker_url)
2014-10-20 13:33:37 +01:00
target = '/etc/swift'
for server in ['account', 'object', 'container']:
url = '%s/%s.builder' % (broker_url, server)
log('Fetching %s.' % url)
2014-10-20 13:17:50 +01:00
cmd = ['wget', url, '--retry-connrefused', '-t', '10', '-O',
2014-10-20 13:19:25 +01:00
"%s/%s.builder" % (target, server)]
subprocess.check_call(cmd)
2013-02-27 16:07:46 +00:00
2013-02-27 16:18:34 +00:00
2013-09-27 13:02:37 +01:00
@hooks.hook('cluster-relation-changed',
'cluster-relation-departed')
2013-09-27 13:02:37 +01:00
@restart_on_change(restart_map())
2013-02-27 16:07:46 +00:00
def cluster_changed():
2013-09-27 13:02:37 +01:00
CONFIGS.write_all()
2013-02-27 16:07:46 +00:00
2014-10-20 12:41:57 +01:00
# If not the leader, see if there are any builder files we can sync from
# the leader.
if not cluster.is_elected_leader(SWIFT_HA_RES):
settings = relation_get()
broker = settings.get('builder-broker', None)
if broker:
2014-10-20 13:17:50 +01:00
path = os.path.basename(get_www_dir())
broker_url = 'http://%s/%s' % (broker, path)
2014-10-20 13:43:39 +01:00
try:
fetch_swift_builders(broker_url)
except subprocess.CalledProcessError:
2014-10-20 14:23:12 +01:00
log("Ring builder sync failed, builders not yet available - "
"leader not ready?", level=WARNING)
2014-10-20 13:43:39 +01:00
return None
2014-10-20 12:41:57 +01:00
if builders_synced():
if should_balance([r for r in SWIFT_RINGS.itervalues()]):
balance_rings()
2014-10-20 19:27:50 +01:00
2014-10-20 12:41:57 +01:00
log('Ring builders synced - starting swift-poxy')
2014-10-20 19:27:50 +01:00
CONFIGS.write_all()
2014-10-20 12:41:57 +01:00
service_start('swift-proxy')
2013-02-27 16:07:46 +00:00
2013-09-27 13:02:37 +01:00
@hooks.hook('ha-relation-changed')
2013-02-27 16:07:46 +00:00
def ha_relation_changed():
2013-09-27 13:02:37 +01:00
clustered = relation_get('clustered')
if clustered and cluster.is_leader(SWIFT_HA_RES):
log('Cluster configured, notifying other services and'
'updating keystone endpoint configuration')
2013-02-27 16:07:46 +00:00
# Tell all related services to start using
2013-03-01 23:20:05 +00:00
# the VIP instead
2013-09-27 13:02:37 +01:00
for r_id in relation_ids('identity-service'):
2013-02-27 16:07:46 +00:00
keystone_joined(relid=r_id)
2013-09-27 13:02:37 +01:00
@hooks.hook('ha-relation-joined')
2013-02-27 16:07:46 +00:00
def ha_relation_joined():
# Obtain the config values necessary for the cluster config. These
# include multicast port and interface to bind to.
2013-09-27 13:02:37 +01:00
corosync_bindiface = config('ha-bindiface')
corosync_mcastport = config('ha-mcastport')
vip = config('vip')
2013-02-27 16:07:46 +00:00
if not vip:
2013-09-27 13:02:37 +01:00
log('Unable to configure hacluster as vip not provided',
level=ERROR)
2013-02-27 16:07:46 +00:00
sys.exit(1)
# Obtain resources
resources = {
2013-09-27 13:02:37 +01:00
'res_swift_haproxy': 'lsb:haproxy'
}
2013-02-27 16:07:46 +00:00
resource_params = {
2013-09-27 13:02:37 +01:00
'res_swift_haproxy': 'op monitor interval="5s"'
}
vip_group = []
for vip in vip.split():
2014-09-30 15:48:30 +08:00
if is_ipv6(vip):
res_swift_vip = 'ocf:heartbeat:IPv6addr'
vip_params = 'ipv6addr'
else:
res_swift_vip = 'ocf:heartbeat:IPaddr2'
vip_params = 'ip'
iface = get_iface_for_address(vip)
if iface is not None:
vip_key = 'res_swift_{}_vip'.format(iface)
2014-08-13 20:15:41 +08:00
resources[vip_key] = res_swift_vip
resource_params[vip_key] = (
2014-08-13 14:46:10 +08:00
'params {ip}="{vip}" cidr_netmask="{netmask}"'
2014-08-13 20:15:41 +08:00
' nic="{iface}"'.format(ip=vip_params,
2014-08-13 14:46:10 +08:00
vip=vip,
iface=iface,
netmask=get_netmask_for_address(vip))
)
vip_group.append(vip_key)
if len(vip_group) >= 1:
relation_set(groups={'grp_swift_vips': ' '.join(vip_group)})
2013-02-27 16:07:46 +00:00
init_services = {
2013-09-27 13:02:37 +01:00
'res_swift_haproxy': 'haproxy'
}
2013-02-27 16:07:46 +00:00
clones = {
2013-09-27 13:02:37 +01:00
'cl_swift_haproxy': 'res_swift_haproxy'
}
relation_set(init_services=init_services,
corosync_bindiface=corosync_bindiface,
corosync_mcastport=corosync_mcastport,
resources=resources,
resource_params=resource_params,
clones=clones)
def configure_https():
'''
Enables SSL API Apache config if appropriate and kicks identity-service
with any required api updates.
'''
# need to write all to ensure changes to the entire request pipeline
# propagate (c-api, haprxy, apache)
CONFIGS.write_all()
if 'https' in CONFIGS.complete_contexts():
cmd = ['a2ensite', 'openstack_https_frontend']
subprocess.check_call(cmd)
else:
cmd = ['a2dissite', 'openstack_https_frontend']
subprocess.check_call(cmd)
# Apache 2.4 required enablement of configuration
if os.path.exists('/usr/sbin/a2enconf'):
subprocess.check_call(['a2enconf', 'swift-rings'])
2013-09-27 13:02:37 +01:00
for rid in relation_ids('identity-service'):
keystone_joined(relid=rid)
write_rc_script()
2013-09-27 13:02:37 +01:00
def main():
try:
hooks.execute(sys.argv)
except UnregisteredHookError as e:
log('Unknown hook {} - skipping.'.format(e))
if __name__ == '__main__':
main()