Esempio n. 1
0
def _assess_interface(interface, optional, missing_interfaces,
                      incomplete_interfaces):
    """Assess a named interface for presence and completeness

    Uses reactive flags 'connected' and 'available' to indicate whether
    an interface is present and complete.

    :param: interface: Name of interface to assess.
    :param: options: Boolean indicating whether interface is optional
    :param: missing_interfaces: List of missing interfaces to update
    :param: incomplete_interfaces: List of incomplete interfaces to update
    :returns: bool, bool: Tuple of booleans indicating (missing, incomplete)
    """
    log("Assessing interface {}".format(interface), level=DEBUG)
    base_name = interface.split('.')[0]
    connected = (is_flag_set('{}.connected'.format(interface))
                 or is_flag_set('{}.connected'.format(base_name)))
    missing = False
    incomplete = False
    if not connected:
        if not optional:
            missing_interfaces.append(base_name)
        missing = True
        incomplete = True
    elif connected and not is_flag_set('{}.available'.format(interface)):
        incomplete_interfaces.append(base_name)
        incomplete = True
    return (missing, incomplete)
Esempio n. 2
0
def publish_global_client_cert():
    """
    This is for backwards compatibility with older tls-certificate clients
    only.  Obviously, it's not good security / design to have clients sharing
    a certificate, but it seems that there are clients that depend on this
    (though some, like etcd, only block on the flag that it triggers but don't
    actually use the cert), so we have to set it for now.
    """
    if not client_approle_authorized():
        log("Vault not authorized: Skipping publish_global_client_cert",
            "WARNING")
        return
    cert_created = is_flag_set('charm.vault.global-client-cert.created')
    reissue_requested = is_flag_set('certificates.reissue.global.requested')
    tls = endpoint_from_flag('certificates.available')
    if not cert_created or reissue_requested:
        ttl = config()['default-ttl']
        max_ttl = config()['max-ttl']
        bundle = vault_pki.generate_certificate('client', 'global-client', [],
                                                ttl, max_ttl)
        unitdata.kv().set('charm.vault.global-client-cert', bundle)
        set_flag('charm.vault.global-client-cert.created')
        clear_flag('certificates.reissue.global.requested')
    else:
        bundle = unitdata.kv().get('charm.vault.global-client-cert')
    tls.set_client_cert(bundle['certificate'], bundle['private_key'])
Esempio n. 3
0
def _assess_status():
    """Assess status of relations and services for local unit"""
    if is_flag_set('snap.channel.invalid'):
        status_set('blocked',
                   'Invalid snap channel '
                   'configured: {}'.format(config('channel')))
        return
    if is_flag_set('config.dns_vip.invalid'):
        status_set('blocked',
                   'vip and dns-ha-access-record configured')
        return

    health = None
    if service_running('vault'):
        health = vault.get_vault_health()
        application_version_set(health.get('version'))

    _missing_interfaces = []
    _incomplete_interfaces = []

    _assess_interface_groups(REQUIRED_INTERFACES, optional=False,
                             missing_interfaces=_missing_interfaces,
                             incomplete_interfaces=_incomplete_interfaces)

    _assess_interface_groups(OPTIONAL_INTERFACES, optional=True,
                             missing_interfaces=_missing_interfaces,
                             incomplete_interfaces=_incomplete_interfaces)

    if _missing_interfaces or _incomplete_interfaces:
        state = 'blocked' if _missing_interfaces else 'waiting'
        status_set(state, ', '.join(_missing_interfaces +
                                    _incomplete_interfaces))
        return

    if not service_running('vault'):
        status_set('blocked', 'Vault service not running')
        return

    if not health['initialized']:
        status_set('blocked', 'Vault needs to be initialized')
        return

    if health['sealed']:
        status_set('blocked', 'Unit is sealed')
        return

    mlock_disabled = is_container() or config('disable-mlock')

    status_set(
        'active',
        'Unit is ready '
        '(active: {}, mlock: {})'.format(
            str(not health['standby']).lower(),
            'disabled' if mlock_disabled else 'enabled'
        )
    )
Esempio n. 4
0
    def test_when_not_set_clear(self):
        flags.register_trigger(when_not='foo',
                               set_flag='bar',
                               clear_flag='qux')
        flags.set_flag('noop')
        flags.clear_flag('noop')
        assert not flags.is_flag_set('bar')

        flags.set_flag('foo')
        flags.set_flag('qux')
        assert not flags.is_flag_set('bar')
        flags.clear_flag('foo')
        assert flags.is_flag_set('bar')
        assert not flags.is_flag_set('qux')
Esempio n. 5
0
def configure_vault(context):
    log("Running configure_vault", level=DEBUG)
    context['disable_mlock'] = is_container() or config('disable-mlock')

    context['ssl_available'] = is_state('vault.ssl.available')

    if is_flag_set('etcd.tls.available'):
        etcd = endpoint_from_flag('etcd.available')
        log("Etcd detected, adding to context", level=DEBUG)
        context['etcd_conn'] = etcd.connection_string()
        context['etcd_tls_ca_file'] = '/var/snap/vault/common/etcd-ca.pem'
        context['etcd_tls_cert_file'] = '/var/snap/vault/common/etcd-cert.pem'
        context['etcd_tls_key_file'] = '/var/snap/vault/common/etcd.key'
        save_etcd_client_credentials(etcd,
                                     key=context['etcd_tls_key_file'],
                                     cert=context['etcd_tls_cert_file'],
                                     ca=context['etcd_tls_ca_file'])
        context['api_addr'] = vault.get_api_url()
        context['cluster_addr'] = vault.get_cluster_url()
        log("Etcd detected, setting api_addr to {}".format(
            context['api_addr']))
    else:
        log("Etcd not detected", level=DEBUG)
    log("Rendering vault.hcl.j2", level=DEBUG)
    render('vault.hcl.j2', VAULT_CONFIG, context, perms=0o600)
    log("Rendering vault systemd configuation", level=DEBUG)
    render('vault.service.j2', VAULT_SYSTEMD_CONFIG, {}, perms=0o644)
    service('enable', 'vault')
    log("Opening vault port", level=DEBUG)
    open_port(8200)
    set_flag('configured')
    if any_file_changed([VAULT_CONFIG, VAULT_SYSTEMD_CONFIG]):
        # force a restart if config has changed
        clear_flag('started')
Esempio n. 6
0
def configure_node(cluster_changed, cluster_joined):
    status_set('maintenance', 'Configuring slurm-node')

    controller_data = cluster_changed.active_data
    create_spool_dir(context=controller_data)

    render_munge_key(context=controller_data)
    # If the munge.key has been changed on the controller and munge is
    # running, the service must be restarted to use the new key
    if flags.is_flag_set('endpoint.slurm-cluster.changed.munge_key'
                         ) and service_running(MUNGE_SERVICE):
        log('Restarting munge due to key change on slurm-controller')
        service_restart(MUNGE_SERVICE)

    render_slurm_config(context=controller_data)

    # Make sure munge is running
    if not service_running(MUNGE_SERVICE):
        service_start(MUNGE_SERVICE)
    # Make sure slurmd is running
    if not service_running(SLURMD_SERVICE):
        service_start(SLURMD_SERVICE)

    flags.set_flag('slurm-node.configured')
    log('Set {} flag'.format('slurm-node.configured'))

    flags.clear_flag('endpoint.slurm-cluster.active.changed')
    log('Cleared {} flag'.format('endpoint.slurm-cluster.active.changed'))

    # Clear this flag to be able to signal munge_key changed if it occurs from
    # a controller.
    flags.clear_flag('endpoint.slurm-cluster.changed.munge_key')
    log('Cleared {} flag'.format('endpoint.slurm-cluster.changed.munge_key'))
Esempio n. 7
0
def send_vault_url_and_ca():
    secrets = endpoint_from_flag('secrets.connected')
    vault_url_external = None
    if is_flag_set('ha.available'):
        hostname = config('hostname')
        if hostname:
            vault_url = vault.get_api_url(address=hostname)
        else:
            vip = vault.get_vip()
            vault_url = vault.get_api_url(address=vip)
            ext_vip = vault.get_vip(binding='external')
            if ext_vip and ext_vip != vip:
                vault_url_external = vault.get_api_url(address=ext_vip,
                                                       binding='external')
    else:
        vault_url = vault.get_api_url()
        vault_url_external = vault.get_api_url(binding='external')
        if vault_url_external == vault_url:
            vault_url_external = None

    secrets.publish_url(vault_url=vault_url, remote_binding='access')
    if vault_url_external:
        secrets.publish_url(vault_url=vault_url_external,
                            remote_binding='external')

    if config('ssl-ca'):
        secrets.publish_ca(vault_ca=config('ssl-ca'))
Esempio n. 8
0
def create_certs():
    reissue_requested = is_flag_set('certificates.reissue.requested')
    tls = endpoint_from_flag('certificates.available')
    requests = tls.all_requests if reissue_requested else tls.new_requests
    if reissue_requested:
        log('Reissuing all certs')
    processed_applications = []
    for request in requests:
        log('Processing certificate request from {} for {}'.format(
            request.unit_name, request.common_name))
        if request.cert_type == 'application':
            cert_type = 'server'
            # When an application cert is published all units recieve the same
            # data so one need to process one request per application.
            if request.application_name in processed_applications:
                log('Already done {}'.format(request.application_name))
                continue
            else:
                processed_applications.append(request.application_name)
        else:
            cert_type = request.cert_type
        try:
            ttl = config()['default-ttl']
            max_ttl = config()['max-ttl']
            bundle = vault_pki.generate_certificate(cert_type,
                                                    request.common_name,
                                                    request.sans, ttl, max_ttl)
            request.set_cert(bundle['certificate'], bundle['private_key'])
        except vault.VaultInvalidRequest as e:
            log(str(e), level=ERROR)
            continue  # TODO: report failure back to client
    clear_flag('certificates.reissue.requested')
Esempio n. 9
0
    def _manage_flags(self):
        """
        Manage automatic relation flags.

        Internal use only.
        """
        already_joined = is_flag_set(self.expand_name('joined'))
        hook_name = hookenv.hook_name()
        rel_hook = hook_name.startswith(self.endpoint_name + '-relation-')
        departed_hook = rel_hook and hook_name.endswith('-departed')

        toggle_flag(self.expand_name('joined'), self.is_joined)

        if departed_hook:
            set_flag(self.expand_name('departed'))
        elif self.is_joined:
            clear_flag(self.expand_name('departed'))

        if already_joined and not rel_hook:
            # skip checking relation data outside hooks for this relation
            # to save on API calls to the controller (unless we didn't have
            # the joined flag before, since then we might migrating to Endpoints)
            return

        for unit in self.all_units:
            for key, value in unit.received.items():
                data_key = 'endpoint.{}.{}.{}.{}'.format(
                    self.endpoint_name, unit.relation.relation_id,
                    unit.unit_name, key)
                if data_changed(data_key, value):
                    set_flag(self.expand_name('changed'))
                    set_flag(self.expand_name('changed.{}'.format(key)))
        self.manage_flags()
Esempio n. 10
0
def send_vault_url_and_ca():
    secrets = endpoint_from_flag('secrets.connected')
    vault_url_external = None
    hostname = config('hostname')
    vip = vault.get_vip()
    if is_flag_set('ha.available'):
        if hostname:
            vault_url = vault.get_api_url(address=hostname)
        else:
            vault_url = vault.get_api_url(address=vip)
            ext_vip = vault.get_vip(binding='external')
            if ext_vip and ext_vip != vip:
                vault_url_external = vault.get_api_url(address=ext_vip,
                                                       binding='external')
    elif vip:
        log(
            "VIP is set but ha.available is not yet set, skipping "
            "send_vault_url_and_ca.",
            level=DEBUG)
        return
    else:
        vault_url = vault.get_api_url()
        vault_url_external = vault.get_api_url(binding='external')
        if vault_url_external == vault_url:
            vault_url_external = None

    secrets.publish_url(vault_url=vault_url, remote_binding='access')
    if vault_url_external:
        secrets.publish_url(vault_url=vault_url_external,
                            remote_binding='external')

    if config('ssl-ca'):
        secrets.publish_ca(vault_ca=config('ssl-ca'))
Esempio n. 11
0
def send_vault_url_and_ca():
    secrets = endpoint_from_flag('secrets.connected')
    if is_flag_set('ha.available'):
        vault_url = vault.get_api_url(address=config('vip'))
    else:
        vault_url = vault.get_api_url()
    secrets.publish_url(vault_url=vault_url)

    if config('ssl-ca'):
        secrets.publish_ca(vault_ca=config('ssl-ca'))
Esempio n. 12
0
    def test_triggers(self, kv):
        kv.return_value = MockKV()

        assert not flags.any_flags_set('foo', 'bar', 'qux')
        flags.set_flag('foo')
        assert not flags.any_flags_set('bar', 'qux')
        flags.clear_flag('foo')
        assert not flags.any_flags_set('foo', 'bar', 'qux')

        flags.register_trigger(when='foo', set_flag='bar')
        flags.set_flag('foo')
        assert flags.is_flag_set('bar')
        flags.clear_flag('foo')
        assert flags.is_flag_set('bar')
        flags.clear_flag('bar')

        flags.register_trigger(when='foo', clear_flag='qux')
        flags.set_flag('qux')
        flags.set_flag('foo')
        assert not flags.is_flag_set('qux')
Esempio n. 13
0
def create_certs():
    reissue_requested = is_flag_set('certificates.reissue.requested')
    tls = endpoint_from_flag('certificates.available')
    requests = tls.all_requests if reissue_requested else tls.new_requests
    if reissue_requested:
        log('Reissuing all certs')
    for request in requests:
        log('Processing certificate request from {} for {}'.format(
            request.unit_name, request.common_name))
        try:
            bundle = vault_pki.generate_certificate(request.cert_type,
                                                    request.common_name,
                                                    request.sans)
            request.set_cert(bundle['certificate'], bundle['private_key'])
        except vault.VaultInvalidRequest as e:
            log(str(e), level=ERROR)
            continue  # TODO: report failure back to client
    clear_flag('certificates.reissue.requested')
Esempio n. 14
0
def send_vault_url_and_ca():
    secrets = endpoint_from_flag('secrets.connected')
    lb_provider = endpoint_from_name('lb-provider')
    vault_url_external = None
    hostname = config('hostname')
    vip = vault.get_vip()
    if is_flag_set('ha.available'):
        if hostname:
            vault_url = vault.get_api_url(address=hostname)
        else:
            vault_url = vault.get_api_url(address=vip)
            ext_vip = vault.get_vip(binding='external')
            if ext_vip and ext_vip != vip:
                vault_url_external = vault.get_api_url(address=ext_vip,
                                                       binding='external')
    elif vip:
        log(
            "VIP is set but ha.available is not yet set, skipping "
            "send_vault_url_and_ca.",
            level=DEBUG)
        return
    elif lb_provider.has_response:
        response = lb_provider.get_response('vault')
        if response.error:
            log('Load balancer failed, skipping: '
                '{}'.format(response.error_message or response.error_fields),
                level=ERROR)
            return
        vault_url = vault.get_api_url(address=response.address)
        vault_url_external = vault_url
        lb_provider.ack_response(response)
    else:
        vault_url = vault.get_api_url()
        vault_url_external = vault.get_api_url(binding='external')
        if vault_url_external == vault_url:
            vault_url_external = None

    secrets.publish_url(vault_url=vault_url, remote_binding='access')
    if vault_url_external:
        secrets.publish_url(vault_url=vault_url_external,
                            remote_binding='external')

    if config('ssl-ca'):
        secrets.publish_ca(vault_ca=config('ssl-ca'))
Esempio n. 15
0
    def from_flag(cls, flag):
        """
        Return an Endpoint subclass instance based on the given flag.

        The instance that is returned depends on the endpoint name embedded
        in the flag.  Flags should be of the form ``endpoint.{name}.extra...``,
        though for legacy purposes, the ``endpoint.`` prefix can be omitted.
        The ``{name}}`` portion will be passed to
        :meth:`~charms.reactive.endpoints.Endpoint.from_name`.

        If the flag is not set, an appropriate Endpoint subclass cannot be
        found, or the flag name can't be parsed, ``None`` will be returned.
        """
        if not is_flag_set(flag) or '.' not in flag:
            return None
        parts = flag.split('.')
        if parts[0] == 'endpoint':
            return cls.from_name(parts[1])
        else:
            # some older handlers might not use the 'endpoint' prefix
            return cls.from_name(parts[0])
Esempio n. 16
0
def included_config_changed():
    # Seems it is not possible to have a @when('leadership.is_leader') together with when_file_changed()
    if flags.is_flag_set('leadership.is_leader'):
        hookenv.log('Should restart slurmctld due to changed file %s/slurm-%s.conf on disk' %
                (helpers.SLURM_CONFIG_DIR, hookenv.config().get('clustername')))
        flags.set_flag('slurm-controller.reconfigure')
Esempio n. 17
0
def upgrade_charm():
    status_set('maintenance', 'Upgrading charm..')
    if is_flag_set('cert-created'):
        clear_flag('cert-created')
        set_flag('client.cert-created')
Esempio n. 18
0
def message():
    '''Set a message to notify the user that this charm is ready.'''
    if is_flag_set('client.available'):
        status_set('active', 'Certificate Authority connected.')
    else:
        status_set('active', 'Certificate Authority ready.')
Esempio n. 19
0
def _assess_status():
    """Assess status of relations and services for local unit"""
    if is_flag_set('snap.channel.invalid'):
        status_set(
            'blocked', 'Invalid snap channel '
            'configured: {}'.format(config('channel')))
        return
    if is_flag_set('config.dns_vip.invalid'):
        status_set('blocked', 'vip and dns-ha-access-record configured')
        return
    if is_flag_set('config.lb_vip.invalid'):
        status_set('blocked', 'lb-provider and vip are mutually exclusive')
        return
    if is_flag_set('config.lb_dns.invalid'):
        status_set(
            'blocked', 'lb-provider and dns-ha-access-record are '
            'mutually exclusive')
        return

    if unitdata.kv().get('charm.vault.series-upgrading'):
        status_set(
            "blocked", "Ready for do-release-upgrade and reboot. "
            "Set complete when finished.")
        return

    if is_flag_set('failed.to.start'):
        status_set("blocked",
                   "Vault failed to start; check journalctl -u vault")
        return

    _missing_interfaces = []
    _incomplete_interfaces = []

    _assess_interface_groups(REQUIRED_INTERFACES,
                             optional=False,
                             missing_interfaces=_missing_interfaces,
                             incomplete_interfaces=_incomplete_interfaces)

    if _missing_interfaces or _incomplete_interfaces:
        state = 'blocked' if _missing_interfaces else 'waiting'
        status_set(state,
                   ', '.join(_missing_interfaces + _incomplete_interfaces))
        return

    health = None
    if service_running('vault'):
        try:
            health = vault.get_vault_health()
        except Exception:
            log(traceback.format_exc(), level=ERROR)
            status_set('blocked', 'Vault health check failed')
            return
    else:
        status_set('blocked', 'Vault service not running')
        return

    if health.get('version'):
        application_version_set(health.get('version'))
    else:
        application_version_set('Unknown')
        status_set('blocked', 'Unknown vault version')
        return

    if not health['initialized']:
        status_set('blocked', 'Vault needs to be initialized')
        return

    if health['sealed']:
        status_set('blocked', 'Unit is sealed')
        return

    if not leader_get(vault.CHARM_ACCESS_ROLE_ID):
        status_set(
            'blocked',
            'Vault charm not yet authorized: run authorize-charm action.')
        return

    if not client_approle_authorized():
        status_set('blocked', 'Vault cannot authorize approle')
        return

    lb_provider = endpoint_from_name('lb-provider')
    is_leader = is_flag_set('leadership.is_leader')
    if is_leader and lb_provider and lb_provider.is_available:
        if not lb_provider.has_response:
            status_set('waiting', 'Waiting for load balancer')
            return
        response = lb_provider.get_response('vault')
        if response.error:
            status_set(
                'blocked', 'Load balancer failed: '
                '{}'.format(response.error_message or response.error_fields))
            return

    is_leader = is_flag_set('leadership.is_leader')
    has_ca = is_flag_set('charm.vault.ca.ready')
    has_cert_reqs = is_flag_set('certificates.certs.requested')
    if is_leader and has_cert_reqs and not has_ca:
        status_set('blocked', 'Missing CA cert')
        return

    has_certs_relation = is_flag_set('certificates.available')
    if is_leader and has_certs_relation and not has_ca:
        status_set('blocked', 'Missing CA cert')
        return

    _assess_interface_groups(OPTIONAL_INTERFACES,
                             optional=True,
                             missing_interfaces=_missing_interfaces,
                             incomplete_interfaces=_incomplete_interfaces)

    if _missing_interfaces or _incomplete_interfaces:
        state = 'blocked' if _missing_interfaces else 'waiting'
        status_set(state,
                   ', '.join(_missing_interfaces + _incomplete_interfaces))
        return

    mlock_disabled = is_container() or config('disable-mlock')

    vault_installed_version = snap.get_installed_version('vault')
    vault_running_version = health.get('version')
    if vault_installed_version != vault_running_version:
        status_set(
            'active',
            'New version of vault installed, manual intervention required '
            'to restart the service.')
        return

    if is_flag_set('etcd.tls.available'):
        client = vault.get_local_client()
        if not client.ha_status['ha_enabled']:
            status_set(
                'active',
                'Vault running as non-HA, manual intervention required '
                'to restart the service.')
            return

    status_set(
        'active', 'Unit is ready '
        '(active: {}, mlock: {})'.format(
            str(not health['standby']).lower(),
            'disabled' if mlock_disabled else 'enabled'))
Esempio n. 20
0
 def test_when_set(self):
     flags.register_trigger(when='foo', set_flag='bar')
     flags.set_flag('foo')
     assert flags.is_flag_set('bar')
     flags.clear_flag('foo')
     assert flags.is_flag_set('bar')
Esempio n. 21
0
 def test_when_clear(self):
     flags.register_trigger(when='foo', clear_flag='qux')
     flags.set_flag('qux')
     flags.set_flag('foo')
     assert not flags.is_flag_set('qux')
Esempio n. 22
0
def configure_controller(*args):
    ''' A controller is only configured after leader election is
    performed. Cluster endpoint must be present for a controller to
    proceed with initial configuration'''
    hookenv.status_set('maintenance', 'Configuring slurm-controller')
    flags.clear_flag('slurm-controller.configured')

    # need to have a role determined here so that a controller context can
    # be uniformly prepared for consumption on the worker side as controller
    # and node layers share a common layer with a slurm.conf template
    # mostly identical on all nodes
    is_active = controller.is_active_controller()

    role = controller.ROLES[is_active]
    peer_role = controller.ROLES[not is_active]

    # the endpoint is present as joined is required for this handler
    cluster_endpoint = relations.endpoint_from_flag(
        'endpoint.slurm-cluster.joined')
    # Get node configs
    nodes = cluster_endpoint.get_node_data()
    partitions = controller.get_partitions(nodes)

    # Implementation of automatic node weights
    node_weight_criteria = hookenv.config().get('node_weight_criteria')
    if node_weight_criteria != 'none':
        weightres = controller.set_node_weight_criteria(node_weight_criteria, nodes)
        # If the weight configuration is incorrect, abort reconfiguration. Status
        # will be set to blocked with an informative message. The controller charm
        # will keep running.
        if not weightres:
            return

    # relation-changed does not necessarily mean that data will be provided
    if not partitions:
        flags.clear_flag('endpoint.slurm-cluster.changed')
        return

    # the whole charm config will be sent to related nodes
    # with some additional options added via dict update
    controller_conf = copy.deepcopy(hookenv.config())
    # if controller cluster config include file exists, add contents to controller_conf dict
    slurmconf_include = '%s/slurm-%s.conf' % (helpers.SLURM_CONFIG_DIR, hookenv.config().get('clustername'))
    if os.path.exists(slurmconf_include):
        f = open(slurmconf_include, "r")
        controller_conf.update({'include': f.read()})
        f.close()
    controller_conf.update({
        'nodes': nodes,
        'partitions': partitions,
        # for worker nodes
        'munge_key': hookenv.leader_get('munge_key'),
    })

    net_details = controller.add_key_prefix(
        cluster_endpoint.network_details(), role)
    # update the config dict used as a context in rendering to have prefixed
    # keys for network details based on a current unit role (active or backup)
    controller_conf.update(net_details)

    ha_endpoint = relations.endpoint_from_flag(
        'endpoint.slurm-controller-ha.joined')
    if ha_endpoint:
        # add prefixed peer data
        peer_data = controller.add_key_prefix(
            ha_endpoint.peer_data, peer_role)
        controller_conf.update(peer_data)
    else:
        peer_data = None

    # If we have a DBD relation, extract endpoint data and configure DBD setup
    # directly, regardless if the clustername gets accepted in the DBD or not
    if flags.is_flag_set('endpoint.slurm-dbd-consumer.joined') and leadership.leader_get('dbd_host'):
        dbd_host = leadership.leader_get('dbd_host')
        controller_conf.update({
            'dbd_host': leadership.leader_get('dbd_host'),
            'dbd_port': leadership.leader_get('dbd_port'),
            'dbd_ipaddr': leadership.leader_get('dbd_ipaddr')
        })


    es_endpoint = relations.endpoint_from_flag(
        'elasticsearch.available')
        
    if es_endpoint:
        for unit in es_endpoint.list_unit_data():
            elastic_host = unit['host']
            elastic_port = unit['port']
        controller_conf.update({
            'elastic_host': elastic_host,
            'elastic_port': elastic_port,
        })
        hookenv.log(("elasticsearch available, using %s:%s from endpoint relation.") % (elastic_host,elastic_port))
    else:
        hookenv.log('No endpoint for elasticsearch available')
        
        
    # In case we are here due to DBD join or charm config change, announce this to the nodes
    # by changing the value of slurm_config_updated
    if flags.is_flag_set('slurm.dbd_host_updated') or flags.is_flag_set('config.changed'):
        ts = time.time()
        hookenv.log('Slurm configuration on controller was updated on %s, annoucing to nodes' % ts)
        controller_conf.update({ 'slurm_config_updated': ts })
        flags.clear_flag('slurm.dbd_host_updated')

    # a controller service is configurable if it is an active controller
    # or a backup controller that knows about an active controller
    is_configurable = is_active or (not is_active and peer_data)
    if is_configurable:
        hookenv.log('The controller is configurable ({})'.format(role))
        # Setup slurm dirs and config
        helpers.create_state_save_location(context=controller_conf)
        helpers.render_slurm_config(context=controller_conf, active_controller=is_active)
        flags.set_flag('slurm-controller.configured')
        flags.clear_flag('slurm-controller.reconfigure')
        flags.clear_flag('slurm-controller.munge_updated')
        # restart controller process on any changes
        # TODO: this could be optimized via goal-state hook by
        # observing "joining" node units
        host.service_restart(helpers.SLURMCTLD_SERVICE)
    else:
        hookenv.log('The controller is NOT configurable ({})'.format(role))
        if not is_active:
            hookenv.status_set('maintenance',
                               'Backup controller is waiting for peer data')

    # Send config to nodes
    if is_active:
        # TODO: wait until a peer acknowledges that it has cleared
        # its side of a node-facing relation - this needs to be done
        # in case an active controller is changed to a different one
        # to avoid split-brain conditions on node units
        cluster_endpoint.send_controller_config(controller_conf)
    else:
        # otherwise make sure that all keys are cleared
        # this is relevant for a former active controller
        cluster_endpoint.send_controller_config({
            k: None for k in controller_conf.keys()
        })

    # clear the changed flag as it is not cleared automatically
    flags.clear_flag('endpoint.slurm-cluster.changed')
Esempio n. 23
0
def configure_secrets_backend():
    """ Process requests for setup and access to simple kv secret backends """
    @tenacity.retry(wait=tenacity.wait_exponential(multiplier=1, max=10),
                    stop=tenacity.stop_after_attempt(10),
                    reraise=True)
    def _check_vault_status(client):
        if (not service_running('vault') or not client.is_initialized()
                or client.is_sealed()):
            return False
        return True

    # NOTE: use localhost listener as policy only allows 127.0.0.1 to
    #       administer the local vault instances via the charm
    client = vault.get_client(url=vault.VAULT_LOCALHOST_URL)

    status_ok = _check_vault_status(client)
    if not status_ok:
        log(
            'Unable to process new secret backend requests,'
            ' deferring until vault is fully configured',
            level=DEBUG)
        return

    charm_role_id = vault.get_local_charm_access_role_id()
    if charm_role_id is None:
        log(
            'Charm access to vault not configured, deferring'
            ' secrets backend setup',
            level=DEBUG)
        return
    client.auth_approle(charm_role_id)

    secrets = (endpoint_from_flag('endpoint.secrets.new-request')
               or endpoint_from_flag('secrets.connected'))
    requests = secrets.requests()

    # Configure KV secret backends
    backends = set([request['secret_backend'] for request in requests])
    for backend in backends:
        if not backend.startswith('charm-'):
            continue
        vault.configure_secret_backend(client, name=backend)

    refresh_secrets = is_flag_set('secrets.refresh')

    # Configure AppRoles for application unit access
    for request in requests:
        # NOTE: backends must start with charm-
        backend_name = request['secret_backend']
        if not backend_name.startswith('charm-'):
            continue

        unit = request['unit']
        hostname = request['hostname']
        access_address = request['ingress_address']
        isolated = request['isolated']
        unit_name = request.get('unit_name', unit.unit_name).replace('/', '-')
        policy_name = approle_name = 'charm-{}'.format(unit_name)

        if isolated:
            policy_template = vault.SECRET_BACKEND_HCL
        else:
            policy_template = vault.SECRET_BACKEND_SHARED_HCL

        vault.configure_policy(client,
                               name=policy_name,
                               hcl=policy_template.format(backend=backend_name,
                                                          hostname=hostname))

        cidr = '{}/32'.format(access_address)
        new_role = (approle_name not in client.list_roles())

        approle_id = vault.configure_approle(client,
                                             name=approle_name,
                                             cidr=cidr,
                                             policies=[policy_name])

        if new_role or refresh_secrets:
            wrapped_secret = vault.generate_role_secret_id(client,
                                                           name=approle_name,
                                                           cidr=cidr)
            secrets.set_role_id(unit=unit,
                                role_id=approle_id,
                                token=wrapped_secret)

    clear_flag('endpoint.secrets.new-request')
    clear_flag('secrets.refresh')
Esempio n. 24
0
def _assess_status():
    """Assess status of relations and services for local unit"""
    if is_flag_set('snap.channel.invalid'):
        status_set(
            'blocked', 'Invalid snap channel '
            'configured: {}'.format(config('channel')))
        return
    if is_flag_set('config.dns_vip.invalid'):
        status_set('blocked', 'vip and dns-ha-access-record configured')
        return

    if unitdata.kv().get('charm.vault.series-upgrading'):
        status_set(
            "blocked", "Ready for do-release-upgrade and reboot. "
            "Set complete when finished.")
        return

    if is_flag_set('failed.to.start'):
        status_set("blocked",
                   "Vault failed to start; check journalctl -u vault")
        return

    _missing_interfaces = []
    _incomplete_interfaces = []

    _assess_interface_groups(REQUIRED_INTERFACES,
                             optional=False,
                             missing_interfaces=_missing_interfaces,
                             incomplete_interfaces=_incomplete_interfaces)

    _assess_interface_groups(OPTIONAL_INTERFACES,
                             optional=True,
                             missing_interfaces=_missing_interfaces,
                             incomplete_interfaces=_incomplete_interfaces)

    if _missing_interfaces or _incomplete_interfaces:
        state = 'blocked' if _missing_interfaces else 'waiting'
        status_set(state,
                   ', '.join(_missing_interfaces + _incomplete_interfaces))
        return

    health = None
    if service_running('vault'):
        try:
            health = vault.get_vault_health()
        except Exception:
            log(traceback.format_exc(), level=ERROR)
            status_set('blocked', 'Vault health check failed')
            return
    else:
        status_set('blocked', 'Vault service not running')
        return

    if health.get('version'):
        application_version_set(health.get('version'))
    else:
        application_version_set('Unknown')
        status_set('blocked', 'Unknown vault version')
        return

    if not health['initialized']:
        status_set('blocked', 'Vault needs to be initialized')
        return

    if health['sealed']:
        status_set('blocked', 'Unit is sealed')
        return

    if not leader_get(vault.CHARM_ACCESS_ROLE_ID):
        status_set(
            'blocked',
            'Vault charm not yet authorized: run authorize-charm action.')
        return

    if not client_approle_authorized():
        status_set('blocked', 'Vault cannot authorize approle')
        return

    mlock_disabled = is_container() or config('disable-mlock')

    vault_installed_version = snap.get_installed_version('vault')
    vault_running_version = health.get('version')
    if vault_installed_version != vault_running_version:
        status_set(
            'active',
            'New version of vault installed, manual intervention required '
            'to restart the service.')
        return

    status_set(
        'active', 'Unit is ready '
        '(active: {}, mlock: {})'.format(
            str(not health['standby']).lower(),
            'disabled' if mlock_disabled else 'enabled'))
Esempio n. 25
0
def message():
    """Set a message to notify the user that this charm is ready."""
    if is_flag_set("client.available"):
        status.active("Certificate Authority connected.")
    else:
        status.active("Certificate Authority ready.")