fenix/fenix/workflow/workflow.py

233 lines
8.1 KiB
Python

# Copyright (c) 2018 OpenStack Foundation.
# All Rights Reserved.
#
# 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 oslo_log import log as logging
from oslo_service import threadgroup
from threading import Thread
import time
LOG = logging.getLogger(__name__)
class Instance(object):
def __init__(self, project, instance_id, instance_name, host, ha=False):
self.project = project
self.instance_id = instance_id
self.instance_name = instance_name
self.host = host
self.ha = ha
def __str__(self):
return "%s: %s" % (self.instance_id, self.instance_name)
def is_on_host(self, host):
if self.host == host:
return True
else:
return False
class Project(object):
def __init__(self, name):
self.name = name
self.state = None
self.state_instances = []
class SessionData(object):
def __init__(self, data, session_id):
self.session_id = session_id
self.projects = []
self.hosts = data['hosts']
self.maintenance_at = str(data['maintenance_at'])
self.metadata = data['metadata']
self.instances = []
self.maintained_hosts = []
self.proj_instance_actions = {}
def get_empty_hosts(self):
empty_hosts = list(self.hosts)
([empty_hosts.remove(instance.host) for instance in
self.instances if instance.host in empty_hosts])
return empty_hosts
def add_instance(self, project, instance_id, instance_name, host,
ha=False):
if host not in self.hosts:
LOG.error('%s: instance %s in invalid host %s' %
(self.session_id, instance_id, host))
if project not in self.project_names():
self.projects.append(Project(project))
self.instances.append(Instance(project, instance_id, instance_name,
host, ha))
def project(self, name):
return ([project for project in self.projects if
project.name == name][0])
def project_names(self):
return [project.name for project in self.projects]
def set_projets_state(self, state):
for project in self.projects:
project.state = state
project.state_instances = []
def project_has_state_instances(self, name):
project = self.project(name)
if project.state_instances:
return True
else:
return False
def set_projects_state_and_host_instances(self, state, host):
some_project_has_instances = False
for project in self.projects:
project.state = state
project.state_instances = (
self.instance_ids_by_host_and_project(host, project.name))
if project.state_instances:
some_project_has_instances = True
project.state = state
else:
project.state = None
if not some_project_has_instances:
LOG.error('%s: No project has instances on host %s' %
(self.session_id, host))
def get_projects_with_state(self):
return ([project for project in self.projects if project.state
is not None])
def state_instance_ids(self, name):
instances = ([project.state_instances for project in self.projects if
project.name == name][0])
if not instances:
instances = self.instance_ids_by_project(name)
return instances
def instances_by_project(self, project):
return [instance for instance in self.instances if
instance.project == project]
def instance_ids_by_project(self, project):
return [instance.instance_id for instance in self.instances if
instance.project == project]
def instance_ids_by_host_and_project(self, host, project):
return [instance.instance_id for instance in self.instances
if instance.host == host and
instance.project == project]
def instances_by_host_and_project(self, host, project):
return [instance for instance in self.instances
if instance.host == host and
instance.project == project]
def instance_action_by_project_reply(self, project, instance_id):
return self.proj_instance_actions[project][instance_id]
def __str__(self):
info = 'Instance info:\n'
for host in self.hosts:
info += ('%s:\n' % host)
for project in self.project_names():
instances = self.instances_by_host_and_project(host, project)
if instances:
info += (' %s:\n' % project)
for instance in instances:
info += (' %s\n' % instance)
return info
class BaseWorkflow(Thread):
def __init__(self, conf, session_id, data):
Thread.__init__(self)
self.conf = conf
self.session_id = session_id
self.stopped = False
self.thg = threadgroup.ThreadGroup()
self.timer = {}
self.state = 'MAINTENANCE'
self.session_data = SessionData(data, session_id)
self.states_methods = {'MAINTENANCE': 'maintenance',
'SCALE_IN': 'scale_in',
'PREPARE_MAINTENANCE': 'prepare_maintenance',
'START_MAINTENANCE': 'start_maintenance',
'PLANNED_MAINTENANCE': 'planned_maintenance',
'MAINTENANCE_COMPLETE': 'maintenance_complete',
'MAINTENANCE_DONE': 'maintenance_done',
'FAILED': 'maintenance_failed'}
def _timer_expired(self, name):
LOG.info("%s: timer expired %s" % (self.session_id, name))
if name in self.timer.keys():
self.timer[name].stop()
self.thg.timer_done(self.timer[name])
self.timer.pop(name)
def is_timer_expired(self, name):
if name in self.timer.keys():
return False
else:
return True
def stop_timer(self, name):
LOG.info("%s: stop_timer %s" % (self.session_id, name))
if name in self.timer.keys():
self.timer[name].stop()
self.thg.timer_done(self.timer[name])
self.timer.pop(name)
def start_timer(self, delay, name):
LOG.info("%s: start_timer %s" % (self.session_id, name))
if name in self.timer.keys():
LOG.error("%s: timer exist!" % self.session_id)
else:
self.timer[name] = (self.thg.add_timer(delay,
self._timer_expired,
delay,
name))
def cleanup(self):
LOG.info("%s: cleanup" % self.session_id)
def stop(self):
LOG.info("%s: stop" % self.session_id)
self.stopped = True
def maintenance(self):
LOG.error("%s: maintenance method not implemented!" % self.session_id)
def maintenance_failed(self):
LOG.error("%s: maintenance_failed method not implemented!" %
self.session_id)
def run(self):
LOG.info("%s: started" % self.session_id)
while not self.stopped:
if self.state != "MAINTENANCE_DONE" and self.state != "FAILED":
statefunc = getattr(self, self.states_methods[self.state])
statefunc()
else:
time.sleep(1)
# IDLE while session removed
LOG.info("%s: done" % self.session_id)