vitrage/vitrage/coordination/coordination.py

115 lines
3.8 KiB
Python

# Copyright 2019 - Nokia Corporation
#
# 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.
import json
import os
import psutil
import socket
import six
import tenacity
import tooz.coordination
from oslo_config import cfg
from oslo_log import log
from oslo_utils import timeutils
CONF = cfg.CONF
LOG = log.getLogger(__name__)
class Coordinator(object):
def __init__(self, my_id=None):
self.backend_url = CONF.coordination.backend_url
self.my_id = my_id or ' '.join(psutil.Process(os.getpid()).cmdline())
self.coordinator = None
if self.backend_url:
self.coordinator = tooz.coordination.get_coordinator(
self.backend_url, six.b('%s_%s' % (my_id, os.getpid())))
def start(self):
if self.backend_url:
try:
self.coordinator.start(start_heart=True)
LOG.info('Coordination backend started successfully.')
except tooz.coordination.ToozError:
LOG.exception('Error connecting to coordination backend.')
def stop(self):
if not self.is_active():
return
try:
self.coordinator.stop()
except tooz.coordination.ToozError:
LOG.exception('Error connecting to coordination backend.')
def is_active(self):
return self.coordinator and self.coordinator.is_started
@tenacity.retry(stop=tenacity.stop_after_attempt(5))
def join_group(self, group_id='vitrage'):
if not self.is_active() or not group_id:
return
try:
now = timeutils.utcnow(with_timezone=True).replace(microsecond=0)
isoformat = now.isoformat()
capabilities = json.dumps(
{
'name': self.my_id,
'hostname': socket.gethostname(),
'process': os.getpid(),
'created': isoformat
}
)
join_req = self.coordinator.join_group(six.b(group_id),
six.b(capabilities))
join_req.get()
LOG.info('Joined service group:%s, member:%s',
group_id, self.my_id)
return
except tooz.coordination.MemberAlreadyExist:
return
except tooz.coordination.GroupNotCreated as e:
create_grp_req = self.coordinator.create_group(six.b(group_id))
try:
create_grp_req.get()
except tooz.coordination.GroupAlreadyExist:
pass
# Re-raise exception to join group again.
raise e
def leave_group(self, group_id):
if self.is_active():
self.coordinator.leave_group(six.b(group_id))
LOG.info('Left group %s', group_id)
def get_services(self, group_id='vitrage'):
if not self.is_active():
return []
while True:
get_members_req = self.coordinator.get_members(six.b(group_id))
try:
return [json.loads(
self.coordinator.get_member_capabilities(
six.b(group_id), member).get().decode('us-ascii'))
for member in get_members_req.get()]
except tooz.coordination.GroupNotCreated:
self.join_group(group_id)