def test_resolve_hostname_to_ip_ips(self): ipv6_address = '2a01:348:2f4:0:dba7:dc58:659b:941f' ipv4_address = '10.10.10.2' self.assertEqual(percona_utils.resolve_hostname_to_ip(ipv6_address), ipv6_address) self.assertEqual(percona_utils.resolve_hostname_to_ip(ipv4_address), ipv4_address)
def test_resolve_hostname_to_ip_ips(self, mock_is_ip, mock_is_ipv6): ipv6_address = '2a01:348:2f4:0:dba7:dc58:659b:941f' ipv4_address = '10.10.10.2' self.assertEqual(percona_utils.resolve_hostname_to_ip(ipv6_address), ipv6_address) self.assertTrue(mock_is_ip.called) self.assertFalse(mock_is_ipv6.called) self.assertEqual(percona_utils.resolve_hostname_to_ip(ipv4_address), ipv4_address) self.assertTrue(mock_is_ip.called) self.assertFalse(mock_is_ipv6.called)
def test_resolve_hostname_to_ip_hostname_noanswer(self, dns_query): dns_query.return_value = [] self.assertEqual(percona_utils.resolve_hostname_to_ip('myhostname'), None) dns_query.assert_has_calls([ mock.call('myhostname', 'A'), ])
def test_resolve_hostname_to_ip_hostname_a(self, dns_query): mock_answer = mock.MagicMock() mock_answer.address = '10.10.10.20' dns_query.return_value = [mock_answer] self.assertEqual(percona_utils.resolve_hostname_to_ip('myhostname'), '10.10.10.20') dns_query.assert_has_calls([ mock.call('myhostname', 'A'), ])
def test_resolve_hostname_to_ip_hostname_aaaa(self, dns_query): mock_answer = mock.MagicMock() mock_answer.address = '2a01:348:2f4:0:dba7:dc58:659b:941f' dns_query.return_value = [mock_answer] self.assertEqual(percona_utils.resolve_hostname_to_ip('myhostname', ipv6=True), '2a01:348:2f4:0:dba7:dc58:659b:941f') dns_query.assert_has_calls([ mock.call('myhostname', 'AAAA'), ])
def test_resolve_hostname_to_ip_hostname_aaaa(self, dns_query, mock_config, mock_is_ip, mock_is_ipv6): def fake_config(key): return {'prefer-ipv6': True}.get(key) mock_config.side_effect = fake_config mock_answer = mock.MagicMock() mock_is_ipv6.return_value = False mock_answer.address = '2a01:348:2f4:0:dba7:dc58:659b:941f' dns_query.return_value = [mock_answer] self.assertEqual(percona_utils.resolve_hostname_to_ip('myhostname'), '2a01:348:2f4:0:dba7:dc58:659b:941f') self.assertFalse(mock_is_ip.called) self.assertTrue(mock_is_ipv6.called) dns_query.assert_has_calls([ mock.call('myhostname', 'AAAA'), ])
def shared_db_changed(relation_id=None, unit=None): if not seeded(): log( "Percona cluster not yet bootstrapped - deferring shared-db rel " "until bootstrapped", DEBUG) return if not is_leader() and client_node_is_ready(): clear_and_populate_client_db_relations(relation_id, 'shared-db') return # Bail if leader is not ready if not leader_node_is_ready(): return settings = relation_get(unit=unit, rid=relation_id) access_network = config('access-network') db_helper = get_db_helper() peer_store_and_set(relation_id=relation_id, relation_settings={'access-network': access_network}) singleset = set(['database', 'username', 'hostname']) if singleset.issubset(settings): # Process a single database configuration hostname = settings['hostname'] database = settings['database'] username = settings['username'] normalized_address = resolve_hostname_to_ip(hostname) if access_network and not is_address_in_network( access_network, normalized_address): # NOTE: for configurations using access-network, only setup # database access if remote unit has presented a # hostname or ip address thats within the configured # network cidr log("Host '%s' not in access-network '%s' - ignoring" % (normalized_address, access_network), level=INFO) return # NOTE: do this before querying access grants password = configure_db_for_hosts(hostname, database, username, db_helper) allowed_units = db_helper.get_allowed_units(database, username, relation_id=relation_id) allowed_units = unit_sorted(allowed_units) allowed_units = ' '.join(allowed_units) relation_set(relation_id=relation_id, allowed_units=allowed_units) db_host = get_db_host(hostname) peer_store_and_set(relation_id=relation_id, db_host=db_host, password=password, allowed_units=allowed_units) else: # Process multiple database setup requests. # from incoming relation data: # nova_database=xxx nova_username=xxx nova_hostname=xxx # quantum_database=xxx quantum_username=xxx quantum_hostname=xxx # create # { # "nova": { # "username": xxx, # "database": xxx, # "hostname": xxx # }, # "quantum": { # "username": xxx, # "database": xxx, # "hostname": xxx # } # } # databases = {} for k, v in settings.iteritems(): db = k.split('_')[0] x = '_'.join(k.split('_')[1:]) if db not in databases: databases[db] = {} databases[db][x] = v allowed_units = {} return_data = {} for db in databases: if singleset.issubset(databases[db]): database = databases[db]['database'] hostname = databases[db]['hostname'] username = databases[db]['username'] normalized_address = resolve_hostname_to_ip(hostname) if (access_network and not is_address_in_network( access_network, normalized_address)): # NOTE: for configurations using access-network, # only setup database access if remote unit # has presented a hostname or ip address # thats within the configured network cidr return # NOTE: do this before querying access grants password = configure_db_for_hosts(hostname, database, username, db_helper) a_units = db_helper.get_allowed_units(database, username, relation_id=relation_id) a_units = ' '.join(unit_sorted(a_units)) allowed_units_key = '%s_allowed_units' % (db) allowed_units[allowed_units_key] = a_units return_data['%s_password' % (db)] = password return_data[allowed_units_key] = a_units db_host = get_db_host(hostname) if allowed_units: relation_set(relation_id=relation_id, **allowed_units) else: log("No allowed_units - not setting relation settings", level=DEBUG) if return_data: peer_store_and_set(relation_id=relation_id, db_host=db_host, **return_data) else: log("No return data - not setting relation settings", level=DEBUG)
def get_db_host(client_hostname, interface='shared-db'): """Get address of local database host for use by db clients If an access-network has been configured, expect selected address to be on that network. If none can be found, revert to primary address. If network spaces are supported (Juju >= 2.0), use network-get to retrieve the network binding for the interface. If DNSHA is set pass os-access-hostname If vip(s) are configured, chooses first available. @param client_hostname: hostname of client side relation setting hostname. Only used if access-network is configured @param interface: Network space binding to check. Usually the relationship name. @returns IP for use with db clients """ vips = config('vip').split() if config('vip') else [] dns_ha = config('dns-ha') access_network = config('access-network') if is_clustered() and dns_ha: log("Using DNS HA hostname: {}".format(config('os-access-hostname'))) return config('os-access-hostname') elif access_network: client_ip = resolve_hostname_to_ip(client_hostname) if is_address_in_network(access_network, client_ip): if is_clustered(): for vip in vips: if is_address_in_network(access_network, vip): return vip log("Unable to identify a VIP in the access-network '%s'" % (access_network), level=WARNING) else: return get_address_in_network(access_network) else: log("Client address '%s' not in access-network '%s'" % (client_ip, access_network), level=WARNING) else: try: # NOTE(jamespage) # Try to use network spaces to resolve binding for # interface, and to resolve the VIP associated with # the binding if provided. interface_binding = network_get_primary_address(interface) if is_clustered() and vips: interface_cidr = resolve_network_cidr(interface_binding) for vip in vips: if is_address_in_network(interface_cidr, vip): return vip return interface_binding except NotImplementedError: # NOTE(jamespage): skip - fallback to previous behaviour pass if is_clustered() and vips: return vips[0] # NOTE on private network if config('prefer-ipv6'): return get_ipv6_addr(exc_list=vips)[0] # Last resort return unit_get('private-address')
def shared_db_changed(relation_id=None, unit=None): if not seeded(): log( "Percona cluster not yet bootstrapped - deferring shared-db rel " "until bootstrapped", DEBUG) return if not is_leader() and client_node_is_ready(): # NOTE(jamespage): relation level data candidate log('Service is peered, clearing shared-db relation ' 'as this service unit is not the leader') relation_clear(relation_id) # Each unit needs to set the db information otherwise if the unit # with the info dies the settings die with it Bug# 1355848 if is_relation_made('cluster'): for rel_id in relation_ids('shared-db'): peerdb_settings = \ peer_retrieve_by_prefix(rel_id, exc_list=['hostname']) passwords = [ key for key in peerdb_settings.keys() if 'password' in key.lower() ] if len(passwords) > 0: relation_set(relation_id=rel_id, **peerdb_settings) return # Bail if leader is not ready if not leader_node_is_ready(): return settings = relation_get(unit=unit, rid=relation_id) access_network = config('access-network') db_helper = get_db_helper() peer_store_and_set(relation_id=relation_id, relation_settings={'access-network': access_network}) singleset = set(['database', 'username', 'hostname']) if singleset.issubset(settings): # Process a single database configuration hostname = settings['hostname'] database = settings['database'] username = settings['username'] normalized_address = resolve_hostname_to_ip(hostname) if access_network and not is_address_in_network( access_network, normalized_address): # NOTE: for configurations using access-network, only setup # database access if remote unit has presented a # hostname or ip address thats within the configured # network cidr log("Host '%s' not in access-network '%s' - ignoring" % (normalized_address, access_network), level=INFO) return # NOTE: do this before querying access grants password = configure_db_for_hosts(hostname, database, username, db_helper) allowed_units = db_helper.get_allowed_units(database, username, relation_id=relation_id) allowed_units = unit_sorted(allowed_units) allowed_units = ' '.join(allowed_units) relation_set(relation_id=relation_id, allowed_units=allowed_units) db_host = get_db_host(hostname) peer_store_and_set(relation_id=relation_id, db_host=db_host, password=password) else: # Process multiple database setup requests. # from incoming relation data: # nova_database=xxx nova_username=xxx nova_hostname=xxx # quantum_database=xxx quantum_username=xxx quantum_hostname=xxx # create # { # "nova": { # "username": xxx, # "database": xxx, # "hostname": xxx # }, # "quantum": { # "username": xxx, # "database": xxx, # "hostname": xxx # } # } # databases = {} for k, v in settings.iteritems(): db = k.split('_')[0] x = '_'.join(k.split('_')[1:]) if db not in databases: databases[db] = {} databases[db][x] = v allowed_units = {} return_data = {} for db in databases: if singleset.issubset(databases[db]): database = databases[db]['database'] hostname = databases[db]['hostname'] username = databases[db]['username'] normalized_address = resolve_hostname_to_ip(hostname) if (access_network and not is_address_in_network( access_network, normalized_address)): # NOTE: for configurations using access-network, # only setup database access if remote unit # has presented a hostname or ip address # thats within the configured network cidr return # NOTE: do this before querying access grants password = configure_db_for_hosts(hostname, database, username, db_helper) a_units = db_helper.get_allowed_units(database, username, relation_id=relation_id) a_units = ' '.join(unit_sorted(a_units)) allowed_units['%s_allowed_units' % (db)] = a_units return_data['%s_password' % (db)] = password db_host = get_db_host(hostname) if allowed_units: relation_set(relation_id=relation_id, **allowed_units) else: log("No allowed_units - not setting relation settings", level=DEBUG) if return_data: peer_store_and_set(relation_id=relation_id, db_host=db_host, **return_data) else: log("No return data - not setting relation settings", level=DEBUG)
def shared_db_changed(relation_id=None, unit=None): if not seeded(): log("Percona cluster not yet bootstrapped - deferring shared-db rel " "until bootstrapped", DEBUG) return if not is_leader() and client_node_is_ready(): clear_and_populate_client_db_relations(relation_id, 'shared-db') return # Bail if leader is not ready if not leader_node_is_ready(): return settings = relation_get(unit=unit, rid=relation_id) access_network = config('access-network') db_helper = get_db_helper() peer_store_and_set(relation_id=relation_id, relation_settings={'access-network': access_network}) singleset = set(['database', 'username', 'hostname']) if singleset.issubset(settings): # Process a single database configuration hostname = settings['hostname'] database = settings['database'] username = settings['username'] normalized_address = resolve_hostname_to_ip(hostname) if access_network and not is_address_in_network(access_network, normalized_address): # NOTE: for configurations using access-network, only setup # database access if remote unit has presented a # hostname or ip address thats within the configured # network cidr log("Host '%s' not in access-network '%s' - ignoring" % (normalized_address, access_network), level=INFO) return # NOTE: do this before querying access grants password = configure_db_for_hosts(hostname, database, username, db_helper) allowed_units = db_helper.get_allowed_units(database, username, relation_id=relation_id) allowed_units = unit_sorted(allowed_units) allowed_units = ' '.join(allowed_units) relation_set(relation_id=relation_id, allowed_units=allowed_units) db_host = get_db_host(hostname) peer_store_and_set(relation_id=relation_id, db_host=db_host, password=password, allowed_units=allowed_units) else: # Process multiple database setup requests. # from incoming relation data: # nova_database=xxx nova_username=xxx nova_hostname=xxx # quantum_database=xxx quantum_username=xxx quantum_hostname=xxx # create # { # "nova": { # "username": xxx, # "database": xxx, # "hostname": xxx # }, # "quantum": { # "username": xxx, # "database": xxx, # "hostname": xxx # } # } # databases = {} for k, v in settings.iteritems(): db = k.split('_')[0] x = '_'.join(k.split('_')[1:]) if db not in databases: databases[db] = {} databases[db][x] = v allowed_units = {} return_data = {} for db in databases: if singleset.issubset(databases[db]): database = databases[db]['database'] hostname = databases[db]['hostname'] username = databases[db]['username'] normalized_address = resolve_hostname_to_ip(hostname) if (access_network and not is_address_in_network(access_network, normalized_address)): # NOTE: for configurations using access-network, # only setup database access if remote unit # has presented a hostname or ip address # thats within the configured network cidr return # NOTE: do this before querying access grants password = configure_db_for_hosts(hostname, database, username, db_helper) a_units = db_helper.get_allowed_units(database, username, relation_id=relation_id) a_units = ' '.join(unit_sorted(a_units)) allowed_units_key = '%s_allowed_units' % (db) allowed_units[allowed_units_key] = a_units return_data['%s_password' % (db)] = password return_data[allowed_units_key] = a_units db_host = get_db_host(hostname) if allowed_units: relation_set(relation_id=relation_id, **allowed_units) else: log("No allowed_units - not setting relation settings", level=DEBUG) if return_data: peer_store_and_set(relation_id=relation_id, db_host=db_host, **return_data) else: log("No return data - not setting relation settings", level=DEBUG)