def handle_requests():
    azure = endpoint_from_name('clients')
    try:
        for request in azure.requests:
            layer.status.maintenance('Granting request for {} ({})'.format(
                request.vm_name, request.unit_name))
            layer.azure.ensure_msi(request)
            layer.azure.send_additional_metadata(request)
            if request.instance_tags:
                layer.azure.tag_instance(request)
            if request.requested_loadbalancer_management:
                layer.azure.enable_loadbalancer_management(request)
            if request.requested_instance_inspection:
                layer.azure.enable_instance_inspection(request)
            if request.requested_network_management:
                layer.azure.enable_network_management(request)
            if request.requested_security_management:
                layer.azure.enable_security_management(request)
            if request.requested_block_storage_management:
                layer.azure.enable_block_storage_management(request)
            if request.requested_dns_management:
                layer.azure.enable_dns_management(request)
            if request.requested_object_storage_access:
                layer.azure.enable_object_storage_access(request)
            if request.requested_object_storage_management:
                layer.azure.enable_object_storage_management(request)
            layer.azure.log('Finished request for {} ({})'.format(
                request.vm_name, request.unit_name))
        azure.mark_completed()
    except layer.azure.AzureError:
        layer.azure.log_err(format_exc())
        layer.status.blocked('error while granting requests; '
                             'check credentials and debug-log')
Example #2
0
def request_lb():
    lb_provider = endpoint_from_name('lb-provider')
    req = lb_provider.get_request('vault')
    req.protocol = req.protocols.tcp
    req.port_mapping = {8220: 8220}
    lb_provider.send_request(req)
    set_flag('vault.requested-lb')
def manage_lbs():
    lb_consumers = endpoint_from_name("lb-consumers")
    for request in lb_consumers.new_requests:
        _create_lb(request)
        lb_consumers.send_response(request)

    for request in lb_consumers.removed_requests:
        layer.azure.remove_loadbalancer(request)
Example #4
0
    def test_endpoint_from_name(self, relation_factory):
        relation_factory('relname').from_name.return_value = 'relinstance'
        relation_factory.reset_mock()

        self.assertEqual(relations.endpoint_from_name('relname'),
                         'relinstance')

        relation_factory.assert_called_once_with('relname')
        relation_factory('relname').from_name.assert_called_once_with('relname')
Example #5
0
def handle_requests():
    clients = endpoint_from_name('clients')
    for request in clients.requests:
        layer.status.maintenance(
            'granting request for {}'.format(request.unit_name))
        if not request.has_credentials:
            creds = layer.vsphere.get_user_credentials()
            request.set_credentials(**creds)
        layer.vsphere.log('Finished request for {}', request.unit_name)
    clients.mark_completed()
def update_roles():
    if not is_flag_set("charm.azure.creds.set"):
        return
    if not get_credentials()["managed-identity"]:
        return
    layer.azure.update_roles()

    lb_consumers = endpoint_from_name("lb-consumers")
    for request in lb_consumers.all_requests:
        _create_lb(request)
        lb_consumers.send_response(request)
def create_or_update_loadbalancers():
    layer.status.maintenance('Managing load balancers')
    lb_clients = endpoint_from_name('loadbalancer')
    try:
        for request in lb_clients.requests:
            if not request.members:
                continue
            lb = layer.openstack.manage_loadbalancer(request.application_name,
                                                     request.members)
            request.set_address_port(lb.fip or lb.address, lb.port)
    except layer.openstack.OpenStackError as e:
        layer.status.blocked(str(e))
def handle_requests():
    clients = endpoint_from_name('clients')
    config_change = is_flag_set('config.changed')
    requests = clients.all_requests if config_change else clients.new_requests
    for request in requests:
        layer.status.maintenance('granting request for {}'.format(
            request.unit_name))
        creds = layer.vsphere.get_vsphere_credentials()
        config = layer.vsphere.get_vsphere_config()

        request.set_credentials(**creds)
        request.set_config(**config)
        layer.vsphere.log('Finished request for {}', request.unit_name)
    clients.mark_completed()
Example #9
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'))
Example #10
0
def handle_requests():
    layer.status.maintenance('Granting integration requests')
    clients = endpoint_from_name('clients')
    config_change = is_flag_set('config.changed')
    config = hookenv.config()
    has_octavia = layer.openstack.detect_octavia()
    try:
        manage_security_groups = strtobool(config['manage-security-groups'])
        # use bool() to force True / False instead of 1 / 0
        manage_security_groups = bool(manage_security_groups)
    except ValueError:
        layer.status.blocked('Invalid value for manage-security-groups config')
        return
    except AttributeError:
        # in case manage_security_groups is already bool
        manage_security_groups = config['manage-security-groups']
    creds_changed = is_flag_set('charm.openstack.creds.changed')
    refresh_requests = config_change or creds_changed
    requests = clients.all_requests if refresh_requests else clients.new_requests
    for request in requests:
        layer.status.maintenance('Granting request for {}'.format(
            request.unit_name))
        creds = layer.openstack.get_credentials()
        request.set_credentials(**creds)
        request.set_lbaas_config(config['subnet-id'],
                                 config['floating-network-id'],
                                 config['lb-method'],
                                 manage_security_groups,
                                 has_octavia,
                                 internal_lb=config['internal-lb'])

        def _or_none(val):
            if val in (None, '', 'null'):
                return None
            else:
                return val

        request.set_block_storage_config(
            _or_none(config.get('bs-version')),
            _or_none(config.get('trust-device-path')),
            _or_none(config.get('ignore-volume-az')))
        layer.openstack.log('Finished request for {}', request.unit_name)
    clients.mark_completed()
    clear_flag('charm.openstack.creds.changed')
def handle_requests():
    gcp = endpoint_from_name('gcp')
    try:
        for request in gcp.requests:
            layer.status.maintenance('granting request for {}'.format(
                request.unit_name))
            if not request.has_credentials:
                creds = layer.gcp.create_account_key(request.model_uuid,
                                                     request.application_name,
                                                     request.relation_id)
                request.set_credentials(creds)
            if request.instance_labels:
                layer.gcp.label_instance(request.instance, request.zone,
                                         request.instance_labels)
            if request.requested_instance_inspection:
                layer.gcp.enable_instance_inspection(request.model_uuid,
                                                     request.application_name)
            if request.requested_network_management:
                layer.gcp.enable_network_management(request.model_uuid,
                                                    request.application_name)
            if request.requested_security_management:
                layer.gcp.enable_security_management(request.model_uuid,
                                                     request.application_name)
            if request.requested_block_storage_management:
                layer.gcp.enable_block_storage_management(
                    request.model_uuid, request.application_name)
            if request.requested_dns_management:
                layer.gcp.enable_dns_management(request.model_uuid,
                                                request.application_name)
            if request.requested_object_storage_access:
                layer.gcp.enable_object_storage_access(
                    request.model_uuid, request.application_name)
            if request.requested_object_storage_management:
                layer.gcp.enable_object_storage_management(
                    request.model_uuid, request.application_name)
            layer.gcp.log('Finished request for {}'.format(request.unit_name))
        gcp.mark_completed()
    except layer.gcp.GCPError:
        layer.gcp.log_err(format_exc())
        layer.status.blocked('error while granting requests; '
                             'check credentials and debug-log')
def handle_requests():
    azure = endpoint_from_name("clients")
    creds_data = get_credentials()
    try:
        for request in azure.requests:
            layer.status.maintenance("Granting request for {} ({})".format(
                request.vm_name, request.unit_name))
            layer.azure.send_additional_metadata(request)
            if not creds_data["managed-identity"]:
                # We don't need to perform operations on the VMs.
                # The Service Principal is taking care of ops.
                continue
            layer.azure.ensure_msi(request)
            if request.instance_tags:
                layer.azure.tag_instance(request)
            if request.requested_loadbalancer_management:
                layer.azure.enable_loadbalancer_management(request)
            if request.requested_instance_inspection:
                layer.azure.enable_instance_inspection(request)
            if request.requested_network_management:
                layer.azure.enable_network_management(request)
            if request.requested_security_management:
                layer.azure.enable_security_management(request)
            if request.requested_block_storage_management:
                layer.azure.enable_block_storage_management(request)
            if request.requested_dns_management:
                layer.azure.enable_dns_management(request)
            if request.requested_object_storage_access:
                layer.azure.enable_object_storage_access(request)
            if request.requested_object_storage_management:
                layer.azure.enable_object_storage_management(request)
            layer.azure.log("Finished request for {} ({})".format(
                request.vm_name, request.unit_name))
        azure.mark_completed()
    except layer.azure.AzureError:
        layer.azure.log_err(format_exc())
        layer.status.blocked("error while granting requests; "
                             "check credentials and debug-log")
def no_requests():
    gcp = endpoint_from_name('gcp')
    layer.gcp.cleanup(gcp.relation_ids)
    layer.status.active('ready')
Example #14
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'))
Example #15
0
 def test_endpoint_from_name_missing_factory(self, relation_factory):
     relation_factory.return_value = None
     self.assertIsNone(relations.endpoint_from_name('relname'))
     relation_factory.assert_called_once_with('relname')
def port_changed():
    prometheus = endpoint_from_name('scrape')
    log("Port changed, telling relations. ({})".format(config('port')))
    prometheus.configure(port=config('port'))
Example #17
0
def write_credentials():
    credentials = endpoint_from_name('credentials')
    reformatted_creds = layer.openstack.get_creds_and_reformat()
    credentials.expose_credentials(reformatted_creds)
Example #18
0
 def test_endpoint_from_name_missing_name(self):
     self.assertIsNone(relations.endpoint_from_name(None))
Example #19
0
def restore():
    """
    Implementation of easyrsa 'restore' action

    Backup restoration process can be summarized as following:

        * Selected backup is scanned and verified
        * Contents of the backup are unpacked into <cahrm_dir>/EasyRSA/pki
        * Data that are stored in the local database are updated
        * All units that have relation with this easyrsa unit will be notified
          about the certificate changes.
    """
    pki_dir = os.path.join(easyrsa_directory, "pki")
    backup_name = function_get("name")

    if backup_name is None:
        raise RuntimeError("Parameter 'name' is required.")

    log("Restoring pki from backup file {}".format(backup_name), hookenv.INFO)

    backup_path = os.path.join(PKI_BACKUP, backup_name)

    if not os.path.isfile(backup_path):
        log("Backup file '{}' does not exists.".format(backup_path), hookenv.ERROR)
        raise RuntimeError(
            "Backup with name '{}' does not exist. Use action "
            "'list-backups' to list all available "
            "backups".format(backup_name)
        )

    with tarfile.open(backup_path, "r:gz") as pki_tar:
        _verify_backup(pki_tar)
        _replace_pki(pki_tar, pki_dir)

    cert_dir = os.path.join(pki_dir, "issued")
    key_dir = os.path.join(pki_dir, "private")

    # Update CA and global client data stored in the local leader's database
    # NOTE(mkalcok): Easyrsa does not really support HA mode, so it's usually
    #                run as a single unit/model
    _update_leadership_data(pki_dir, cert_dir, key_dir)

    ca_cert = leader_get("certificate_authority")
    tls = endpoint_from_name("client")
    log("Sending CA certificate to all related units", hookenv.INFO)
    tls.set_ca(ca_cert)
    log("Sending global client certificate and key to all related units", hookenv.INFO)
    tls.set_client_cert(leader_get("client_certificate"), leader_get("client_key"))
    for client in tls.all_requests:
        try:
            cert_file = os.path.join(cert_dir, "{}.crt".format(client.common_name))
            key_file = os.path.join(key_dir, "{}.key".format(client.common_name))
            with open(cert_file, "r") as file:
                cert = file.read()
            with open(key_file, "r") as file:
                key = file.read()
            log(
                "Sending certificate for '{}' to unit"
                "'{}'".format(client.common_name, client.unit_name),
                hookenv.INFO,
            )
            log(cert, hookenv.DEBUG)
            client.set_cert(cert, key)

        except FileNotFoundError:
            log(
                "Certificate for '{}' not found in backup. " "Generating new one.",
                hookenv.INFO,
            )
            if client.cert_type == "client":
                cert, key = create_client_certificate(client.common_name)
            elif client.cert_type == "server":
                cert, key = create_server_certificate(
                    client.common_name, client.sans, client.common_name
                )
            else:
                # This use case should not really happen as easyrsa charm
                # does not support Application type certificates
                raise RuntimeError(
                    "Unrecognized certificate request type "
                    '"{}".'.format(client.cert_type)
                )
            log(
                "Sending certificate for '{}' to unit"
                "'{}'".format(client.common_name, client.unit_name),
                hookenv.INFO,
            )
            log(cert, hookenv.DEBUG)
            client.set_cert(cert, key)

    hookenv._run_atexit()
def cleanup():
    lb_consumers = endpoint_from_name("lb-consumers")
    # These are expected to both always be empty lists (since the relations should have
    # already been removed by this point), but we do this anyway JIC.
    for request in lb_consumers.all_requests + lb_consumers.removed_requests:
        layer.azure.remove_loadbalancer(request)
Example #21
0
 def arg_gen():
     # use a generator to defer calling of hookenv.relation_type, for tests
     rel = endpoint_from_name(hookenv.relation_type())
     if rel:
         yield rel