223 lines
6.6 KiB
Python
223 lines
6.6 KiB
Python
#
|
|
# Copyright (c) 2014 Wind River Systems, Inc.
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
#
|
|
|
|
"""
|
|
PaceMaker
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import uuid
|
|
import logging
|
|
from lxml import etree
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
NODE_STATE_NOT_SET = ''
|
|
NODE_STATE_OFFLINE = 'offline'
|
|
NODE_STATE_ONLINE = 'online'
|
|
|
|
RESOURCE_STATE_NOT_SET = ''
|
|
RESOURCE_STATE_UNKNOWN = 'unknown'
|
|
RESOURCE_STATE_ENABLED = 'enabled'
|
|
RESOURCE_STATE_DISABLED = 'disabled'
|
|
RESOURCE_STATE_FAILED = 'failed'
|
|
|
|
|
|
class PaceMakerNode(object):
|
|
""" Pacemaker Node information about a node making up the cluster
|
|
"""
|
|
|
|
def __init__(self, node_name):
|
|
self.name = node_name
|
|
self.state = NODE_STATE_NOT_SET
|
|
|
|
|
|
class PaceMakerResource(object):
|
|
""" Pacemaker Resource information on a resource running on a node
|
|
in the cluster
|
|
"""
|
|
|
|
def __init__(self, node_name, resource_name):
|
|
self.name = resource_name
|
|
self.node_name = node_name
|
|
self.last_operation = ""
|
|
self.state = RESOURCE_STATE_NOT_SET
|
|
|
|
|
|
class Pacemaker(object):
|
|
""" Pacemaker
|
|
"""
|
|
|
|
def __init__(self):
|
|
self._xmldoc = None
|
|
|
|
def load(self):
|
|
""" Ask for the latest information on the cluster
|
|
"""
|
|
|
|
pacemaker_xml_filename = ('/tmp/pacemaker_%s.xml'
|
|
% str(uuid.uuid4()))
|
|
|
|
try:
|
|
if not os.path.exists('/usr/sbin/cibadmin'):
|
|
return
|
|
|
|
os.system("/usr/sbin/cibadmin --query > %s"
|
|
% pacemaker_xml_filename)
|
|
|
|
if not os.path.exists(pacemaker_xml_filename):
|
|
return
|
|
|
|
self._xmldoc = etree.parse(pacemaker_xml_filename)
|
|
if self._xmldoc is None:
|
|
os.remove(pacemaker_xml_filename)
|
|
return
|
|
|
|
if not etree.iselement(self._xmldoc.getroot()):
|
|
self._xmldoc = None
|
|
os.remove(pacemaker_xml_filename)
|
|
return
|
|
|
|
if len(self._xmldoc.getroot()) == 0:
|
|
self._xmldoc = None
|
|
os.remove(pacemaker_xml_filename)
|
|
return
|
|
|
|
os.remove(pacemaker_xml_filename)
|
|
|
|
except Exception:
|
|
if os.path.exists(pacemaker_xml_filename):
|
|
os.remove(pacemaker_xml_filename)
|
|
|
|
LOG.error("error:", sys.exc_info()[0])
|
|
|
|
def get_resource(self, node_name, resource_name):
|
|
""" Get a resource's information and state
|
|
"""
|
|
|
|
if self._xmldoc is None:
|
|
return None
|
|
|
|
xmlroot = self._xmldoc.getroot()
|
|
|
|
xmlresource = xmlroot.find(".//status/node_state[@id='%s']/"
|
|
"lrm[@id='%s']/lrm_resources/"
|
|
"lrm_resource[@id='%s']/lrm_rsc_op"
|
|
% (node_name, node_name, resource_name))
|
|
if not etree.iselement(xmlresource):
|
|
return None
|
|
|
|
resource = PaceMakerResource(node_name, resource_name)
|
|
|
|
resource.last_operation = xmlresource.attrib["operation"]
|
|
|
|
if (xmlresource.attrib["operation"] == "start" or
|
|
xmlresource.attrib["operation"] == "promote"):
|
|
if xmlresource.attrib["rc-code"] == "0":
|
|
resource.state = RESOURCE_STATE_ENABLED
|
|
else:
|
|
resource.state = RESOURCE_STATE_FAILED
|
|
|
|
elif (xmlresource.attrib["operation"] == "stop" or
|
|
xmlresource.attrib["operation"] == "demote"):
|
|
if xmlresource.attrib["rc-code"] == "0":
|
|
resource.state = RESOURCE_STATE_DISABLED
|
|
else:
|
|
resource.state = RESOURCE_STATE_FAILED
|
|
|
|
elif xmlresource.attrib["operation"] == "monitor":
|
|
if xmlresource.attrib["rc-code"] == "0":
|
|
resource.state = RESOURCE_STATE_ENABLED
|
|
elif xmlresource.attrib["rc-code"] == "7":
|
|
resource.state = RESOURCE_STATE_DISABLED
|
|
else:
|
|
resource.state = RESOURCE_STATE_FAILED
|
|
else:
|
|
resource.state = RESOURCE_STATE_UNKNOWN
|
|
|
|
return resource
|
|
|
|
def get_node(self, node_name):
|
|
""" Get a node's information and state
|
|
"""
|
|
|
|
if self._xmldoc is None:
|
|
return None
|
|
|
|
node = PaceMakerNode(node_name)
|
|
|
|
xmlroot = self._xmldoc.getroot()
|
|
|
|
# Check the static configuration for state.
|
|
xmlnode = xmlroot.find((".//nodes/node[@id='%s']"
|
|
"/instance_attributes[@id='nodes-%s']"
|
|
"/nvpair[@id='nodes-%s-standby']"
|
|
% (node_name, node_name, node_name)))
|
|
if etree.iselement(xmlnode):
|
|
if xmlnode.attrib["name"] == "standby":
|
|
if xmlnode.attrib["value"] == "on":
|
|
node.state = NODE_STATE_OFFLINE
|
|
return node
|
|
|
|
# Now check the running status for state.
|
|
xmlnode = xmlroot.find(".//status/node_state[@id='%s']"
|
|
% node_name)
|
|
if not etree.iselement(xmlnode):
|
|
return None
|
|
|
|
if xmlnode.attrib["in_ccm"] == "true":
|
|
if xmlnode.attrib["crmd"] == "online":
|
|
node.state = NODE_STATE_ONLINE
|
|
else:
|
|
node.state = NODE_STATE_OFFLINE
|
|
else:
|
|
node.state = NODE_STATE_OFFLINE
|
|
|
|
return node
|
|
|
|
def set_node_state(self, node_name, node_state):
|
|
""" Set the state of a node in the cluster
|
|
"""
|
|
|
|
try:
|
|
if not os.path.exists('/usr/sbin/crm'):
|
|
return False
|
|
|
|
if node_state == NODE_STATE_OFFLINE:
|
|
action = "standby"
|
|
elif node_state == NODE_STATE_ONLINE:
|
|
action = "online"
|
|
else:
|
|
LOG.warning("Unsupported state (%s) requested for %s."
|
|
% (node_state, node_name))
|
|
return False
|
|
|
|
os.system("/usr/sbin/crm node %s %s" % (action, node_name))
|
|
return True
|
|
|
|
except Exception:
|
|
LOG.error("error:", sys.exc_info()[0])
|
|
return False
|
|
|
|
def migrate_resource_to_node(self, resource_name, node_name, lifetime):
|
|
""" Migrate resource to a node in the cluster.
|
|
"""
|
|
|
|
try:
|
|
if not os.path.exists('/usr/sbin/crm'):
|
|
return False
|
|
|
|
# Lifetime follows the duration format specified in ISO_8601
|
|
os.system("/usr/sbin/crm resource migrate %s %s P%sS"
|
|
% (resource_name, node_name, lifetime))
|
|
return True
|
|
|
|
except Exception:
|
|
os.system("/usr/sbin/crm resource unmigrate %s" % resource_name)
|
|
LOG.error("error:", sys.exc_info()[0])
|
|
return False
|