def create_ip_cert_links(ssl_dir, custom_hostname_link=None):
    """Create symlinks for SAN records

    :param ssl_dir: str Directory to create symlinks in
    :param custom_hostname_link: str Additional link to be created
    """
    hostname = get_hostname(unit_get('private-address'))
    hostname_cert = os.path.join(
        ssl_dir,
        'cert_{}'.format(hostname))
    hostname_key = os.path.join(
        ssl_dir,
        'key_{}'.format(hostname))
    # Add links to hostname cert, used if os-hostname vars not set
    for net_type in [INTERNAL, ADMIN, PUBLIC]:
        try:
            addr = resolve_address(endpoint_type=net_type)
            cert = os.path.join(ssl_dir, 'cert_{}'.format(addr))
            key = os.path.join(ssl_dir, 'key_{}'.format(addr))
            if os.path.isfile(hostname_cert) and not os.path.isfile(cert):
                os.symlink(hostname_cert, cert)
                os.symlink(hostname_key, key)
        except NoNetworkBinding:
            log("Skipping creating cert symlink for ip in {} space, no "
                "local address found".format(net_type), WARNING)
    if custom_hostname_link:
        custom_cert = os.path.join(
            ssl_dir,
            'cert_{}'.format(custom_hostname_link))
        custom_key = os.path.join(
            ssl_dir,
            'key_{}'.format(custom_hostname_link))
        if os.path.isfile(hostname_cert) and not os.path.isfile(custom_cert):
            os.symlink(hostname_cert, custom_cert)
            os.symlink(hostname_key, custom_key)
Exemple #2
0
def get_certificate_sans(bindings=None):
    """Get all possible IP addresses for certificate SANs.
    """
    _sans = [unit_get('private-address')]
    if bindings:
        # Add default API bindings to bindings list
        bindings = set(bindings + get_default_api_bindings())
    else:
        # Use default API bindings
        bindings = get_default_api_bindings()

    for binding in bindings:
        # Check for config override
        try:
            net_config = config(ADDRESS_MAP[binding]['config'])
        except KeyError:
            # There is no configuration network for this binding name
            net_config = None
        # Using resolve_address is likely redundant. Keeping it here in
        # case there is an edge case it handles.
        net_addr = resolve_address(endpoint_type=binding)
        ip = get_relation_ip(binding, cidr_network=net_config)
        _sans = _sans + [net_addr, ip]
        vip = get_vip_in_network(resolve_network_cidr(ip))
        if vip:
            _sans.append(vip)
    return set(_sans)
    def __call__(self):
        ctxt = {}
        servers = []
        try:
            for rid in relation_ids('memcache'):
                for rel in relations_for_id(rid):
                    priv_addr = rel['private-address']
                    # Format it as IPv6 address if needed
                    priv_addr = format_ipv6_addr(priv_addr) or priv_addr
                    servers.append("%s:%s" % (priv_addr, rel['port']))
        except Exception as ex:
            log("Could not get memcache servers: %s" % (ex), level='WARNING')
            servers = []

        ctxt['memcached_servers'] = ','.join(servers)

        # Configure nova-novncproxy https if nova-api is using https.
        if https():
            cn = resolve_address(endpoint_type=INTERNAL)
            if cn:
                cert_filename = 'cert_{}'.format(cn)
                key_filename = 'key_{}'.format(cn)
            else:
                cert_filename = 'cert'
                key_filename = 'key'

            ssl_dir = '/etc/apache2/ssl/nova'
            cert = os.path.join(ssl_dir, cert_filename)
            key = os.path.join(ssl_dir, key_filename)
            if os.path.exists(cert) and os.path.exists(key):
                ctxt['ssl_cert'] = cert
                ctxt['ssl_key'] = key

        return ctxt
Exemple #4
0
    def __call__(self):
        ctxt = super(NovaConfigContext, self).__call__()
        ctxt['scheduler_default_filters'] = (
            hookenv.config('scheduler-default-filters'))
        if hookenv.config('pci-alias'):
            aliases = json.loads(hookenv.config('pci-alias'))
            if isinstance(aliases, list):
                ctxt['pci_aliases'] = [
                    json.dumps(x, sort_keys=True) for x in aliases
                ]
            else:
                ctxt['pci_alias'] = json.dumps(aliases, sort_keys=True)

        ctxt['disk_allocation_ratio'] = hookenv.config('disk-allocation-ratio')
        ctxt['cpu_allocation_ratio'] = hookenv.config('cpu-allocation-ratio')
        ctxt['ram_allocation_ratio'] = hookenv.config('ram-allocation-ratio')
        addr = ch_ip.resolve_address(ch_ip.INTERNAL)
        ctxt['host_ip'] = ch_network_ip.format_ipv6_addr(addr) or addr
        ctxt['quota_instances'] = hookenv.config('quota-instances')
        ctxt['quota_cores'] = hookenv.config('quota-cores')
        ctxt['quota_ram'] = hookenv.config('quota-ram')
        ctxt['quota_metadata_items'] = hookenv.config('quota-metadata-items')
        ctxt['quota_injected_files'] = hookenv.config('quota-injected-files')
        ctxt['quota_injected_file_content_bytes'] = hookenv.config(
            'quota-injected-file-size')
        ctxt['quota_injected_file_path_length'] = hookenv.config(
            'quota-injected-path-size')
        ctxt['quota_key_pairs'] = hookenv.config('quota-key-pairs')
        ctxt['quota_server_groups'] = hookenv.config('quota-server-groups')
        ctxt['quota_server_group_members'] = hookenv.config(
            'quota-server-group-members')
        return ctxt
def get_certificate_request(json_encode=True):
    """Generate a certificatee requests based on the network confioguration

    """
    req = CertRequest(json_encode=json_encode)
    req.add_hostname_cn()
    # Add os-hostname entries
    for net_type in [INTERNAL, ADMIN, PUBLIC]:
        net_config = config(ADDRESS_MAP[net_type]['override'])
        try:
            net_addr = resolve_address(endpoint_type=net_type)
            ip = network_get_primary_address(ADDRESS_MAP[net_type]['binding'])
            addresses = [net_addr, ip]
            vip = get_vip_in_network(resolve_network_cidr(ip))
            if vip:
                addresses.append(vip)
            if net_config:
                req.add_entry(net_type, net_config, addresses)
            else:
                # There is network address with no corresponding hostname.
                # Add the ip to the hostname cert to allow for this.
                req.add_hostname_cn_ip(addresses)
        except NoNetworkBinding:
            log(
                "Skipping request for certificate for ip in {} space, no "
                "local address found".format(net_type), WARNING)
    return req.get_request()
Exemple #6
0
    def __call__(self):
        ctxt = {}
        servers = []
        try:
            for rid in relation_ids('memcache'):
                for rel in relations_for_id(rid):
                    priv_addr = rel['private-address']
                    # Format it as IPv6 address if needed
                    priv_addr = format_ipv6_addr(priv_addr) or priv_addr
                    servers.append("%s:%s" % (priv_addr, rel['port']))
        except Exception as ex:
            log("Could not get memcache servers: %s" % (ex), level='WARNING')
            servers = []

        ctxt['memcached_servers'] = ','.join(servers)

        # Configure nova-novncproxy https if nova-api is using https.
        if https():
            cn = resolve_address(endpoint_type=INTERNAL)
            if cn:
                cert_filename = 'cert_{}'.format(cn)
                key_filename = 'key_{}'.format(cn)
            else:
                cert_filename = 'cert'
                key_filename = 'key'

            ssl_dir = '/etc/apache2/ssl/nova'
            cert = os.path.join(ssl_dir, cert_filename)
            key = os.path.join(ssl_dir, key_filename)
            if os.path.exists(cert) and os.path.exists(key):
                ctxt['ssl_cert'] = cert
                ctxt['ssl_key'] = key

        return ctxt
Exemple #7
0
 def test_resolve_address_default_clustered(self):
     self.is_clustered.return_value = True
     self.test_config.set('vip', '10.5.3.1')
     self.assertEquals(ip.resolve_address(), '10.5.3.1')
     self.config.assert_has_calls(
         [call('vip'),
          call('os-public-network')])
def create_ip_cert_links(ssl_dir, custom_hostname_link=None):
    """Create symlinks for SAN records

    :param ssl_dir: str Directory to create symlinks in
    :param custom_hostname_link: str Additional link to be created
    """
    hostname = get_hostname(unit_get('private-address'))
    hostname_cert = os.path.join(ssl_dir, 'cert_{}'.format(hostname))
    hostname_key = os.path.join(ssl_dir, 'key_{}'.format(hostname))
    # Add links to hostname cert, used if os-hostname vars not set
    for net_type in [INTERNAL, ADMIN, PUBLIC]:
        try:
            addr = resolve_address(endpoint_type=net_type)
            cert = os.path.join(ssl_dir, 'cert_{}'.format(addr))
            key = os.path.join(ssl_dir, 'key_{}'.format(addr))
            if os.path.isfile(hostname_cert) and not os.path.isfile(cert):
                os.symlink(hostname_cert, cert)
                os.symlink(hostname_key, key)
        except NoNetworkBinding:
            log(
                "Skipping creating cert symlink for ip in {} space, no "
                "local address found".format(net_type), WARNING)
    if custom_hostname_link:
        custom_cert = os.path.join(ssl_dir,
                                   'cert_{}'.format(custom_hostname_link))
        custom_key = os.path.join(ssl_dir,
                                  'key_{}'.format(custom_hostname_link))
        if os.path.isfile(hostname_cert) and not os.path.isfile(custom_cert):
            os.symlink(hostname_cert, custom_cert)
            os.symlink(hostname_key, custom_key)
def get_certificate_request(json_encode=True):
    """Generate a certificatee requests based on the network confioguration

    """
    req = CertRequest(json_encode=json_encode)
    req.add_hostname_cn()
    # Add os-hostname entries
    for net_type in [INTERNAL, ADMIN, PUBLIC]:
        net_config = config(ADDRESS_MAP[net_type]['override'])
        try:
            net_addr = resolve_address(endpoint_type=net_type)
            ip = network_get_primary_address(
                ADDRESS_MAP[net_type]['binding'])
            addresses = [net_addr, ip]
            vip = get_vip_in_network(resolve_network_cidr(ip))
            if vip:
                addresses.append(vip)
            if net_config:
                req.add_entry(
                    net_type,
                    net_config,
                    addresses)
            else:
                # There is network address with no corresponding hostname.
                # Add the ip to the hostname cert to allow for this.
                req.add_hostname_cn_ip(addresses)
        except NoNetworkBinding:
            log("Skipping request for certificate for ip in {} space, no "
                "local address found".format(net_type), WARNING)
    return req.get_request()
Exemple #10
0
def websso_trusted_dashboard_changed():
    """
    Provide L7 endpoint details for the dashboard and also
    handle any config changes that may affect those.
    """
    relations = relation_ids('websso-trusted-dashboard')
    if not relations:
        return

    # TODO: check for vault relation in order to determine url scheme
    tls_configured = (relation_ids('certificates') or config('ssl-key')
                      or config('enforce-ssl'))
    scheme = 'https://' if tls_configured else 'http://'

    hostname = resolve_address(endpoint_type=PUBLIC, override=True)

    # urljoin needs a base url to be '/'-terminated contrary to the joined path
    webroot = config('webroot')
    if not webroot.endswith('/'):
        webroot += '/'

    path = urllib.parse.urljoin(webroot, "auth/websso/")
    # provide trusted dashboard URL details
    for rid in relations:
        relation_set(relation_id=rid,
                     relation_settings={
                         "scheme": scheme,
                         "hostname": hostname,
                         "path": path,
                     })
 def __call__(self):
     ctxt = super(NovaConfigContext, self).__call__()
     ctxt['scheduler_default_filters'] = config('scheduler-default-filters')
     ctxt['cpu_allocation_ratio'] = config('cpu-allocation-ratio')
     ctxt['ram_allocation_ratio'] = config('ram-allocation-ratio')
     addr = resolve_address(INTERNAL)
     ctxt['host_ip'] = format_ipv6_addr(addr) or addr
     return ctxt
Exemple #12
0
 def __call__(self):
     ctxt = super(NovaConfigContext, self).__call__()
     ctxt['scheduler_default_filters'] = config('scheduler-default-filters')
     ctxt['cpu_allocation_ratio'] = config('cpu-allocation-ratio')
     ctxt['ram_allocation_ratio'] = config('ram-allocation-ratio')
     addr = resolve_address(INTERNAL)
     ctxt['host_ip'] = format_ipv6_addr(addr) or addr
     return ctxt
Exemple #13
0
 def test_resolve_address_default_internal(self):
     self.is_clustered.return_value = False
     self.unit_get.return_value = 'unit1'
     self.get_address_in_network.return_value = 'unit1'
     self.assertEquals(ip.resolve_address(ip.INTERNAL), 'unit1')
     self.unit_get.assert_called_with('private-address')
     calls = [call('os-internal-network'), call('prefer-ipv6')]
     self.config.assert_has_calls(calls)
Exemple #14
0
 def test_resolve_address_default(self):
     self.is_clustered.return_value = False
     self.unit_get.return_value = 'unit1'
     self.get_address_in_network.return_value = 'unit1'
     self.assertEquals(ip.resolve_address(), 'unit1')
     self.unit_get.assert_called_with('public-address')
     calls = [call('os-public-network'), call('prefer-ipv6')]
     self.config.assert_has_calls(calls)
     self.get_address_in_network.assert_called_with(None, 'unit1')
Exemple #15
0
def configure_ceph_exporter_relation(target):
    config = hookenv.config()
    if data_changed('target.config', config):
        try:
            hostname = hookenv.network_get_primary_address('target').decode(
                "utf-8")
        except NotImplementedError:
            hostname = resolve_address(endpoint_type=INTERNAL)
        target.configure(hostname=hostname, port=config.get('port'))
        hookenv.status_set('active', 'Ready')
def canonical_url():
    """Returns the correct HTTP URL to this host given the state of HTTPS
    configuration and hacluster.
    """
    scheme = 'http'
    if https():
        scheme = 'https'

    addr = resolve_address(INTERNAL)
    return '%s://%s' % (scheme, format_ipv6_addr(addr) or addr)
 def __call__(self):
     if NeutronAPIContext()()['enable_dvr'] or \
             config('enable-local-dhcp-and-metadata'):
         ctxt = {
             'shared_secret': get_shared_secret(),
             'local_ip': resolve_address(),
         }
     else:
         ctxt = {}
     return ctxt
Exemple #18
0
def canonical_url():
    """Returns the correct HTTP URL to this host given the state of HTTPS
    configuration and hacluster.
    """
    scheme = 'http'
    if https():
        scheme = 'https'

    addr = resolve_address(INTERNAL)
    return '%s://%s' % (scheme, format_ipv6_addr(addr) or addr)
    def __call__(self):
        ip_addr = ch_ip.resolve_address(endpoint_type=ch_ip.PUBLIC)
        ip_addr = ch_network_ip.format_ipv6_addr(ip_addr) or ip_addr

        ctxt = {
            'enable_serial_console':
                str(hookenv.config('enable-serial-console')).lower(),
            'serial_console_base_url': 'ws://{}:6083/'.format(ip_addr)
        }
        return ctxt
 def __call__(self):
     if NeutronAPIContext()()['enable_dvr'] or \
             config('enable-local-dhcp-and-metadata'):
         ctxt = {
             'shared_secret': get_shared_secret(),
             'local_ip': resolve_address(),
         }
     else:
         ctxt = {}
     return ctxt
Exemple #21
0
    def __call__(self):
        ip_addr = ch_ip.resolve_address(endpoint_type=ch_ip.PUBLIC)
        ip_addr = ch_network_ip.format_ipv6_addr(ip_addr) or ip_addr

        ctxt = {
            'enable_serial_console':
                str(hookenv.config('enable-serial-console')).lower(),
            'serial_console_base_url': 'ws://{}:6083/'.format(ip_addr)
        }
        return ctxt
Exemple #22
0
def admin_relation_changed(relation_id=None):
    # TODO: fixup
    relation_data = {
        'service_hostname': resolve_address(ADMIN),
        'service_port': config('service-port'),
        'service_username': config('admin-user'),
        'service_tenant_name': config('admin-role'),
        'service_region': config('region'),
    }
    relation_data['service_password'] = get_admin_passwd()
    relation_set(relation_id=relation_id, **relation_data)
def admin_relation_changed(relation_id=None):
    # TODO: fixup
    relation_data = {
        'service_hostname': resolve_address(ADMIN),
        'service_port': config('service-port'),
        'service_username': config('admin-user'),
        'service_tenant_name': config('admin-role'),
        'service_region': config('region'),
    }
    relation_data['service_password'] = get_admin_passwd()
    relation_set(relation_id=relation_id, **relation_data)
Exemple #24
0
 def test_resolve_address_public_not_clustered(self):
     self.is_clustered.return_value = False
     self.test_config.set('os-public-network', '192.168.20.0/24')
     self.unit_get.return_value = 'unit1'
     self.get_address_in_network.return_value = '192.168.20.1'
     self.assertEquals(ip.resolve_address(), '192.168.20.1')
     self.unit_get.assert_called_with('public-address')
     calls = [call('os-public-network'), call('prefer-ipv6')]
     self.config.assert_has_calls(calls)
     self.get_address_in_network.assert_called_with('192.168.20.0/24',
                                                    'unit1')
Exemple #25
0
 def test_resolve_address_network_get(self):
     self.is_clustered.return_value = False
     self.unit_get.return_value = 'unit1'
     self.network_get_primary_address.side_effect = None
     self.network_get_primary_address.return_value = '10.5.60.1'
     self.assertEqual(ip.resolve_address(), '10.5.60.1')
     self.unit_get.assert_called_with('public-address')
     calls = [call('os-public-network'),
              call('prefer-ipv6')]
     self.config.assert_has_calls(calls)
     self.network_get_primary_address.assert_called_with('public')
Exemple #26
0
 def test_resolve_address_network_get_clustered(self):
     self.is_clustered.return_value = True
     self.test_config.set('vip', '10.5.60.20 192.168.1.20')
     self.network_get_primary_address.side_effect = None
     self.network_get_primary_address.return_value = '10.5.60.1'
     self.resolve_network_cidr.return_value = '10.5.60.1/24'
     self.assertEqual(ip.resolve_address(), '10.5.60.20')
     calls = [call('os-public-hostname'),
              call('vip'),
              call('os-public-network')]
     self.config.assert_has_calls(calls)
     self.network_get_primary_address.assert_called_with('public')
Exemple #27
0
def admin_relation_changed(relation_id=None):
    # TODO: fixup
    relation_data = {
        "service_hostname": resolve_address(ADMIN),
        "service_port": config("service-port"),
        "service_username": config("admin-user"),
        "service_tenant_name": config("admin-role"),
        "service_region": config("region"),
        "service_protocol": "https" if https() else "http",
    }
    relation_data["service_password"] = get_admin_passwd()
    relation_set(relation_id=relation_id, **relation_data)
Exemple #28
0
def get_certificate_request(json_encode=True, bindings=None):
    """Generate a certificate requests based on the network configuration

    :param json_encode: Encode request in JSON or not. Used for setting
                        directly on a relation.
    :type json_encode: boolean
    :param bindings: List of bindings to check in addition to default api
                     bindings.
    :type bindings: list of strings
    :returns: CertRequest request as dictionary or JSON string.
    :rtype: Union[dict, json]
    """
    if bindings:
        # Add default API bindings to bindings list
        bindings = set(bindings + get_default_api_bindings())
    else:
        # Use default API bindings
        bindings = get_default_api_bindings()
    req = CertRequest(json_encode=json_encode)
    req.add_hostname_cn()
    # Add os-hostname entries
    _sans = get_certificate_sans()

    # Handle specific hostnames per binding
    for binding in bindings:
        hostname_override = config(ADDRESS_MAP[binding]['override'])
        try:
            net_addr = resolve_address(endpoint_type=binding)
            ip = network_get_primary_address(ADDRESS_MAP[binding]['binding'])
            addresses = [net_addr, ip]
            vip = get_vip_in_network(resolve_network_cidr(ip))
            if vip:
                addresses.append(vip)
            # Add hostname certificate request
            if hostname_override:
                req.add_entry(binding, hostname_override, addresses)
            # Remove hostname specific addresses from _sans
            for addr in addresses:
                try:
                    _sans.remove(addr)
                except (ValueError, KeyError):
                    pass

        except NoNetworkBinding:
            log(
                "Skipping request for certificate for ip in {} space, no "
                "local address found".format(binding), WARNING)
    # Gurantee all SANs are covered
    # These are network addresses with no corresponding hostname.
    # Add the ips to the hostname cert to allow for this.
    req.add_hostname_cn_ip(_sans)
    return req.get_request()
Exemple #29
0
    def __call__(self):
        ''' Grab cert and key from configuraton for SSL config '''
        ctxt = {'http_port': 70, 'https_port': 433}

        if config('enforce-ssl'):
            # NOTE(dosaboy): if ssl is not configured we shouldn't allow this
            if all(get_cert()):
                ctxt['ssl_addr'] = resolve_address()
            else:
                log(
                    "Enforce ssl redirect requested but ssl not configured - "
                    "skipping redirect",
                    level=WARNING)

        return ctxt
    def __call__(self):
        ctxt = {}

        if (hookenv.config('console-ssl-cert') and
                hookenv.config('console-ssl-key') and
                hookenv.config('console-access-protocol')):
            ssl_dir = '/etc/nova/ssl/'
            if not os.path.exists(ssl_dir):
                hookenv.log('Creating %s.' % ssl_dir, level=hookenv.DEBUG)
                os.mkdir(ssl_dir)

            cert_path = os.path.join(ssl_dir, 'nova_cert.pem')
            decode_ssl_cert = base64.b64decode(
                hookenv.config('console-ssl-cert'))

            key_path = os.path.join(ssl_dir, 'nova_key.pem')
            decode_ssl_key = base64.b64decode(
                hookenv.config('console-ssl-key'))

            with open(cert_path, 'wb') as fh:
                fh.write(decode_ssl_cert)
            with open(key_path, 'wb') as fh:
                fh.write(decode_ssl_key)

            ctxt['ssl_only'] = True
            ctxt['ssl_cert'] = cert_path
            ctxt['ssl_key'] = key_path

            if ch_cluster.is_clustered():
                ip_addr = ch_ip.resolve_address(endpoint_type=ch_ip.PUBLIC)
            else:
                ip_addr = hookenv.unit_get('private-address')

            ip_addr = ch_network_ip.format_ipv6_addr(ip_addr) or ip_addr

            _proto = hookenv.config('console-access-protocol')
            url = "https://%s:%s%s" % (
                ip_addr,
                common.console_attributes('proxy-port', proto=_proto),
                common.console_attributes('proxy-page', proto=_proto))

            if _proto == 'novnc':
                ctxt['novncproxy_base_url'] = url
            elif _proto == 'spice':
                ctxt['html5proxy_base_url'] = url

        return ctxt
Exemple #31
0
    def __call__(self):
        ctxt = {}

        if (hookenv.config('console-ssl-cert') and
                hookenv.config('console-ssl-key') and
                hookenv.config('console-access-protocol')):
            ssl_dir = '/etc/nova/ssl/'
            if not os.path.exists(ssl_dir):
                hookenv.log('Creating %s.' % ssl_dir, level=hookenv.DEBUG)
                os.mkdir(ssl_dir)

            cert_path = os.path.join(ssl_dir, 'nova_cert.pem')
            decode_ssl_cert = base64.b64decode(
                hookenv.config('console-ssl-cert'))

            key_path = os.path.join(ssl_dir, 'nova_key.pem')
            decode_ssl_key = base64.b64decode(
                hookenv.config('console-ssl-key'))

            with open(cert_path, 'wb') as fh:
                fh.write(decode_ssl_cert)
            with open(key_path, 'wb') as fh:
                fh.write(decode_ssl_key)

            ctxt['ssl_only'] = True
            ctxt['ssl_cert'] = cert_path
            ctxt['ssl_key'] = key_path

            if ch_cluster.is_clustered():
                ip_addr = ch_ip.resolve_address(endpoint_type=ch_ip.PUBLIC)
            else:
                ip_addr = hookenv.unit_get('private-address')

            ip_addr = ch_network_ip.format_ipv6_addr(ip_addr) or ip_addr

            _proto = hookenv.config('console-access-protocol')
            url = "https://%s:%s%s" % (
                ip_addr,
                common.console_attributes('proxy-port', proto=_proto),
                common.console_attributes('proxy-page', proto=_proto))

            if _proto == 'novnc':
                ctxt['novncproxy_base_url'] = url
            elif _proto == 'spice':
                ctxt['html5proxy_base_url'] = url

        return ctxt
def console_settings():
    rel_settings = {}
    proto = common.console_attributes('protocol')
    if not proto:
        return {}
    rel_settings['console_keymap'] = hookenv.config('console-keymap')
    rel_settings['console_access_protocol'] = proto

    console_ssl = False
    if (hookenv.config('console-ssl-cert') and
            hookenv.config('console-ssl-key')):
        console_ssl = True

    if hookenv.config('console-proxy-ip') == 'local':
        if console_ssl:
            address = ch_ip.resolve_address(endpoint_type=ch_ip.PUBLIC)
            address = ch_network_ip.format_ipv6_addr(address) or address
            proxy_base_addr = 'https://%s' % address
        else:
            # canonical_url will only return 'https:' if API SSL are enabled.
            proxy_base_addr = ch_ip.canonical_url(CONFIGS, ch_ip.PUBLIC)
    else:
        if console_ssl or ch_cluster.https():
            schema = "https"
        else:
            schema = "http"

        proxy_base_addr = ("{}://{}"
                           .format(schema, hookenv.config('console-proxy-ip')))

    if proto == 'vnc':
        protocols = ['novnc', 'xvpvnc']
    else:
        protocols = [proto]
    for _proto in protocols:
        rel_settings['console_proxy_{}_address'.format(_proto)] = \
            "{}:{}{}".format(
                proxy_base_addr,
                common.console_attributes('proxy-port', proto=_proto),
                common.console_attributes('proxy-page', proto=_proto))
        rel_settings['console_proxy_%s_host' % (_proto)] = \
            urlparse(proxy_base_addr).hostname
        rel_settings['console_proxy_%s_port' % (_proto)] = \
            common.console_attributes('proxy-port', proto=_proto)

    return rel_settings
def console_settings():
    rel_settings = {}
    proto = common.console_attributes('protocol')
    if not proto:
        return {}
    rel_settings['console_keymap'] = hookenv.config('console-keymap')
    rel_settings['console_access_protocol'] = proto

    console_ssl = False
    if (hookenv.config('console-ssl-cert') and
            hookenv.config('console-ssl-key')):
        console_ssl = True

    if hookenv.config('console-proxy-ip') == 'local':
        if console_ssl:
            address = ch_ip.resolve_address(endpoint_type=ch_ip.PUBLIC)
            address = ch_network_ip.format_ipv6_addr(address) or address
            proxy_base_addr = 'https://%s' % address
        else:
            # canonical_url will only return 'https:' if API SSL are enabled.
            proxy_base_addr = ch_ip.canonical_url(CONFIGS, ch_ip.PUBLIC)
    else:
        if console_ssl or ch_cluster.https():
            schema = "https"
        else:
            schema = "http"

        proxy_base_addr = ("{}://{}"
                           .format(schema, hookenv.config('console-proxy-ip')))

    if proto == 'vnc':
        protocols = ['novnc', 'xvpvnc']
    else:
        protocols = [proto]
    for _proto in protocols:
        rel_settings['console_proxy_{}_address'.format(_proto)] = \
            "{}:{}{}".format(
                proxy_base_addr,
                common.console_attributes('proxy-port', proto=_proto),
                common.console_attributes('proxy-page', proto=_proto))
        rel_settings['console_proxy_%s_host' % (_proto)] = \
            urlparse(proxy_base_addr).hostname
        rel_settings['console_proxy_%s_port' % (_proto)] = \
            common.console_attributes('proxy-port', proto=_proto)

    return rel_settings
Exemple #34
0
def console_settings():
    rel_settings = {}
    proto = console_attributes('protocol')
    if not proto:
        return {}
    rel_settings['console_keymap'] = config('console-keymap')
    rel_settings['console_access_protocol'] = proto

    console_ssl = False
    if config('console-ssl-cert') and config('console-ssl-key'):
        console_ssl = True

    if config('console-proxy-ip') == 'local':
        if console_ssl:
            address = resolve_address(endpoint_type=PUBLIC)
            address = format_ipv6_addr(address) or address
            proxy_base_addr = 'https://%s' % address
        else:
            # canonical_url will only return 'https:' if API SSL are enabled.
            proxy_base_addr = canonical_url(CONFIGS, PUBLIC)
    else:
        if console_ssl or https():
            schema = "https"
        else:
            schema = "http"

        proxy_base_addr = "%s://%s" % (schema, config('console-proxy-ip'))

    if proto == 'vnc':
        protocols = ['novnc', 'xvpvnc']
    else:
        protocols = [proto]
    for _proto in protocols:
        rel_settings['console_proxy_%s_address' % (_proto)] = \
            "%s:%s%s" % (proxy_base_addr,
                         console_attributes('proxy-port', proto=_proto),
                         console_attributes('proxy-page', proto=_proto))
        rel_settings['console_proxy_%s_host' % (_proto)] = \
            urlparse(proxy_base_addr).hostname
        rel_settings['console_proxy_%s_port' % (_proto)] = \
            console_attributes('proxy-port', proto=_proto)

    return rel_settings
def console_settings():
    rel_settings = {}
    proto = console_attributes('protocol')
    if not proto:
        return {}
    rel_settings['console_keymap'] = config('console-keymap')
    rel_settings['console_access_protocol'] = proto

    console_ssl = False
    if config('console-ssl-cert') and config('console-ssl-key'):
        console_ssl = True

    if config('console-proxy-ip') == 'local':
        if console_ssl:
            address = resolve_address(endpoint_type=PUBLIC)
            address = format_ipv6_addr(address) or address
            proxy_base_addr = 'https://%s' % address
        else:
            # canonical_url will only return 'https:' if API SSL are enabled.
            proxy_base_addr = canonical_url(CONFIGS, PUBLIC)
    else:
        if console_ssl or https():
            schema = "https"
        else:
            schema = "http"

        proxy_base_addr = "%s://%s" % (schema, config('console-proxy-ip'))

    if proto == 'vnc':
        protocols = ['novnc', 'xvpvnc']
    else:
        protocols = [proto]
    for _proto in protocols:
        rel_settings['console_proxy_%s_address' % (_proto)] = \
            "%s:%s%s" % (proxy_base_addr,
                         console_attributes('proxy-port', proto=_proto),
                         console_attributes('proxy-page', proto=_proto))
        rel_settings['console_proxy_%s_host' % (_proto)] = \
            urlparse(proxy_base_addr).hostname
        rel_settings['console_proxy_%s_port' % (_proto)] = \
            console_attributes('proxy-port', proto=_proto)

    return rel_settings
    def __call__(self):
        ctxt = super(NovaConfigContext, self).__call__()
        ctxt['scheduler_default_filters'] = (
            hookenv.config('scheduler-default-filters'))
        if hookenv.config('pci-alias'):
            aliases = json.loads(hookenv.config('pci-alias'))
            if isinstance(aliases, list):
                ctxt['pci_aliases'] = [
                    json.dumps(x, sort_keys=True) for x in aliases
                ]
            else:
                ctxt['pci_alias'] = json.dumps(aliases, sort_keys=True)

        ctxt['disk_allocation_ratio'] = hookenv.config('disk-allocation-ratio')
        ctxt['cpu_allocation_ratio'] = hookenv.config('cpu-allocation-ratio')
        ctxt['ram_allocation_ratio'] = hookenv.config('ram-allocation-ratio')
        addr = ch_ip.resolve_address(ch_ip.INTERNAL)
        ctxt['host_ip'] = ch_network_ip.format_ipv6_addr(addr) or addr
        return ctxt
    def __call__(self):
        ctxt = super(NovaConfigContext, self).__call__()
        ctxt['scheduler_default_filters'] = (
            hookenv.config('scheduler-default-filters') or
            ','.join(default_enabled_filters()))
        if hookenv.config('pci-alias'):
            aliases = json.loads(hookenv.config('pci-alias'))
            if isinstance(aliases, list):
                ctxt['pci_aliases'] = [json.dumps(x, sort_keys=True)
                                       for x in aliases]
            else:
                ctxt['pci_alias'] = json.dumps(aliases, sort_keys=True)

        ctxt['disk_allocation_ratio'] = hookenv.config('disk-allocation-ratio')
        ctxt['cpu_allocation_ratio'] = hookenv.config('cpu-allocation-ratio')
        ctxt['ram_allocation_ratio'] = hookenv.config('ram-allocation-ratio')
        ctxt['enable_new_services'] = hookenv.config('enable-new-services')
        addr = ch_ip.resolve_address(ch_ip.INTERNAL)
        ctxt['host_ip'] = ch_network_ip.format_ipv6_addr(addr) or addr
        ctxt['quota_instances'] = hookenv.config('quota-instances')
        ctxt['quota_cores'] = hookenv.config('quota-cores')
        ctxt['quota_ram'] = hookenv.config('quota-ram')
        ctxt['quota_metadata_items'] = hookenv.config('quota-metadata-items')
        ctxt['quota_injected_files'] = hookenv.config('quota-injected-files')
        ctxt['quota_injected_file_content_bytes'] = hookenv.config(
            'quota-injected-file-size')
        ctxt['quota_injected_file_path_length'] = hookenv.config(
            'quota-injected-path-size')
        ctxt['quota_key_pairs'] = hookenv.config('quota-key-pairs')
        ctxt['quota_server_groups'] = hookenv.config('quota-server-groups')
        ctxt['quota_server_group_members'] = hookenv.config(
            'quota-server-group-members')
        ctxt['quota_count_usage_from_placement'] = hookenv.config(
            'quota-count-usage-from-placement')
        ctxt['console_access_protocol'] = hookenv.config(
            'console-access-protocol')
        ctxt['console_access_port'] = hookenv.config('console-access-port')
        ctxt['scheduler_host_subset_size'] = hookenv.config(
            'scheduler-host-subset-size')
        ctxt['unique_server_names'] = hookenv.config('unique-server-names')
        ctxt['skip_hosts_with_build_failures'] = hookenv.config(
            'skip-hosts-with-build-failures')
        return ctxt
Exemple #38
0
def update_keystone_fid_service_provider(relation_id=None):
    if relation_ids('certificates'):
        tls_enabled = True
    else:
        tls_enabled = (config('ssl_cert') is not None
                       and config('ssl_key') is not None)
    # NOTE: thedac Use resolve_address which checks host name, VIP  and
    # network bindings. Use PUBLIC for now. Possible TODO make this
    # configurable?
    hostname = resolve_address(endpoint_type=PUBLIC, override=True)
    # reactive endpoints implementation on the other side, hence
    # json-encoded values
    fid_settings = {
        'hostname': json.dumps(hostname),
        'port': json.dumps(config('service-port')),
        'tls-enabled': json.dumps(tls_enabled),
    }

    relation_set(relation_id=relation_id, relation_settings=fid_settings)
 def __call__(self):
     ctxt = {}
     for rid in relation_ids('cloud-compute'):
         for unit in related_units(rid):
             rel = {'rid': rid, 'unit': unit}
             proto = relation_get('console_access_protocol', **rel)
             if not proto:
                 # only bother with units that have a proto set.
                 continue
             ctxt['console_keymap'] = relation_get('console_keymap', **rel)
             ctxt['console_access_protocol'] = proto
             ctxt['console_vnc_type'] = True if 'vnc' in proto else False
             if proto == 'vnc':
                 ctxt = dict(ctxt, **self.get_console_info('xvpvnc', **rel))
                 ctxt = dict(ctxt, **self.get_console_info('novnc', **rel))
             else:
                 ctxt = dict(ctxt, **self.get_console_info(proto, **rel))
             break
     ctxt['console_listen_addr'] = resolve_address(endpoint_type=INTERNAL)
     return ctxt
def update_keystone_fid_service_provider(relation_id=None):
    if relation_ids('certificates'):
        tls_enabled = True
    else:
        tls_enabled = (config('ssl_cert') is not None and
                       config('ssl_key') is not None)
    # NOTE: thedac Use resolve_address which checks host name, VIP  and
    # network bindings. Use PUBLIC for now. Possible TODO make this
    # configurable?
    hostname = resolve_address(endpoint_type=PUBLIC, override=True)
    # reactive endpoints implementation on the other side, hence
    # json-encoded values
    fid_settings = {
        'hostname': json.dumps(hostname),
        'port': json.dumps(config('service-port')),
        'tls-enabled': json.dumps(tls_enabled),
    }

    relation_set(relation_id=relation_id,
                 relation_settings=fid_settings)
    def __call__(self):
        ctxt = {}
        # Configure nova-novncproxy https if nova-api is using https.
        if ch_cluster.https():
            cn = ch_ip.resolve_address(endpoint_type=ch_ip.INTERNAL)
            if cn:
                cert_filename = 'cert_{}'.format(cn)
                key_filename = 'key_{}'.format(cn)
            else:
                cert_filename = 'cert'
                key_filename = 'key'

            ssl_dir = '/etc/apache2/ssl/nova'
            cert = os.path.join(ssl_dir, cert_filename)
            key = os.path.join(ssl_dir, key_filename)
            if os.path.exists(cert) and os.path.exists(key):
                ctxt['ssl_cert'] = cert
                ctxt['ssl_key'] = key

        return ctxt
 def __call__(self):
     ctxt = {}
     for rid in relation_ids('cloud-compute'):
         for unit in related_units(rid):
             rel = {'rid': rid, 'unit': unit}
             proto = relation_get('console_access_protocol', **rel)
             if not proto:
                 # only bother with units that have a proto set.
                 continue
             ctxt['console_keymap'] = relation_get('console_keymap', **rel)
             ctxt['console_access_protocol'] = proto
             ctxt['console_vnc_type'] = True if 'vnc' in proto else False
             if proto == 'vnc':
                 ctxt = dict(ctxt, **self.get_console_info('xvpvnc', **rel))
                 ctxt = dict(ctxt, **self.get_console_info('novnc', **rel))
             else:
                 ctxt = dict(ctxt, **self.get_console_info(proto, **rel))
             break
     ctxt['console_listen_addr'] = resolve_address(endpoint_type=INTERNAL)
     return ctxt
    def __call__(self):
        ctxt = {}
        # Configure nova-novncproxy https if nova-api is using https.
        if ch_cluster.https():
            cn = ch_ip.resolve_address(endpoint_type=ch_ip.INTERNAL)
            if cn:
                cert_filename = 'cert_{}'.format(cn)
                key_filename = 'key_{}'.format(cn)
            else:
                cert_filename = 'cert'
                key_filename = 'key'

            ssl_dir = '/etc/apache2/ssl/nova'
            cert = os.path.join(ssl_dir, cert_filename)
            key = os.path.join(ssl_dir, key_filename)
            if os.path.exists(cert) and os.path.exists(key):
                ctxt['ssl_cert'] = cert
                ctxt['ssl_key'] = key

        return ctxt
def admin_relation_changed(relation_id=None):
    # TODO: fixup
    if expect_ha() and not is_clustered():
        log("Expected to be HA but no hacluster relation yet", level=INFO)
        return
    relation_data = {
        'service_hostname': resolve_address(ADMIN),
        'service_port': config('service-port'),
        'service_username': config('admin-user'),
        'service_tenant_name': config('admin-role'),
        'service_region': config('region'),
        'service_protocol': 'https' if https() else 'http',
        'api_version': get_api_version(),
    }
    if relation_data['api_version'] > 2:
        relation_data['service_user_domain_name'] = ADMIN_DOMAIN
        relation_data['service_project_domain_name'] = ADMIN_DOMAIN
        relation_data['service_project_name'] = ADMIN_PROJECT
    relation_data['service_password'] = get_admin_passwd()
    relation_set(relation_id=relation_id, **relation_data)
Exemple #45
0
def admin_relation_changed(relation_id=None):
    # TODO: fixup
    if expect_ha() and not is_clustered():
        log("Expected to be HA but no hacluster relation yet", level=INFO)
        return
    relation_data = {
        'service_hostname': resolve_address(ADMIN),
        'service_port': config('service-port'),
        'service_username': config('admin-user'),
        'service_tenant_name': config('admin-role'),
        'service_region': config('region'),
        'service_protocol': 'https' if https() else 'http',
        'api_version': get_api_version(),
    }
    if relation_data['api_version'] > 2:
        relation_data['service_user_domain_name'] = ADMIN_DOMAIN
        relation_data['service_project_domain_name'] = ADMIN_DOMAIN
        relation_data['service_project_name'] = ADMIN_PROJECT
    relation_data['service_password'] = get_admin_passwd()
    relation_set(relation_id=relation_id, **relation_data)
Exemple #46
0
    def __call__(self):
        if isinstance(self.external_ports, six.string_types):
            self.external_ports = [self.external_ports]

        if not self.external_ports or not https():
            return {}

        self.configure_ca()
        self.enable_modules()

        ctxt = {
            'namespace': self.service_namespace,
            'endpoints': [],
            'ext_ports': []
        }

        cns = self.canonical_names()
        if cns:
            for cn in cns:
                self.configure_cert(cn)
        else:
            # Expect cert/key provided in config (currently assumed that ca
            # uses ip for cn)
            cn = resolve_address(endpoint_type=INTERNAL)
            self.configure_cert(cn)

        addresses = self.get_network_addresses()
        for address, endpoint in sorted(set(addresses)):
            for api_port in self.external_ports:
                ext_port = determine_apache_port(api_port,
                                                 singlenode_mode=True)
                int_port = determine_api_port(api_port, singlenode_mode=True)
                portmap = (address, endpoint, int(ext_port), int(int_port))
                ctxt['endpoints'].append(portmap)
                ctxt['ext_ports'].append(int(ext_port))

        ctxt['ext_ports'] = sorted(list(set(ctxt['ext_ports'])))
        return ctxt
def websso_trusted_dashboard_changed():
    """
    Provide L7 endpoint details for the dashboard and also
    handle any config changes that may affect those.
    """
    relations = relation_ids('websso-trusted-dashboard')
    if not relations:
        return

    # TODO: check for vault relation in order to determine url scheme
    tls_configured = (relation_ids('certificates') or
                      config('ssl-key') or config('enforce-ssl'))
    scheme = 'https://' if tls_configured else 'http://'

    hostname = resolve_address(endpoint_type=PUBLIC, override=True)
    path = "{}/auth/websso/".format(config('webroot'))
    # provide trusted dashboard URL details
    for rid in relations:
        relation_set(relation_id=rid, relation_settings={
            "scheme": scheme,
            "hostname": hostname,
            "path": path,
        })
    def __call__(self):
        ctxt = super(NovaConfigContext, self).__call__()
        ctxt['scheduler_default_filters'] = (
            hookenv.config('scheduler-default-filters') or
            ','.join(default_enabled_filters()))
        if hookenv.config('pci-alias'):
            aliases = json.loads(hookenv.config('pci-alias'))
            if isinstance(aliases, list):
                ctxt['pci_aliases'] = [json.dumps(x, sort_keys=True)
                                       for x in aliases]
            else:
                ctxt['pci_alias'] = json.dumps(aliases, sort_keys=True)

        ctxt['disk_allocation_ratio'] = hookenv.config('disk-allocation-ratio')
        ctxt['cpu_allocation_ratio'] = hookenv.config('cpu-allocation-ratio')
        ctxt['ram_allocation_ratio'] = hookenv.config('ram-allocation-ratio')
        ctxt['enable_new_services'] = hookenv.config('enable-new-services')
        addr = ch_ip.resolve_address(ch_ip.INTERNAL)
        ctxt['host_ip'] = ch_network_ip.format_ipv6_addr(addr) or addr
        ctxt['quota_instances'] = hookenv.config('quota-instances')
        ctxt['quota_cores'] = hookenv.config('quota-cores')
        ctxt['quota_ram'] = hookenv.config('quota-ram')
        ctxt['quota_metadata_items'] = hookenv.config('quota-metadata-items')
        ctxt['quota_injected_files'] = hookenv.config('quota-injected-files')
        ctxt['quota_injected_file_content_bytes'] = hookenv.config(
            'quota-injected-file-size')
        ctxt['quota_injected_file_path_length'] = hookenv.config(
            'quota-injected-path-size')
        ctxt['quota_key_pairs'] = hookenv.config('quota-key-pairs')
        ctxt['quota_server_groups'] = hookenv.config('quota-server-groups')
        ctxt['quota_server_group_members'] = hookenv.config(
            'quota-server-group-members')
        ctxt['console_access_protocol'] = hookenv.config(
            'console-access-protocol')
        ctxt['console_access_port'] = hookenv.config('console-access-port')
        return ctxt
Exemple #49
0
    def __call__(self):
        if isinstance(self.external_ports, six.string_types):
            self.external_ports = [self.external_ports]

        if not self.external_ports or not https():
            return {}

        self.configure_ca()
        self.enable_modules()

        ctxt = {'namespace': self.service_namespace,
                'endpoints': [],
                'ext_ports': []}

        cns = self.canonical_names()
        if cns:
            for cn in cns:
                self.configure_cert(cn)
        else:
            # Expect cert/key provided in config (currently assumed that ca
            # uses ip for cn)
            cn = resolve_address(endpoint_type=INTERNAL)
            self.configure_cert(cn)

        addresses = self.get_network_addresses()
        for address, endpoint in sorted(set(addresses)):
            for api_port in self.external_ports:
                ext_port = determine_apache_port(api_port,
                                                 singlenode_mode=True)
                int_port = determine_api_port(api_port, singlenode_mode=True)
                portmap = (address, endpoint, int(ext_port), int(int_port))
                ctxt['endpoints'].append(portmap)
                ctxt['ext_ports'].append(int(ext_port))

        ctxt['ext_ports'] = sorted(list(set(ctxt['ext_ports'])))
        return ctxt
def update_hacluster_dns_ha(service, relation_data,
                            crm_ocf='ocf:maas:dns'):
    """ Configure DNS-HA resources based on provided configuration

    @param service: Name of the service being configured
    @param relation_data: Pointer to dictionary of relation data.
    @param crm_ocf: Corosync Open Cluster Framework resource agent to use for
                    DNS HA
    """
    # Validate the charm environment for DNS HA
    assert_charm_supports_dns_ha()

    settings = ['os-admin-hostname', 'os-internal-hostname',
                'os-public-hostname', 'os-access-hostname']

    # Check which DNS settings are set and update dictionaries
    hostname_group = []
    for setting in settings:
        hostname = config(setting)
        if hostname is None:
            log('DNS HA: Hostname setting {} is None. Ignoring.'
                ''.format(setting),
                DEBUG)
            continue
        m = re.search('os-(.+?)-hostname', setting)
        if m:
            endpoint_type = m.group(1)
            # resolve_address's ADDRESS_MAP uses 'int' not 'internal'
            if endpoint_type == 'internal':
                endpoint_type = 'int'
        else:
            msg = ('Unexpected DNS hostname setting: {}. '
                   'Cannot determine endpoint_type name'
                   ''.format(setting))
            status_set('blocked', msg)
            raise DNSHAException(msg)

        hostname_key = 'res_{}_{}_hostname'.format(service, endpoint_type)
        if hostname_key in hostname_group:
            log('DNS HA: Resource {}: {} already exists in '
                'hostname group - skipping'.format(hostname_key, hostname),
                DEBUG)
            continue

        hostname_group.append(hostname_key)
        relation_data['resources'][hostname_key] = crm_ocf
        relation_data['resource_params'][hostname_key] = (
            'params fqdn="{}" ip_address="{}"'
            .format(hostname, resolve_address(endpoint_type=endpoint_type,
                                              override=False)))

    if len(hostname_group) >= 1:
        log('DNS HA: Hostname group is set with {} as members. '
            'Informing the ha relation'.format(' '.join(hostname_group)),
            DEBUG)
        relation_data['groups'] = {
            'grp_{}_hostnames'.format(service): ' '.join(hostname_group)
        }
    else:
        msg = 'DNS HA: Hostname group has no members.'
        status_set('blocked', msg)
        raise DNSHAException(msg)
Exemple #51
0
def update_dns_ha_resource_params(resources, resource_params,
                                  relation_id=None,
                                  crm_ocf='ocf:maas:dns'):
    """ Check for os-*-hostname settings and update resource dictionaries for
    the HA relation.

    @param resources: Pointer to dictionary of resources.
                      Usually instantiated in ha_joined().
    @param resource_params: Pointer to dictionary of resource parameters.
                            Usually instantiated in ha_joined()
    @param relation_id: Relation ID of the ha relation
    @param crm_ocf: Corosync Open Cluster Framework resource agent to use for
                    DNS HA
    """

    # Validate the charm environment for DNS HA
    assert_charm_supports_dns_ha()

    settings = ['os-admin-hostname', 'os-internal-hostname',
                'os-public-hostname', 'os-access-hostname']

    # Check which DNS settings are set and update dictionaries
    hostname_group = []
    for setting in settings:
        hostname = config(setting)
        if hostname is None:
            log('DNS HA: Hostname setting {} is None. Ignoring.'
                ''.format(setting),
                DEBUG)
            continue
        m = re.search('os-(.+?)-hostname', setting)
        if m:
            networkspace = m.group(1)
        else:
            msg = ('Unexpected DNS hostname setting: {}. '
                   'Cannot determine network space name'
                   ''.format(setting))
            status_set('blocked', msg)
            raise DNSHAException(msg)

        hostname_key = 'res_{}_{}_hostname'.format(charm_name(), networkspace)
        if hostname_key in hostname_group:
            log('DNS HA: Resource {}: {} already exists in '
                'hostname group - skipping'.format(hostname_key, hostname),
                DEBUG)
            continue

        hostname_group.append(hostname_key)
        resources[hostname_key] = crm_ocf
        resource_params[hostname_key] = (
            'params fqdn="{}" ip_address="{}" '
            ''.format(hostname, resolve_address(endpoint_type=networkspace,
                                                override=False)))

    if len(hostname_group) >= 1:
        log('DNS HA: Hostname group is set with {} as members. '
            'Informing the ha relation'.format(' '.join(hostname_group)),
            DEBUG)
        relation_set(relation_id=relation_id, groups={
            'grp_{}_hostnames'.format(charm_name()): ' '.join(hostname_group)})
    else:
        msg = 'DNS HA: Hostname group has no members.'
        status_set('blocked', msg)
        raise DNSHAException(msg)