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')
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)
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')
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()
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'))
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')
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'))
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'))
def write_credentials(): credentials = endpoint_from_name('credentials') reformatted_creds = layer.openstack.get_creds_and_reformat() credentials.expose_credentials(reformatted_creds)
def test_endpoint_from_name_missing_name(self): self.assertIsNone(relations.endpoint_from_name(None))
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)
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