charm-interface-openstack-ha/peers.py
James Page 75286e5117 Make peer discover more defensive
Before adding a (unit-name, address) tuple to the list of peers
for a particular address type, ensure that the unit has actually
presented data for the requested address type.

This avoids 'None' values being written to the haproxy configuration
files during initial deployment and scale back of units.

Change-Id: Ia10e1454791d92b9ca6233425b4a6ea89642def0
Closes-Bug: 1849901
2020-07-20 08:24:13 +01:00

114 lines
3.7 KiB
Python

#!/usr/bin/python
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from charms.reactive import RelationBase
from charms.reactive import hook
from charms.reactive import scopes
class OpenstackHAPeers(RelationBase):
scope = scopes.UNIT
@hook('{peers:openstack-ha}-relation-joined')
def joined(self):
conv = self.conversation()
conv.set_state('{relation_name}.connected')
@hook('{peers:openstack-ha}-relation-changed')
def changed(self):
conv = self.conversation()
conv.set_state('{relation_name}.connected')
if self.data_complete(conv):
conv.set_state('{relation_name}.available')
def ip_map(self, address_key='private-address'):
nodes = []
for conv in self.conversations():
host_name = conv.scope.replace('/', '-')
address = conv.get_remote(address_key)
if address:
nodes.append((host_name, address))
return nodes
@hook('{peers:openstack-ha}-relation-{broken,departed}')
def departed_or_broken(self):
conv = self.conversation()
conv.remove_state('{relation_name}.connected')
if not self.data_complete(conv):
conv.remove_state('{relation_name}.available')
def data_complete(self, conv):
"""
Get the connection string, if available, or None.
"""
data = {
'private_address': conv.get_remote('private-address'),
}
if all(data.values()):
return True
return False
def set_address(self, address_type, address):
'''Advertise the address of this unit of a particular type
:param address_type: str Type of address being advertised, e.g.
internal/public/admin etc
:param address: str IP of this unit in 'address_type' network
@returns None'''
for conv in self.conversations():
conv.set_remote(
key='{}-address'.format(address_type),
value=address)
def send_all(self, settings, store_local=False):
'''Advertise a setting to peer units
:param settings: dict Settings to be advertised to peers
:param store_local: boolean Whether to store setting in local db
@returns None'''
for conv in self.conversations():
conv.set_remote(data=settings)
if store_local:
conv.set_local(data=settings)
def retrieve_local(self, key):
'''Inspect conversation and look for key in local db
:param key: str Key to look for in localdb
@returns list: List of values of key
'''
values = []
for conv in self.conversations():
value = conv.get_local(key)
if value:
values.append(value)
return values
def retrieve_remote(self, key):
'''Inspect conversation and look for key being advertised by peer
:param key: str Key to look for from peer
@returns list: List of values of key
'''
values = []
for conv in self.conversations():
value = conv.get_remote(key)
if value:
values.append(value)
return values