255 lines
9.8 KiB
Plaintext
Raw Normal View History

2011-12-08 09:52:12 -08:00
#!/usr/bin/python
2011-12-08 09:52:12 -08:00
import sys
import time
2011-12-08 09:52:12 -08:00
from utils import *
config = config_get()
packages = "keystone python-mysqldb pwgen"
service = "keystone"
# used to verify joined services are valid openstack components.
2011-12-23 17:34:15 -08:00
# this should reflect the current "core" components of openstack
# and be expanded as we add support for them as a distro
2011-12-08 09:52:12 -08:00
valid_services = {
"nova": {
"type": "compute",
"desc": "Nova Compute Service"
},
"nova-volume": {
"type": "volume",
"desc": "Nova Volume Service"
},
2012-03-01 12:35:39 -08:00
"ec2": {
"type": "ec2",
"desc": "EC2 Compatibility Layer"
},
2011-12-08 09:52:12 -08:00
"glance": {
"type": "image",
"desc": "Glance Image Service"
2011-12-21 15:29:31 -08:00
},
2012-04-13 17:24:56 -07:00
"s3": {
"type": "s3",
"desc": "S3 Compatible object-store"
},
2011-12-21 15:29:31 -08:00
"swift": {
"type": "storage",
"desc": "Swift Object Storage Service"
2011-12-08 09:52:12 -08:00
}
}
def install_hook():
if config["keystone-release"] != "distro":
setup_ppa(config["keystone-release"])
2011-12-23 17:34:15 -08:00
execute("apt-get update", die=True)
execute("apt-get -y install %s" % packages, die=True, echo=True)
2012-02-28 17:18:17 -08:00
update_config_block('DEFAULT', public_port=config["service-port"])
update_config_block('DEFAULT', admin_port=config["admin-port"])
2012-03-01 12:35:39 -08:00
# Set or generate the keystone.conf admin token. This can probably go
# away soon since the admin tokens will be deprecated in favor of service
# tenants. http://etherpad.openstack.org/keystone-admin-config
if config['admin-token'] != "None":
juju_log("Configuring keystone.conf to use a preconfigured admin"\
"token")
token = config['admin-token']
else:
juju_log("Generating an keystone.conf admin token.")
token = execute('pwgen -c 32 1', die=True)[0]
update_config_block('DEFAULT', admin_token=token)
2012-02-28 17:18:17 -08:00
# set all backends to use sql+sqlite, if they are not already by default
update_config_block('sql',
connection='sqlite:////var/lib/keystone/keystone.db')
update_config_block('identity',
driver='keystone.identity.backends.sql.Identity')
update_config_block('catalog',
driver='keystone.catalog.backends.sql.Catalog')
update_config_block('token',
driver='keystone.token.backends.sql.Token')
update_config_block('ec2',
driver='keystone.contrib.ec2.backends.sql.Ec2')
execute("service keystone stop", echo=True)
2012-02-28 17:18:17 -08:00
execute("keystone-manage db_sync")
#NOTE(adam_g): This chown can be removed once packaging switches to sql
# and does this for us
execute("chown keystone /var/lib/keystone/ -R")
execute("service keystone start", echo=True)
time.sleep(5)
2011-12-08 09:52:12 -08:00
ensure_initial_admin(config)
def db_joined():
relation_data = { "database": config["database"],
"username": config["database-user"],
"hostname": config["hostname"] }
relation_set(relation_data)
def db_changed():
relation_data = relation_get(["private-address", "password"])
if len(relation_data) != 2:
juju_log("private-address or password not set. Peer not ready, exit 0")
exit(0)
2012-02-28 17:18:17 -08:00
update_config_block('sql', connection="mysql://%s:%s@%s/%s" %
2011-12-08 09:52:12 -08:00
(config["database-user"],
relation_data["password"],
relation_data["private-address"],
config["database"]))
execute("service keystone stop", echo=True)
2012-02-28 17:18:17 -08:00
execute("keystone-manage db_sync", echo=True)
execute("service keystone start")
time.sleep(5)
ensure_initial_admin(config)
2011-12-08 09:52:12 -08:00
# If the backend database has been switched to something new and there
# are existing identity-service relations,, service entries need to be
# recreated in the new database. Re-executing identity-service-changed
# will do this.
for id in relation_ids(relation_name='identity-service'):
for unit in relation_list(relation_id=id):
juju_log("Re-exec'ing identity-service-changed for: %s - %s" %
(id, unit))
identity_changed(relation_id=id, remote_unit=unit)
2011-12-08 09:52:12 -08:00
def identity_joined():
""" Do nothing until we get information about requested service """
pass
def identity_changed(relation_id=None, remote_unit=None):
""" A service has advertised its API endpoints, create an entry in the
service catalog.
Optionally allow this hook to be re-fired for an existing
relation+unit, for context see see db_changed().
"""
def ensure_valid_service(service):
if service not in valid_services.keys():
juju_log("WARN: Invalid service requested: '%s'" % service)
realtion_set({ "admin_token": -1 })
return
def add_endpoint(region, service, public_url, admin_url, internal_url):
desc = valid_services[service]["desc"]
service_type = valid_services[service]["type"]
create_service_entry(service, service_type, desc)
create_endpoint_template(region=region, service=service,
public_url=public_url,
admin_url=admin_url,
internal_url=internal_url)
settings = relation_get_dict(relation_id=relation_id,
remote_unit=remote_unit)
# the minimum settings needed per endpoint
single = set(['service', 'region', 'public_url', 'admin_url',
'internal_url'])
if single.issubset(settings):
# other end of relation advertised only one endpoint
if 'None' in [v for k,v in settings.iteritems()]:
# Some backend services advertise no endpoint but require a
# hook execution to update auth strategy.
return
ensure_valid_service(settings['service'])
add_endpoint(region=settings['region'], service=settings['service'],
public_url=settings['public_url'],
admin_url=settings['admin_url'],
internal_url=settings['internal_url'])
service_username = settings['service']
else:
# assemble multiple endpoints from relation data. service name
# should be prepended to setting name, ie:
# realtion-set ec2_service=$foo ec2_region=$foo ec2_public_url=$foo
# relation-set nova_service=$foo nova_region=$foo nova_public_url=$foo
# Results in a dict that looks like:
# { 'ec2': {
# 'service': $foo
# 'region': $foo
# 'public_url': $foo
# }
# 'nova': {
# 'service': $foo
# 'region': $foo
# 'public_url': $foo
# }
# }
endpoints = {}
for k,v in settings.iteritems():
ep = k.split('_')[0]
x = '_'.join(k.split('_')[1:])
if ep not in endpoints:
endpoints[ep] = {}
endpoints[ep][x] = v
services = []
for ep in endpoints:
# weed out any unrelated relation stuff Juju might have added
# by ensuring each possible endpiont has appropriate fields
# ['service', 'region', 'public_url', 'admin_url', 'internal_url']
if single.issubset(endpoints[ep]):
ep = endpoints[ep]
ensure_valid_service(ep['service'])
add_endpoint(region=ep['region'], service=ep['service'],
public_url=ep['public_url'],
admin_url=ep['admin_url'],
internal_url=ep['internal_url'])
services.append(ep['service'])
service_username = '_'.join(services)
2011-12-08 09:52:12 -08:00
if 'None' in [v for k,v in settings.iteritems()]:
return
if not service_username:
return
token = get_admin_token()
juju_log("Creating service credentials for '%s'" % service_username)
2012-03-09 14:56:59 -08:00
stored_passwd = '/var/lib/keystone/%s.passwd' % service_username
if os.path.isfile(stored_passwd):
juju_log("Loading stored service passwd from %s" % stored_passwd)
service_password = open(stored_passwd, 'r').readline().strip('\n')
else:
juju_log("Generating a new service password for %s" % service_username)
service_password = execute('pwgen -c 32 1', die=True)[0].strip()
open(stored_passwd, 'w+').writelines("%s\n" % service_password)
create_user(service_username, service_password, config['service-tenant'])
grant_role(service_username, config['admin-role'], config['service-tenant'])
# As of https://review.openstack.org/#change,4675, all nodes hosting
# an endpoint(s) needs a service username and password assigned to
# the service tenant and granted admin role.
# note: config['service-tenant'] is created in utils.ensure_initial_admin()
# we return a token, information about our API endpoints, and the generated
# service credentials
2011-12-08 09:52:12 -08:00
relation_data = {
"admin_token": token,
"service_host": config["hostname"],
"service_port": config["service-port"],
"auth_host": config["hostname"],
"auth_port": config["admin-port"],
"service_username": service_username,
"service_password": service_password,
"service_tenant": config['service-tenant']
2011-12-08 09:52:12 -08:00
}
relation_set(relation_data)
def config_changed():
ensure_initial_admin(config)
2011-12-08 09:52:12 -08:00
hooks = {
"install": install_hook,
"shared-db-relation-joined": db_joined,
"shared-db-relation-changed": db_changed,
"identity-service-relation-joined": identity_joined,
"identity-service-relation-changed": identity_changed,
"config-changed": config_changed
2011-12-08 09:52:12 -08:00
}
2011-12-23 17:34:15 -08:00
# keystone-hooks gets called by symlink corresponding to the requested relation
# hook.
2011-12-08 09:52:12 -08:00
arg0 = sys.argv[0].split("/").pop()
if arg0 not in hooks.keys():
error_out("Unsupported hook: %s" % arg0)
hooks[arg0]()