Esempio n. 1
0
def ssh_authorized_peers(peer_interface, user, group=None, ensure_local_user=False):
    """
    Main setup function, should be called from both peer -changed and -joined
    hooks with the same parameters.
    """
    if ensure_local_user:
        ensure_user(user, group)
    priv_key, pub_key = get_keypair(user)
    hook = os.path.basename(sys.argv[0])
    if hook == '%s-relation-joined' % peer_interface:
        utils.relation_set(ssh_pub_key=pub_key)
        print 'joined'
    elif hook == '%s-relation-changed' % peer_interface:
        hosts = []
        keys = []
        for r_id in utils.relation_ids(peer_interface):
            for unit in utils.relation_list(r_id):
                settings = utils.relation_get_dict(relation_id=r_id,
                                                   remote_unit=unit)
                if 'ssh_pub_key' in settings:
                    keys.append(settings['ssh_pub_key'])
                    hosts.append(settings['private-address'])
                else:
                    utils.juju_log('INFO',
                                   'ssh_authorized_peers(): ssh_pub_key '\
                                   'missing for unit %s, skipping.' % unit)
        write_authorized_keys(user, keys)
        write_known_hosts(user, hosts)
        authed_hosts = ':'.join(hosts)
        utils.relation_set(ssh_authorized_hosts=authed_hosts)
Esempio n. 2
0
def ssh_authorized_peers(peer_interface,
                         user,
                         group=None,
                         ensure_local_user=False):
    """
    Main setup function, should be called from both peer -changed and -joined
    hooks with the same parameters.
    """
    if ensure_local_user:
        ensure_user(user, group)
    priv_key, pub_key = get_keypair(user)
    hook = os.path.basename(sys.argv[0])
    if hook == '%s-relation-joined' % peer_interface:
        utils.relation_set(ssh_pub_key=pub_key)
        print 'joined'
    elif hook == '%s-relation-changed' % peer_interface:
        hosts = []
        keys = []
        for r_id in utils.relation_ids(peer_interface):
            for unit in utils.relation_list(r_id):
                settings = utils.relation_get_dict(relation_id=r_id,
                                                   remote_unit=unit)
                if 'ssh_pub_key' in settings:
                    keys.append(settings['ssh_pub_key'])
                    hosts.append(settings['private-address'])
                else:
                    utils.juju_log('INFO',
                                   'ssh_authorized_peers(): ssh_pub_key '\
                                   'missing for unit %s, skipping.' % unit)
        write_authorized_keys(user, keys)
        write_known_hosts(user, hosts)
        authed_hosts = ':'.join(hosts)
        utils.relation_set(ssh_authorized_hosts=authed_hosts)
def ha_relation_changed():
    relation_data = utils.relation_get_dict()
    if ('clustered' in relation_data and
        cluster.is_leader(CLUSTER_RES)):
        utils.juju_log('INFO',
                       'Cluster configured, notifying other services'
                       ' and updating keystone endpoint configuration')
        # Update keystone endpoint to point at VIP
        ensure_initial_admin(config)
        # Tell all related services to start using
        # the VIP and haproxy ports instead
        for r_id in utils.relation_ids('identity-service'):
            utils.relation_set(rid=r_id,
                               auth_host=config['vip'],
                               service_host=config['vip'])
Esempio n. 4
0
def sync_to_peers(peer_interface, user, paths=[], verbose=False):
    base_cmd = [
        'unison', '-auto', '-batch=true', '-confirmbigdel=false',
        '-fastcheck=true', '-group=false', '-owner=false', '-prefer=newer',
        '-times=true'
    ]
    if not verbose:
        base_cmd.append('-silent')

    hosts = []
    for r_id in (utils.relation_ids(peer_interface) or []):
        for unit in utils.relation_list(r_id):
            settings = utils.relation_get_dict(relation_id=r_id,
                                               remote_unit=unit)
            try:
                authed_hosts = settings['ssh_authorized_hosts'].split(':')
            except KeyError:
                print 'unison sync_to_peers: peer has not authorized *any* '\
                      'hosts yet.'
                return

            unit_hostname = utils.unit_get('private-address')
            add_host = None
            for authed_host in authed_hosts:
                if unit_hostname == authed_host:
                    add_host = settings['private-address']
            if add_host:
                hosts.append(settings['private-address'])
            else:
                print 'unison sync_to_peers: peer (%s) has not authorized '\
                      '*this* host yet, skipping.' %\
                       settings['private-address']

    for path in paths:
        # removing trailing slash from directory paths, unison
        # doesn't like these.
        if path.endswith('/'):
            path = path[:(len(path) - 1)]
        for host in hosts:
            try:
                cmd = base_cmd + [path, 'ssh://%s@%s/%s' % (user, host, path)]
                utils.juju_log(
                    'INFO', 'Syncing local path %s to %s@%s:%s' %
                    (path, user, host, path))
                run_as_user(user, cmd)
            except:
                # it may fail for permissions on some files
                pass
Esempio n. 5
0
def sync_to_peers(peer_interface, user, paths=[], verbose=False):
    base_cmd = ['unison', '-auto', '-batch=true', '-confirmbigdel=false',
                '-fastcheck=true', '-group=false', '-owner=false',
                '-prefer=newer', '-times=true']
    if not verbose:
        base_cmd.append('-silent')

    hosts = []
    for r_id in (utils.relation_ids(peer_interface) or []):
        for unit in utils.relation_list(r_id):
            settings = utils.relation_get_dict(relation_id=r_id,
                                               remote_unit=unit)
            try:
                authed_hosts = settings['ssh_authorized_hosts'].split(':')
            except KeyError:
                print 'unison sync_to_peers: peer has not authorized *any* '\
                      'hosts yet.'
                return

            unit_hostname = utils.unit_get('private-address')
            add_host = None
            for authed_host in authed_hosts:
                if unit_hostname == authed_host:
                    add_host = settings['private-address']
            if add_host:
                hosts.append(settings['private-address'])
            else:
                print 'unison sync_to_peers: peer (%s) has not authorized '\
                      '*this* host yet, skipping.' %\
                       settings['private-address']

    for path in paths:
        # removing trailing slash from directory paths, unison
        # doesn't like these.
        if path.endswith('/'):
            path = path[:(len(path) - 1)]
        for host in hosts:
            try:
                cmd = base_cmd + [path, 'ssh://%s@%s/%s' % (user, host, path)]
                utils.juju_log('INFO', 'Syncing local path %s to %s@%s:%s' %
                               (path, user, host, path))
                run_as_user(user, cmd)
            except:
                # it may fail for permissions on some files
                pass
def db_changed():
    relation_data = utils.relation_get_dict()
    if ('password' not in relation_data or
        'db_host' not in relation_data):
        utils.juju_log('INFO',
                       "db_host or password not set. Peer not ready, exit 0")
        return

    update_config_block('sql', connection="mysql://%s:%s@%s/%s" %
                            (config["database-user"],
                             relation_data["password"],
                             relation_data["db_host"],
                             config["database"]))

    if cluster.eligible_leader(CLUSTER_RES):
        utils.juju_log('INFO',
                       'Cluster leader, performing db-sync')
        execute("keystone-manage db_sync", echo=True)

    if config_dirty():
        utils.restart('keystone')

    time.sleep(5)

    if cluster.eligible_leader(CLUSTER_RES):
        ensure_initial_admin(config)
        # 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 rid in utils.relation_ids('identity-service'):
            for unit in utils.relation_list(rid=rid):
                utils.juju_log('INFO',
                               "Re-exec'ing identity-service-changed"
                               " for: %s - %s" % (rid, unit))
                identity_changed(relation_id=rid, remote_unit=unit)
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().
    """
    if not cluster.eligible_leader(CLUSTER_RES):
        utils.juju_log('INFO',
                       'Deferring identity_changed() to service leader.')
        return

    settings = utils.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.
            relation_data = {}
            # Check if clustered and use vip + haproxy ports if so
            if cluster.is_clustered():
                relation_data["auth_host"] = config['vip']
                relation_data["service_host"] = config['vip']
            else:
                relation_data["auth_host"] = config['hostname']
                relation_data["service_host"] = config['hostname']
            relation_data["auth_port"] = config['admin-port']
            relation_data["service_port"] = config['service-port']
            if config['https-service-endpoints'] in ['True', 'true']:
                # Pass CA cert as client will need it to
                # verify https connections
                ca = get_ca(user=SSH_USER)
                ca_bundle = ca.get_ca_bundle()
                relation_data['https_keystone'] = 'True'
                relation_data['ca_cert'] = b64encode(ca_bundle)
            if relation_id:
                relation_data['rid'] = relation_id
            # Allow the remote service to request creation of any additional
            # roles. Currently used by Horizon
            for role in get_requested_roles(settings):
                utils.juju_log('INFO',
                               "Creating requested role: %s" % role)
                create_role(role)
            utils.relation_set(**relation_data)
            return
        else:
            ensure_valid_service(settings['service'])
            add_endpoint(region=settings['region'],
                         service=settings['service'],
                         publicurl=settings['public_url'],
                         adminurl=settings['admin_url'],
                         internalurl=settings['internal_url'])
            service_username = settings['service']
            https_cn = urlparse.urlparse(settings['internal_url'])
            https_cn = https_cn.hostname
    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 = []
        https_cn = None
        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'],
                             publicurl=ep['public_url'],
                             adminurl=ep['admin_url'],
                             internalurl=ep['internal_url'])
                services.append(ep['service'])
                if not https_cn:
                    https_cn = urlparse.urlparse(ep['internal_url'])
                    https_cn = https_cn.hostname
        service_username = '******'.join(services)

    if 'None' in [v for k, v in settings.iteritems()]:
        return

    if not service_username:
        return

    token = get_admin_token()
    utils.juju_log('INFO',
                   "Creating service credentials for '%s'" % service_username)

    service_password = get_service_password(service_username)
    create_user(service_username, service_password, config['service-tenant'])
    grant_role(service_username, config['admin-role'],
               config['service-tenant'])

    # Allow the remote service to request creation of any additional roles.
    # Currently used by Swift and Ceilometer.
    for role in get_requested_roles(settings):
        utils.juju_log('INFO',
                       "Creating requested role: %s" % role)
        create_role(role, service_username,
                    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
    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'],
        "https_keystone": "False",
        "ssl_cert": "",
        "ssl_key": "",
        "ca_cert": ""
    }

    if relation_id:
        relation_data['rid'] = relation_id

    # Check if clustered and use vip + haproxy ports if so
    if cluster.is_clustered():
        relation_data["auth_host"] = config['vip']
        relation_data["service_host"] = config['vip']

    # generate or get a new cert/key for service if set to manage certs.
    if config['https-service-endpoints'] in ['True', 'true']:
        ca = get_ca(user=SSH_USER)
        cert, key = ca.get_cert_and_key(common_name=https_cn)
        ca_bundle = ca.get_ca_bundle()
        relation_data['ssl_cert'] = b64encode(cert)
        relation_data['ssl_key'] = b64encode(key)
        relation_data['ca_cert'] = b64encode(ca_bundle)
        relation_data['https_keystone'] = 'True'
        unison.sync_to_peers(peer_interface='cluster',
                             paths=[SSL_DIR], user=SSH_USER, verbose=True)
    utils.relation_set(**relation_data)
    synchronize_service_credentials()
def do_openstack_upgrade(install_src, packages):
    '''Upgrade packages from a given install src.'''

    config = config_get()
    old_vers = get_os_codename_package('keystone')
    new_vers = get_os_codename_install_source(install_src)

    utils.juju_log('INFO',
                   "Beginning Keystone upgrade: %s -> %s" % \
                   (old_vers, new_vers))

    # Backup previous config.
    utils.juju_log('INFO', "Backing up contents of /etc/keystone.")
    stamp = time.strftime('%Y%m%d%H%M')
    cmd = 'tar -pcf /var/lib/juju/keystone-backup-%s.tar /etc/keystone' % stamp
    execute(cmd, die=True, echo=True)

    configure_installation_source(install_src)
    execute('apt-get update', die=True, echo=True)
    os.environ['DEBIAN_FRONTEND'] = 'noninteractive'
    cmd = 'apt-get --option Dpkg::Options::=--force-confnew -y '\
          'install %s' % packages
    execute(cmd, echo=True, die=True)

    # we have new, fresh config files that need updating.
    # set the admin token, which is still stored in config.
    set_admin_token(config['admin-token'])

    # set the sql connection string if a shared-db relation is found.
    ids = utils.relation_ids('shared-db')

    if ids:
        for rid in ids:
            for unit in utils.relation_list(rid):
                utils.juju_log('INFO',
                               'Configuring new keystone.conf for '
                               'database access on existing database'
                               ' relation to %s' % unit)
                relation_data = utils.relation_get_dict(relation_id=rid,
                                                        remote_unit=unit)

                update_config_block('sql', connection="mysql://%s:%s@%s/%s" %
                                        (config["database-user"],
                                         relation_data["password"],
                                         relation_data["private-address"],
                                         config["database"]))

    utils.stop('keystone')
    if (cluster.eligible_leader(CLUSTER_RES)):
        utils.juju_log('INFO',
                       'Running database migrations for %s' % new_vers)
        execute('keystone-manage db_sync', echo=True, die=True)
    else:
        utils.juju_log('INFO',
                       'Not cluster leader; snoozing whilst'
                       ' leader upgrades DB')
        time.sleep(10)
    utils.start('keystone')
    time.sleep(5)
    utils.juju_log('INFO',
                   'Completed Keystone upgrade: '
                   '%s -> %s' % (old_vers, new_vers))