def get_db_host(client_hostname): """Get address of local database host. 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 vip(s) are configured, chooses first available. """ vips = config('vip').split() if config('vip') else [] access_network = config('access-network') if access_network: client_ip = get_host_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) if is_clustered() and vips: return vips[0] # NOTE on private network if config('prefer-ipv6'): return get_ipv6_addr(exc_list=vips)[0] return unit_get('private-address')
def get_db_host(client_hostname, interface='shared-db'): """Get address of local database host. 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 vip(s) are configured, chooses first available. """ vips = config('vip').split() if config('vip') else [] dns_ha = config('dns-ha') access_network = config('access-network') client_ip = get_host_ip(client_hostname) if is_clustered() and dns_ha: log("Using DNS HA hostname: {}".format(config('os-access-hostname'))) return config('os-access-hostname') elif access_network: 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] return unit_get('private-address')
def get_db_host(client_hostname, interface='shared-db'): """Get address of local database host. 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 vip(s) are configured, chooses first available. """ vips = config('vip').split() if config('vip') else [] access_network = config('access-network') client_ip = get_host_ip(client_hostname) if access_network: 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] return unit_get('private-address')
def test_is_address_in_network(self): self.assertTrue( net_ip.is_address_in_network('192.168.1.0/24', '192.168.1.1')) self.assertFalse( net_ip.is_address_in_network('192.168.1.0/24', '10.5.1.1')) self.assertRaises(ValueError, net_ip.is_address_in_network, 'broken', '192.168.1.1') self.assertRaises(ValueError, net_ip.is_address_in_network, '192.168.1.0/24', 'hostname') self.assertTrue( net_ip.is_address_in_network('2a01:348:2f4::/64', '2a01:348:2f4:0:685e:5748:ae62:209f')) self.assertFalse( net_ip.is_address_in_network( '2a01:348:2f4::/64', 'fdfc:3bd5:210b:cc8d:8c80:9e10:3f07:371'))
def resolve_address(endpoint_type=PUBLIC): resolved_address = None if is_clustered(): if config(_address_map[endpoint_type]['config']) is None: # Assume vip is simple and pass back directly resolved_address = config('vip') else: for vip in config('vip').split(): if is_address_in_network( config(_address_map[endpoint_type]['config']), vip): resolved_address = vip else: if config('prefer-ipv6'): fallback_addr = get_ipv6_addr() else: fallback_addr = unit_get(_address_map[endpoint_type]['fallback']) resolved_address = get_address_in_network( config(_address_map[endpoint_type]['config']), fallback_addr) if resolved_address is None: raise ValueError('Unable to resolve a suitable IP address' ' based on charm state and configuration') else: return resolved_address
def is_filtered(address, networks): found = False for net in networks: if is_address_in_network(net, address): found = True break return found
def get_vip_in_network(network): matching_vip = None vips = config('vip') if vips: for vip in vips.split(): if is_address_in_network(network, vip): matching_vip = vip return matching_vip
def access_ip(self): vips = config().get('vip') if vips: vips = vips.split() clustered = is_clustered() net_addr = ch_net_ip.get_relation_ip('tenant-storage') bound_cidr = ch_net_ip.resolve_network_cidr( ch_net_ip.network_get_primary_address('tenant-storage')) if clustered and vips: for vip in vips: if ch_net_ip.is_address_in_network(bound_cidr, vip): return vip return net_addr
def __call__(self): if isinstance(self.external_ports, basestring): 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': [] } for cn in self.canonical_names(): self.configure_cert(cn) addresses = [] vips = [] if config('vip'): vips = config('vip').split() for network_type in ['os-internal-network', 'os-admin-network', 'os-public-network']: address = get_address_in_network(config(network_type), unit_get('private-address')) if len(vips) > 0 and is_clustered(): for vip in vips: if is_address_in_network(config(network_type), vip): addresses.append((address, vip)) break elif is_clustered(): addresses.append((address, config('vip'))) else: addresses.append((address, address)) for address, endpoint in set(addresses): for api_port in self.external_ports: ext_port = determine_apache_port(api_port) int_port = determine_api_port(api_port) portmap = (address, endpoint, int(ext_port), int(int_port)) ctxt['endpoints'].append(portmap) ctxt['ext_ports'].append(int(ext_port)) ctxt['ext_ports'] = list(set(ctxt['ext_ports'])) return ctxt
def resolve_address(endpoint_type=PUBLIC): """Return unit address depending on net config. If unit is clustered with vip(s) and has net splits defined, return vip on correct network. If clustered with no nets defined, return primary vip. If not clustered, return unit address ensuring address is on configured net split if one is configured. :param endpoint_type: Network endpoing type """ resolved_address = _get_address_override(endpoint_type) if resolved_address: return resolved_address vips = config('vip') if vips: vips = vips.split() net_type = ADDRESS_MAP[endpoint_type]['config'] net_addr = config(net_type) net_fallback = ADDRESS_MAP[endpoint_type]['fallback'] clustered = is_clustered() if clustered: if not net_addr: # If no net-splits defined, we expect a single vip resolved_address = vips[0] else: for vip in vips: if is_address_in_network(net_addr, vip): resolved_address = vip break else: if config('prefer-ipv6'): fallback_addr = get_ipv6_addr(exc_list=vips)[0] else: fallback_addr = unit_get(net_fallback) resolved_address = get_address_in_network(net_addr, fallback_addr) if resolved_address is None: raise ValueError("Unable to resolve a suitable IP address based on " "charm state and configuration. (net_type=%s, " "clustered=%s)" % (net_type, clustered)) return resolved_address
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 = [] if config('vip'): vips = config('vip').split() else: vips = [] for net_type in [ 'os-internal-network', 'os-admin-network', 'os-public-network' ]: addr = get_address_in_network(config(net_type), unit_get('private-address')) if len(vips) > 1 and is_clustered(): if not config(net_type): log("Multiple networks configured but net_type " "is None (%s)." % net_type, level=WARNING) continue for vip in vips: if is_address_in_network(config(net_type), vip): addresses.append((addr, vip)) break elif is_clustered() and config('vip'): addresses.append((addr, config('vip'))) else: addresses.append((addr, addr)) return sorted(addresses)
def get_vip(binding=None): vip = hookenv.config('vip') if not vip: return None vips = vip.split() if len(vips) == 1: return vips[0] if not binding: binding = 'access' bound_cidr = resolve_network_cidr(binding_address(binding)) for vip in vips: if is_address_in_network(bound_cidr, vip): return vip return None
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 = [] if config('vip'): vips = config('vip').split() else: vips = [] for net_type in ['os-internal-network', 'os-admin-network', 'os-public-network']: addr = get_address_in_network(config(net_type), unit_get('private-address')) if len(vips) > 1 and is_clustered(): if not config(net_type): log("Multiple networks configured but net_type " "is None (%s)." % net_type, level=WARNING) continue for vip in vips: if is_address_in_network(config(net_type), vip): addresses.append((addr, vip)) break elif is_clustered() and config('vip'): addresses.append((addr, config('vip'))) else: addresses.append((addr, addr)) return sorted(addresses)
def publish_url(self, vault_url, remote_binding=None): """ Publish URL for Vault to all Relations :param vault_url: api url used by remote client to speak to vault. :param remote_binding: if provided, remote units not using this binding will be ignored. """ for relation in self.relations: if remote_binding: units = relation.units if units: addr = units[0].received['ingress-address'] or \ units[0].received['access_address'] bound_cidr = resolve_network_cidr( network_get_primary_address(remote_binding)) if not (addr and is_address_in_network(bound_cidr, addr)): continue relation.to_publish['vault_url'] = vault_url
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 resolve_address(endpoint_type=PUBLIC, override=True): """Return unit address depending on net config. If unit is clustered with vip(s) and has net splits defined, return vip on correct network. If clustered with no nets defined, return primary vip. If not clustered, return unit address ensuring address is on configured net split if one is configured, or a Juju 2.0 extra-binding has been used. :param endpoint_type: Network endpoing type :param override: Accept hostname overrides or not """ resolved_address = None if override: resolved_address = _get_address_override(endpoint_type) if resolved_address: return resolved_address vips = config('vip') if vips: vips = vips.split() net_type = ADDRESS_MAP[endpoint_type]['config'] net_addr = config(net_type) net_fallback = ADDRESS_MAP[endpoint_type]['fallback'] binding = ADDRESS_MAP[endpoint_type]['binding'] clustered = is_clustered() if clustered and vips: if net_addr: for vip in vips: if is_address_in_network(net_addr, vip): resolved_address = vip break else: # NOTE: endeavour to check vips against network space # bindings try: bound_cidr = resolve_network_cidr( network_get_primary_address(binding) ) for vip in vips: if is_address_in_network(bound_cidr, vip): resolved_address = vip break except NotImplementedError: # If no net-splits configured and no support for extra # bindings/network spaces so we expect a single vip resolved_address = vips[0] else: if config('prefer-ipv6'): fallback_addr = get_ipv6_addr(exc_list=vips)[0] else: fallback_addr = unit_get(net_fallback) if net_addr: resolved_address = get_address_in_network(net_addr, fallback_addr) else: # NOTE: only try to use extra bindings if legacy network # configuration is not in use try: resolved_address = network_get_primary_address(binding) except NotImplementedError: resolved_address = fallback_addr if resolved_address is None: raise ValueError("Unable to resolve a suitable IP address based on " "charm state and configuration. (net_type=%s, " "clustered=%s)" % (net_type, clustered)) return resolved_address
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_elected_leader(DC_RESOURCE_NAME): # 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 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 = get_host_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 = get_host_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)