diff --git a/crmsh/migration.py b/crmsh/migration.py index badbc7974..d0d90d980 100644 --- a/crmsh/migration.py +++ b/crmsh/migration.py @@ -2,6 +2,7 @@ import json import logging import re +import subprocess import sys import typing @@ -10,6 +11,7 @@ from crmsh import service_manager from crmsh import sh from crmsh import utils +from crmsh import xmlutil from crmsh.prun import prun logger = logging.getLogger(__name__) @@ -103,7 +105,9 @@ def write_in_color(f, color: str, text: str): f.write(text) def end(self): - if not self.has_problems: + if self.has_problems: + self.write_in_color(sys.stdout, constants.RED, '[FAIL]\n\n') + else: self.write_in_color(sys.stdout, constants.GREEN, '[PASS]\n\n') @@ -112,19 +116,24 @@ def check(args: typing.Sequence[str]) -> int: parser.add_argument('--json', nargs='?', const='pretty', choices=['oneline', 'pretty']) parser.add_argument('--local', action='store_true') parsed_args = parser.parse_args(args) + + if 'oneline' == parsed_args.json: + handler = CheckResultJsonHandler() + elif 'pretty' == parsed_args.json: + handler = CheckResultJsonHandler(indent=2) + else: + handler = CheckResultInteractiveHandler() + ret = 0 if not parsed_args.local and not parsed_args.json: remote_ret = check_remote() print('------ localhost ------') + check_local(handler) + check_global(handler) else: remote_ret = 0 - if 'oneline' == parsed_args.json: - handler = CheckResultJsonHandler() - elif 'pretty' == parsed_args.json: - handler = CheckResultJsonHandler(indent=2) - else: - handler = CheckResultInteractiveHandler() - check_local(handler) + check_local(handler) + handler.end() if isinstance(handler, CheckResultJsonHandler): ret = 0 if handler.json_result["pass"] else 1 elif isinstance(handler, CheckResultInteractiveHandler): @@ -139,7 +148,6 @@ def check_local(handler: CheckResultHandler): check_dependency_version(handler) check_service_status(handler) check_unsupported_corosync_features(handler) - handler.end() def check_remote(): @@ -206,6 +214,10 @@ def check_remote(): return ret +def check_global(handler: CheckResultHandler): + check_unsupported_resource_agents(handler) + + def check_dependency_version(handler: CheckResultHandler): handler.log_info('Checking dependency version...') shell = sh.LocalShell() @@ -245,3 +257,29 @@ def check_unsupported_corosync_features(handler: CheckResultHandler): handler.handle_tip(f'Corosync RRP will be deprecated in corosync 3.', [ 'After migrating to SLES 16, run "crm health sles16 --fix" to migrate it to knet multilink.', ]) + + +def check_unsupported_resource_agents(handler: CheckResultHandler): + handler.log_info("Checking used resource agents...") + crm_mon = xmlutil.CrmMonXmlParser() + resource_agents = crm_mon.get_configured_resource_agents() + _check_saphana_resource_agent(handler, resource_agents) + + +def _check_saphana_resource_agent(handler: CheckResultHandler, resource_agents: typing.Set[str]): + # "SAPHana" appears only in SAPHanaSR Classic + has_sap_hana_sr_resources = any(agent in resource_agents for agent in [ + 'ocf::suse:SAPHana', + 'ocf::suse:SAPHanaController', + 'ocf::suse:SAPHanaTopology', + ]) + if has_sap_hana_sr_resources: + if 0 != subprocess.run( + ['rpm', '-q', 'SAPHanaSR-angi'], + stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ).returncode: + handler.handle_problem(False, 'SAPHanaSR Classic will be removed in SLES 16.', [ + 'Before migrating to SLES 16, replace it with SAPHanaSR-angi.', + ]) diff --git a/crmsh/xmlutil.py b/crmsh/xmlutil.py index e9d45d7f3..01cbda141 100644 --- a/crmsh/xmlutil.py +++ b/crmsh/xmlutil.py @@ -4,6 +4,8 @@ import os import subprocess +import typing + from lxml import etree, doctestcompare import copy import bz2 @@ -1576,4 +1578,8 @@ def get_resource_id_list_via_type(self, ra_type): """ xpath = f'//resource[@resource_agent="{ra_type}"]' return [elem.get('id') for elem in self.xml_elem.xpath(xpath)] + + def get_configured_resource_agents(self) -> typing.Set[str]: + xpath = '/*/resources/resource/@resource_agent' + return set(self.xml_elem.xpath(xpath)) # vim:ts=4:sw=4:et: