2011-12-08 09:52:12 -08:00
|
|
|
#!/usr/bin/python
|
2012-02-29 11:59:37 -08:00
|
|
|
|
2011-12-08 09:52:12 -08:00
|
|
|
import sys
|
2012-02-29 11:59:37 -08:00
|
|
|
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"
|
|
|
|
},
|
2012-03-08 14:38:36 -08:00
|
|
|
"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():
|
2011-12-22 10:21:23 -08:00
|
|
|
if config["keystone-release"] != "distro":
|
|
|
|
setup_ppa(config["keystone-release"])
|
2011-12-23 17:34:15 -08:00
|
|
|
execute("apt-get update", die=True)
|
2011-12-22 10:21:23 -08:00
|
|
|
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')
|
2012-01-10 23:21:57 -08:00
|
|
|
execute("service keystone stop", echo=True)
|
2012-02-28 17:18:17 -08:00
|
|
|
execute("keystone-manage db_sync")
|
2012-02-29 11:59:37 -08:00
|
|
|
#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")
|
2012-01-10 23:21:57 -08:00
|
|
|
execute("service keystone start", echo=True)
|
2012-02-29 11:59:37 -08:00
|
|
|
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"]))
|
2012-01-10 23:21:57 -08:00
|
|
|
execute("service keystone stop", echo=True)
|
2012-02-28 17:18:17 -08:00
|
|
|
execute("keystone-manage db_sync", echo=True)
|
2012-01-10 23:21:57 -08:00
|
|
|
execute("service keystone start")
|
2012-02-29 11:59:37 -08:00
|
|
|
time.sleep(5)
|
|
|
|
ensure_initial_admin(config)
|
2011-12-08 09:52:12 -08:00
|
|
|
|
2012-09-17 17:39:51 -07: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
|
|
|
|
|
2012-09-17 17:39:51 -07:00
|
|
|
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().
|
|
|
|
"""
|
2012-03-01 12:37:18 -08:00
|
|
|
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)
|
|
|
|
|
2012-09-17 17:39:51 -07:00
|
|
|
settings = relation_get_dict(relation_id=relation_id,
|
|
|
|
remote_unit=remote_unit)
|
2012-03-01 12:37:18 -08:00
|
|
|
|
|
|
|
# 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
|
2012-03-08 14:38:36 -08:00
|
|
|
|
|
|
|
if 'None' in [v for k,v in settings.iteritems()]:
|
2012-09-17 17:39:51 -07:00
|
|
|
# Some backend services advertise no endpoint but require a
|
|
|
|
# hook execution to update auth strategy.
|
2012-03-08 14:38:36 -08:00
|
|
|
return
|
|
|
|
|
2012-03-01 12:37:18 -08:00
|
|
|
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'])
|
2012-03-02 12:46:20 -08:00
|
|
|
service_username = settings['service']
|
2012-03-01 12:37:18 -08:00
|
|
|
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
|
2012-03-02 12:46:20 -08:00
|
|
|
services = []
|
2012-03-01 12:37:18 -08:00
|
|
|
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'])
|
2012-03-02 12:46:20 -08:00
|
|
|
services.append(ep['service'])
|
|
|
|
service_username = '_'.join(services)
|
2011-12-08 09:52:12 -08:00
|
|
|
|
2012-03-08 14:38:36 -08:00
|
|
|
if 'None' in [v for k,v in settings.iteritems()]:
|
|
|
|
return
|
|
|
|
|
|
|
|
if not service_username:
|
|
|
|
return
|
2012-03-02 12:46:20 -08:00
|
|
|
|
2012-03-08 14:38:36 -08:00
|
|
|
token = get_admin_token()
|
2012-03-02 12:46:20 -08:00
|
|
|
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)
|
|
|
|
|
2012-03-02 12:46:20 -08:00
|
|
|
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"],
|
2012-03-02 12:46:20 -08:00
|
|
|
"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)
|
|
|
|
|
2012-08-08 16:17:52 -07:00
|
|
|
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,
|
2012-08-08 16:17:52 -07:00
|
|
|
"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]()
|