def get_network_addresses(self): """For each network configured, return corresponding address and vip (if available). Returns a list of tuples of the form: [(address_in_net_a, vip_in_net_a), (address_in_net_b, vip_in_net_b), ...] or, if no vip(s) available: [(address_in_net_a, address_in_net_a), (address_in_net_b, address_in_net_b), ...] """ addresses = [] for net_type in ADDRESS_TYPES: net_cfg_opt = os_ip.ADDRESS_MAP[net_type]['config'].replace( '-', '_') config_cidr = getattr(self, net_cfg_opt, None) addr = ch_ip.get_address_in_network( config_cidr, hookenv.unit_get('private-address')) addresses.append( (addr, os_ip.resolve_address(endpoint_type=net_type))) return sorted(addresses)
def external_endpoints(self): """Dict of service names and attributes that clients use to connect @return { 'svc1': { 'proto': 'http', 'ip': '10.0.0.10', 'port': '8080', 'url': 'http://10.0.0.10:8080}, 'svc2': { 'proto': 'https', 'ip': '10.0.0.20', 'port': '8443', 'url': 'https://10.0.0.20:8443}, ... """ info = {} proto = 'https' if self.apache_enabled else 'http' if self.port_map: for service in self.port_map.keys(): key = service.replace('-', '_') info[key] = { 'proto': proto, 'ip': os_ip.resolve_address(os_ip.ADMIN), 'port': self.port_map[service][os_ip.ADMIN] } info[key]['url'] = '{proto}://{ip}:{port}'.format(**info[key]) return info
def get_default_cn(self): """Return the default Canonical Name to be used for TLS setup :returns: 'canonical_name' :rtype: str """ return os_ip.resolve_address(endpoint_type=os_ip.INTERNAL)
def external_endpoints(self): """Dict of service names and attributes that clients use to connect @return { 'svc1': { 'proto': 'http', 'ip': '10.0.0.10', 'port': '8080', 'url': 'http://10.0.0.10:8080}, 'svc2': { 'proto': 'https', 'ip': '10.0.0.20', 'port': '8443', 'url': 'https://10.0.0.20:8443}, ... """ info = {} proto = 'https' if self.apache_enabled else 'http' if self.port_map: for service in self.port_map.keys(): key = service.replace('-', '_') info[key] = { 'proto': proto, 'ip': os_ip.resolve_address(os_ip.ADMIN), 'port': self.port_map[service][os_ip.ADMIN]} info[key]['url'] = '{proto}://{ip}:{port}'.format(**info[key]) return info
def _add_dnsha_config(self, hacluster): """Add a DNSHA object to self.resources @param hacluster instance of interface class HAClusterRequires """ if not self.config.get(DNSHA_KEY): return settings = ['os-admin-hostname', 'os-internal-hostname', 'os-public-hostname', 'os-access-hostname'] for setting in settings: hostname = self.config.get(setting) if hostname is None: hookenv.log( 'DNS HA: Hostname setting {} is None. Ignoring.'.format( setting), hookenv.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)) hookenv.status_set('blocked', msg) raise os_ha.DNSHAException(msg) ip = os_ip.resolve_address( endpoint_type=endpoint_type, override=False) hacluster.add_dnsha(self.name, ip, hostname, endpoint_type)
def get_network_addresses(self): """For each network configured, return corresponding address and vip (if available). Returns a list of tuples of the form: [(address_in_net_a, vip_in_net_a), (address_in_net_b, vip_in_net_b), ...] or, if no vip(s) available: [(address_in_net_a, address_in_net_a), (address_in_net_b, address_in_net_b), ...] """ addresses = [] for net_type in ADDRESS_TYPES: net_cfg_opt = os_ip.ADDRESS_MAP[net_type]['config'].replace('-', '_') config_cidr = getattr(self, net_cfg_opt, None) addr = ch_ip.get_address_in_network( config_cidr, hookenv.unit_get('private-address')) addresses.append( (addr, os_ip.resolve_address(endpoint_type=net_type))) return sorted(addresses)
def get_certs_and_keys(self, keystone_interface=None, certificates_interface=None): """Collect SSL config for local endpoints SSL keys and certs may come from user specified configuration for this charm or they may come directly from Keystone. If collecting from keystone there may be a certificate and key per endpoint (public, admin etc). @returns [ {'key': 'key1', 'cert': 'cert1', 'ca': 'ca1', 'cn': 'cn1'} {'key': 'key2', 'cert': 'cert2', 'ca': 'ca2', 'cn': 'cn2'} ... ] """ if self.config_defined_ssl_key and self.config_defined_ssl_cert: ssl_artifacts = [] for ep_type in [os_ip.INTERNAL, os_ip.ADMIN, os_ip.PUBLIC]: ssl_artifacts.append({ 'key': self.config_defined_ssl_key.decode('utf-8'), 'cert': self.config_defined_ssl_cert.decode('utf-8'), 'ca': (self.config_defined_ssl_ca.decode('utf-8') if self.config_defined_ssl_ca else None), 'cn': os_ip.resolve_address(endpoint_type=ep_type)}) return ssl_artifacts elif keystone_interface: keys_and_certs = [] for addr in self.get_local_addresses(): key = keystone_interface.get_ssl_key(addr) cert = keystone_interface.get_ssl_cert(addr) ca = keystone_interface.get_ssl_ca() if key and cert: keys_and_certs.append({ 'key': key, 'cert': cert, 'ca': ca, 'cn': addr}) return keys_and_certs elif certificates_interface: keys_and_certs = [] reqs = certificates_interface.get_batch_requests() ca = certificates_interface.get_ca() chain = certificates_interface.get_chain() for cn, data in sorted(reqs.items()): cert = data['cert'] if chain: cert = cert + os.linesep + chain keys_and_certs.append({ 'key': data['key'], 'cert': cert, 'ca': ca, 'cn': cn}) return keys_and_certs else: return []
def get_certs_and_keys(self, keystone_interface=None, certificates_interface=None): """Collect TLS config for local endpoints TLS keys and certs may come from user specified configuration for this charm or they may come directly from the ``certificates`` relation. If collecting from ``certificates`` relation there may be a certificate and key per endpoint (public, admin etc). :param keystone_interface: DEPRECATED Functionality removed. :type keystone_interace: Option[None, KeystoneRequires(RelationBase)] :param certificates_interface: Certificates interface object :type certificates_interface: TlsRequires(Endpoint) :returns: [ {'key': 'key1', 'cert': 'cert1', 'ca': 'ca1', 'cn': 'cn1'} {'key': 'key2', 'cert': 'cert2', 'ca': 'ca2', 'cn': 'cn2'} ... ] :rtype: List[Dict[str,str]] """ if self.config_defined_ssl_key and self.config_defined_ssl_cert: ssl_artifacts = [] for ep_type in [os_ip.INTERNAL, os_ip.ADMIN, os_ip.PUBLIC]: ssl_artifacts.append({ 'key': self.config_defined_ssl_key.decode('utf-8'), 'cert': self.config_defined_ssl_cert.decode('utf-8'), 'ca': (self.config_defined_ssl_ca.decode('utf-8') if self.config_defined_ssl_ca else None), 'cn': os_ip.resolve_address(endpoint_type=ep_type) }) return ssl_artifacts elif certificates_interface: keys_and_certs = [] reqs = certificates_interface.get_batch_requests() ca = certificates_interface.get_ca() chain = certificates_interface.get_chain() for cn, data in sorted(reqs.items()): cert = data['cert'] if chain: cert = cert + os.linesep + chain keys_and_certs.append({ 'key': data['key'], 'cert': cert, 'ca': ca, 'chain': chain, 'cn': cn }) return keys_and_certs else: return []
def get_local_addresses(self): """Return list of local addresses on each configured network For each network return an address the local unit has on that network if one exists. @returns [private_addr, admin_addr, public_addr, ...] """ addresses = [os_utils.get_host_ip(hookenv.unit_get('private-address'))] for addr_type in os_ip.ADDRESS_MAP.keys(): laddr = os_ip.resolve_address(endpoint_type=addr_type) if laddr: addresses.append(laddr) return sorted(list(set(addresses)))
def get_local_addresses(self): """Return list of local addresses on each configured network For each network return an address the local unit has on that network if one exists. @returns [private_addr, admin_addr, public_addr, ...] """ addresses = [ os_utils.get_host_ip(hookenv.unit_get('private-address'))] for addr_type in os_ip.ADDRESS_MAP.keys(): laddr = os_ip.resolve_address(endpoint_type=addr_type) if laddr: addresses.append(laddr) return sorted(list(set(addresses)))
def configure_cert(self, cert, key, cn=None): """Configure service SSL cert and key Write out service SSL certificate and key for Apache. @param cert string SSL Certificate @param key string SSL Key @param cn string Canonical name for service """ if not cn: cn = os_ip.resolve_address(endpoint_type=os_ip.INTERNAL) ssl_dir = os.path.join('/etc/apache2/ssl/', self.name) ch_host.mkdir(path=ssl_dir) if cn: cert_filename = 'cert_{}'.format(cn) key_filename = 'key_{}'.format(cn) else: cert_filename = 'cert' key_filename = 'key' ch_host.write_file(path=os.path.join(ssl_dir, cert_filename), content=cert.encode('utf-8')) ch_host.write_file(path=os.path.join(ssl_dir, key_filename), content=key.encode('utf-8'))
def get_default_cn(self): """Return the default Canonical Name to be used for SSL setup @returns 'canonical_name' """ return os_ip.resolve_address(endpoint_type=os_ip.INTERNAL)
def test_resolve_address(self): self.patch_object(ip.cluster, 'is_clustered') self.patch_object(ip.hookenv, 'config') self.patch_object(ip.hookenv, 'network_get_primary_address') self.patch_object(ip.net_ip, 'is_address_in_network') self.patch_object(ip.net_ip, 'get_ipv6_addr') self.patch_object(ip.hookenv, 'unit_get') self.patch_object(ip.net_ip, 'get_address_in_network') # define a fake_config() that returns predictable results and remembers # what it was called with. calls_list = [] _config = { 'vip': 'vip-address', 'prefer-ipv6': False, 'os-public-network': 'the-public-network', 'os-public-hostname': None, 'os-internal-network': 'the-internal-network', 'os-admin-network': 'the-admin-network', } def fake_config(*args): calls_list.append(args) return _config[args[0]] self.config.side_effect = fake_config # Juju pre 2.0 behaviour where network-get is not implemented self.network_get_primary_address.side_effect = NotImplementedError # first test, if not clustered, that the function uses unit_get() and # get_address_in_network to get a real address. # for the default PUBLIC endpoint self.is_clustered.return_value = False self.get_address_in_network.return_value = 'got-address' self.unit_get.return_value = 'unit-get-address' addr = ip.resolve_address() self.assertEqual(addr, 'got-address') self.assertEqual(calls_list, [('os-public-hostname', ), ('vip', ), ('os-public-network', ), ('prefer-ipv6', )]) self.unit_get.assert_called_once_with('public-address') self.get_address_in_network.assert_called_once_with( 'the-public-network', 'unit-get-address') # second test: not clustered, prefer-ipv6 is True _config['prefer-ipv6'] = True calls_list = [] self.get_ipv6_addr.return_value = ['ipv6-addr'] self.get_address_in_network.reset_mock() addr = ip.resolve_address() self.get_ipv6_addr.assert_called_once_with(exc_list=['vip-address']) self.get_address_in_network.assert_called_once_with( 'the-public-network', 'ipv6-addr') # Third test: clustered, and config(...) returns None self.is_clustered.return_value = True _config['os-public-network'] = None calls_list = [] addr = ip.resolve_address() self.assertEqual(calls_list, [('os-public-hostname', ), ('vip', ), ('os-public-network', )]) # Fourth test: clustered, and config(...) returns not None _config['os-public-network'] = 'the-public-network' calls_list = [] _config['vip'] = 'vip1 vip2' def _fake_addr_in_net(address, vip): return True if vip == 'vip2' else False self.is_address_in_network.side_effect = _fake_addr_in_net addr = ip.resolve_address() self.assertEqual(calls_list, [ ('os-public-hostname', ), ('vip', ), ('os-public-network', ), ]) self.assertEqual(addr, 'vip2') # Finally resolved_address returns None -> ValueError() # allow vip to not be found: self.is_address_in_network.return_value = False self.is_address_in_network.side_effect = None with self.assertRaises(ValueError): addr = ip.resolve_address()
def get_certs_and_keys(self, keystone_interface=None, certificates_interface=None): """Collect SSL config for local endpoints SSL keys and certs may come from user specified configuration for this charm or they may come directly from Keystone. If collecting from keystone there may be a certificate and key per endpoint (public, admin etc). @returns [ {'key': 'key1', 'cert': 'cert1', 'ca': 'ca1', 'cn': 'cn1'} {'key': 'key2', 'cert': 'cert2', 'ca': 'ca2', 'cn': 'cn2'} ... ] """ if self.config_defined_ssl_key and self.config_defined_ssl_cert: ssl_artifacts = [] for ep_type in [os_ip.INTERNAL, os_ip.ADMIN, os_ip.PUBLIC]: ssl_artifacts.append({ 'key': self.config_defined_ssl_key.decode('utf-8'), 'cert': self.config_defined_ssl_cert.decode('utf-8'), 'ca': (self.config_defined_ssl_ca.decode('utf-8') if self.config_defined_ssl_ca else None), 'cn': os_ip.resolve_address(endpoint_type=ep_type) }) return ssl_artifacts elif keystone_interface: keys_and_certs = [] for addr in self.get_local_addresses(): key = keystone_interface.get_ssl_key(addr) cert = keystone_interface.get_ssl_cert(addr) ca = keystone_interface.get_ssl_ca() if key and cert: keys_and_certs.append({ 'key': key, 'cert': cert, 'ca': ca, 'cn': addr }) return keys_and_certs elif certificates_interface: keys_and_certs = [] reqs = certificates_interface.get_batch_requests() ca = certificates_interface.get_ca() chain = certificates_interface.get_chain() for cn, data in sorted(reqs.items()): cert = data['cert'] if chain: cert = cert + os.linesep + chain keys_and_certs.append({ 'key': data['key'], 'cert': cert, 'ca': ca, 'cn': cn }) return keys_and_certs else: return []
def access_ip(self): return resolve_address()
def rndc_master_ip(config): """Returns IP address slave DNS slave should use to query master """ return os_ip.resolve_address(endpoint_type=os_ip.INTERNAL)
def test_resolve_address_network_binding(self): self.patch_object(ip.cluster, 'is_clustered') self.patch_object(ip.hookenv, 'config') self.patch_object(ip.hookenv, 'network_get_primary_address') self.patch_object(ip.net_ip, 'is_address_in_network') self.patch_object(ip.net_ip, 'get_ipv6_addr') self.patch_object(ip.hookenv, 'unit_get') self.patch_object(ip.net_ip, 'get_address_in_network') self.patch_object(ip, '_resolve_network_cidr') # define a fake_config() that returns predictable results and remembers # what it was called with. calls_list = [] _config = { 'vip': 'vip1 vip2', 'prefer-ipv6': False, 'os-public-network': None, 'os-public-hostname': None, 'os-internal-network': None, 'os-admin-network': None, } def fake_config(*args): calls_list.append(args) return _config[args[0]] self.config.side_effect = fake_config # first test, if not clustered, that the function uses unit_get # network_get_primary_address to get a real address. # for the default PUBLIC endpoint self.is_clustered.return_value = False self.network_get_primary_address.return_value = 'got-address' self._resolve_network_cidr.return_value = 'cidr' self.unit_get.return_value = 'unit-get-address' addr = ip.resolve_address() self.assertEqual(addr, 'got-address') self.assertEqual(calls_list, [('os-public-hostname',), ('vip',), ('os-public-network',), ('prefer-ipv6',)]) self.unit_get.assert_called_once_with('public-address') self.network_get_primary_address.assert_called_with( 'public' ) # second test: not clustered, prefer-ipv6 is True, ensure # that ipv6 address is fallback and network-get is still # used to determine the public endpoint binding _config['prefer-ipv6'] = True calls_list = [] self.get_ipv6_addr.return_value = ['ipv6-addr'] self.get_address_in_network.reset_mock() addr = ip.resolve_address() self.get_ipv6_addr.assert_called_once_with(exc_list=['vip1', 'vip2']) self.network_get_primary_address.assert_called_with( 'public' ) def _fake_addr_in_net(address, vip): return True if vip == 'vip2' else False self.is_address_in_network.side_effect = _fake_addr_in_net # Third test: clustered self.is_clustered.return_value = True calls_list = [] addr = ip.resolve_address() self.assertEqual(calls_list, [('os-public-hostname',), ('vip',), ('os-public-network',)]) self.network_get_primary_address.assert_called_with( 'public' ) # Fourth test: clustered, and config(...) returns not None _config['os-public-network'] = 'the-public-network' calls_list = [] _config['vip'] = 'vip1 vip2' addr = ip.resolve_address() self.assertEqual(calls_list, [ ('os-public-hostname',), ('vip',), ('os-public-network',), ]) self.assertEqual(addr, 'vip2') self.network_get_primary_address.assert_called_with( 'public' )
def test_resolve_address_network_binding(self): self.patch_object(ip.cluster, 'is_clustered') self.patch_object(ip.hookenv, 'config') self.patch_object(ip.hookenv, 'network_get_primary_address') self.patch_object(ip.net_ip, 'is_address_in_network') self.patch_object(ip.net_ip, 'get_ipv6_addr') self.patch_object(ip.hookenv, 'unit_get') self.patch_object(ip.net_ip, 'get_address_in_network') self.patch_object(ip, '_resolve_network_cidr') # define a fake_config() that returns predictable results and remembers # what it was called with. calls_list = [] _config = { 'vip': 'vip1 vip2', 'prefer-ipv6': False, 'os-public-network': None, 'os-public-hostname': None, 'os-internal-network': None, 'os-admin-network': None, } def fake_config(*args): calls_list.append(args) return _config[args[0]] self.config.side_effect = fake_config # first test, if not clustered, that the function uses unit_get # network_get_primary_address to get a real address. # for the default PUBLIC endpoint self.is_clustered.return_value = False self.network_get_primary_address.return_value = 'got-address' self._resolve_network_cidr.return_value = 'cidr' self.unit_get.return_value = 'unit-get-address' addr = ip.resolve_address() self.assertEqual(addr, 'got-address') self.assertEqual(calls_list, [('os-public-hostname', ), ('vip', ), ('os-public-network', ), ('prefer-ipv6', )]) self.unit_get.assert_called_once_with('public-address') self.network_get_primary_address.assert_called_with('public') # second test: not clustered, prefer-ipv6 is True, ensure # that ipv6 address is fallback and network-get is still # used to determine the public endpoint binding _config['prefer-ipv6'] = True calls_list = [] self.get_ipv6_addr.return_value = ['ipv6-addr'] self.get_address_in_network.reset_mock() addr = ip.resolve_address() self.get_ipv6_addr.assert_called_once_with(exc_list=['vip1', 'vip2']) self.network_get_primary_address.assert_called_with('public') def _fake_addr_in_net(address, vip): return True if vip == 'vip2' else False self.is_address_in_network.side_effect = _fake_addr_in_net # Third test: clustered self.is_clustered.return_value = True calls_list = [] addr = ip.resolve_address() self.assertEqual(calls_list, [('os-public-hostname', ), ('vip', ), ('os-public-network', )]) self.network_get_primary_address.assert_called_with('public') # Fourth test: clustered, and config(...) returns not None _config['os-public-network'] = 'the-public-network' calls_list = [] _config['vip'] = 'vip1 vip2' addr = ip.resolve_address() self.assertEqual(calls_list, [ ('os-public-hostname', ), ('vip', ), ('os-public-network', ), ]) self.assertEqual(addr, 'vip2') self.network_get_primary_address.assert_called_with('public')
def test_resolve_address(self): self.patch_object(ip.cluster, 'is_clustered') self.patch_object(ip.hookenv, 'config') self.patch_object(ip.hookenv, 'network_get_primary_address') self.patch_object(ip.net_ip, 'is_address_in_network') self.patch_object(ip.net_ip, 'get_ipv6_addr') self.patch_object(ip.hookenv, 'unit_get') self.patch_object(ip.net_ip, 'get_address_in_network') # define a fake_config() that returns predictable results and remembers # what it was called with. calls_list = [] _config = { 'vip': 'vip-address', 'prefer-ipv6': False, 'os-public-network': 'the-public-network', 'os-public-hostname': None, 'os-internal-network': 'the-internal-network', 'os-admin-network': 'the-admin-network', } def fake_config(*args): calls_list.append(args) return _config[args[0]] self.config.side_effect = fake_config # Juju pre 2.0 behaviour where network-get is not implemented self.network_get_primary_address.side_effect = NotImplementedError # first test, if not clustered, that the function uses unit_get() and # get_address_in_network to get a real address. # for the default PUBLIC endpoint self.is_clustered.return_value = False self.get_address_in_network.return_value = 'got-address' self.unit_get.return_value = 'unit-get-address' addr = ip.resolve_address() self.assertEqual(addr, 'got-address') self.assertEqual(calls_list, [('os-public-hostname',), ('vip',), ('os-public-network',), ('prefer-ipv6',)]) self.unit_get.assert_called_once_with('public-address') self.get_address_in_network.assert_called_once_with( 'the-public-network', 'unit-get-address') # second test: not clustered, prefer-ipv6 is True _config['prefer-ipv6'] = True calls_list = [] self.get_ipv6_addr.return_value = ['ipv6-addr'] self.get_address_in_network.reset_mock() addr = ip.resolve_address() self.get_ipv6_addr.assert_called_once_with(exc_list=['vip-address']) self.get_address_in_network.assert_called_once_with( 'the-public-network', 'ipv6-addr') # Third test: clustered, and config(...) returns None self.is_clustered.return_value = True _config['os-public-network'] = None calls_list = [] addr = ip.resolve_address() self.assertEqual(calls_list, [('os-public-hostname',), ('vip',), ('os-public-network',)]) # Fourth test: clustered, and config(...) returns not None _config['os-public-network'] = 'the-public-network' calls_list = [] _config['vip'] = 'vip1 vip2' def _fake_addr_in_net(address, vip): return True if vip == 'vip2' else False self.is_address_in_network.side_effect = _fake_addr_in_net addr = ip.resolve_address() self.assertEqual(calls_list, [ ('os-public-hostname',), ('vip',), ('os-public-network',), ]) self.assertEqual(addr, 'vip2') # Finally resolved_address returns None -> ValueError() # allow vip to not be found: self.is_address_in_network.return_value = False self.is_address_in_network.side_effect = None with self.assertRaises(ValueError): addr = ip.resolve_address()
def rndc_master_ip(self): """Returns IP address slave DNS slave should use to query master """ return os_ip.resolve_address(endpoint_type=os_ip.INTERNAL)