Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Leap:15.4:Update
crmsh.15169
0001-High-bootstrap-using-class-SBDManager-for-...
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 0001-High-bootstrap-using-class-SBDManager-for-sbd-config.patch of Package crmsh.15169
From 57615d576dc54045b03c000fed6c66a6275eb770 Mon Sep 17 00:00:00 2001 From: liangxin1300 <XLiang@suse.com> Date: Fri, 8 May 2020 15:14:10 +0800 Subject: [PATCH] High: bootstrap: using class SBDManager for sbd configuration and management(bsc#1170037, bsc#1170999) --- crmsh/bootstrap.py | 402 +++++++++++++++++++++++++------------------- crmsh/ui_cluster.py | 6 +- 2 files changed, 236 insertions(+), 172 deletions(-) diff --git a/crmsh/bootstrap.py b/crmsh/bootstrap.py index 39a6f4f8..c6c6274d 100644 --- a/crmsh/bootstrap.py +++ b/crmsh/bootstrap.py @@ -58,7 +58,6 @@ class Context(object): self.yes_to_all = None self.template = None self.cluster_name = None - self.diskless_sbd = None self.watchdog = None self.no_overwrite_sshkey = None self.nic = None @@ -72,7 +71,6 @@ class Context(object): self.qdevice_algo = None self.qdevice_tie_breaker = None self.shared_device = None - self.sbd_device = None self.ocfs2_device = None self.cluster_node = None self.force = None @@ -81,6 +79,9 @@ class Context(object): self.tickets = None self.ip_address = None self.ip_network = None + self.sbd_manager = None + self.sbd_devices = None + self.diskless_sbd = None @classmethod def set_context(cls, options): @@ -98,6 +99,229 @@ class Context(object): algo=self.qdevice_algo, tie_breaker=self.qdevice_tie_breaker) + def init_sbd_manager(self): + self.sbd_manager = SBDManager(self.sbd_devices, self.diskless_sbd) + + +class SBDManager(object): + """ + Class to manage sbd configuration and services + """ + if os.path.exists("/usr/share/fillup-templates"): + SYSCONFIG_SBD_TEMPLATE = "/usr/share/fillup-templates/sysconfig.sbd" + else: + SYSCONFIG_SBD_TEMPLATE = "/var/adm/fillup-templates/sysconfig.sbd" + + SBD_STATUS_DESCRIPTION = """ +Configure SBD: + If you have shared storage, for example a SAN or iSCSI target, + you can use it avoid split-brain scenarios by configuring SBD. + This requires a 1 MB partition, accessible to all nodes in the + cluster. The device path must be persistent and consistent + across all nodes in the cluster, so /dev/disk/by-id/* devices + are a good choice. Note that all data on the partition you + specify here will be destroyed. +""" + + def __init__(self, sbd_devices=None, diskless_sbd=False): + """ + Init function + + sbd_devices is provided by '-s' option on init process + diskless_sbd is provided by '-S' option on init process + """ + self.sbd_devices_input = sbd_devices + self.diskless_sbd = diskless_sbd + self._sbd_service_flag = False + self._sbd_devices = None + + @staticmethod + def _check_environment(): + """ + Check prerequisites for SBD + """ + if not check_watchdog(): + error("Watchdog device must be configured in order to use SBD") + if not utils.is_program("sbd"): + error("sbd executable not found! Cannot configure SBD") + + def _parse_sbd_device(self): + """ + Parse sbd devices, possible command line is like: + -s "/dev/sdb1;/dev/sdb2" + -s /dev/sdb1 -s /dev/sbd2 + """ + result_list = [] + for dev in self.sbd_devices_input: + if ';' in dev: + result_list.extend(dev.strip(';').split(';')) + else: + result_list.append(dev) + return result_list + + @staticmethod + def _verify_sbd_device(dev_list): + """ + Verify sbd device + """ + if len(dev_list) > 3: + raise ValueError("Maximum number of SBD device is 3") + for dev in dev_list: + if not is_block_device(dev): + raise ValueError("{} doesn't look like a block device".format(dev)) + + def _get_sbd_device_interactive(self): + """ + Get sbd device on interactive mode + """ + if _context.yes_to_all: + warn("Not configuring SBD (%s left untouched)." % (SYSCONFIG_SBD)) + return + + status(self.SBD_STATUS_DESCRIPTION) + + if not confirm("Do you wish to use SBD?"): + warn("Not configuring SBD - STONITH will be disabled.") + return + + self._check_environment() + + configured_dev = self._get_sbd_device_from_config() + if configured_dev and not confirm("SBD is already configured to use {} - overwrite?".format(';'.join(configured_dev))): + return configured_dev + + dev_list = [] + dev_looks_sane = False + while not dev_looks_sane: + dev = prompt_for_string('Path to storage device (e.g. /dev/disk/by-id/...), or "none" for diskless sbd, use ";" as separator for multi path', r'none|\/.*') + if dev == "none": + self.diskless_sbd = True + return + dev_list = dev.strip(';').split(';') + try: + self._verify_sbd_device(dev_list) + except ValueError as err_msg: + print(term.render(clidisplay.error(str(err_msg)))) + continue + for dev_item in dev_list: + warn("All data on {} will be destroyed!".format(dev_item)) + if confirm('Are you sure you wish to use this device?'): + dev_looks_sane = True + else: + dev_looks_sane = False + break + + return dev_list + + def _get_sbd_device(self): + """ + Get sbd device from options or interactive mode + """ + dev_list = [] + if self.sbd_devices_input: + dev_list = self._parse_sbd_device() + self._verify_sbd_device(dev_list) + self._check_environment() + elif self.diskless_sbd: + self._check_environment() + else: + dev_list = self._get_sbd_device_interactive() + self._sbd_devices = dev_list + + def _initialize_sbd(self): + """ + Initialize SBD device + """ + if self.diskless_sbd: + return + for dev in self._sbd_devices: + if not invoke("sbd -d {} create".format(dev)): + error("Failed to initialize SBD device {}".format(dev)) + + def _update_configuration(self): + """ + Update /etc/sysconfig/sbd + """ + shutil.copyfile(self.SYSCONFIG_SBD_TEMPLATE, SYSCONFIG_SBD) + sbd_config_dict = { + "SBD_PACEMAKER": "yes", + "SBD_STARTMODE": "always", + "SBD_DELAY_START": "no", + "SBD_WATCHDOG_DEV": detect_watchdog_device() + } + if self._sbd_devices: + sbd_config_dict["SBD_DEVICE"] = ';'.join(self._sbd_devices) + utils.sysconfig_set(SYSCONFIG_SBD, **sbd_config_dict) + csync2_update(SYSCONFIG_SBD) + + @staticmethod + def _get_sbd_device_from_config(): + """ + Gets currently configured SBD device, i.e. what's in /etc/sysconfig/sbd + """ + conf = utils.parse_sysconfig(SYSCONFIG_SBD) + res = conf.get("SBD_DEVICE") + if res: + return res.strip(';').split(';') + else: + return None + + def sbd_init(self): + """ + Function sbd_init includes these steps: + 1. Get sbd device from options or interactive mode + 2. Initialize sbd device + 3. Write config file /etc/sysconfig/sbd + """ + self._get_sbd_device() + if not self._sbd_devices and not self.diskless_sbd: + return + status_long("Initializing {}SBD...".format("diskless " if self.diskless_sbd else "")) + self._initialize_sbd() + self._update_configuration() + status_done() + # If process work through here, consider it's ready for enable service + self._sbd_service_flag = True + + def manage_sbd_service(self): + """ + Manage sbd service, running on both init and join process + """ + if self._sbd_service_flag: + invoke("systemctl enable sbd.service") + else: + invoke("systemctl disable sbd.service") + + def configure_sbd_resource(self): + """ + Configure stonith-sbd resource and stonith-enabled property + """ + if self._sbd_devices and self._get_sbd_device_from_config(): + if not invoke("crm configure primitive stonith-sbd stonith:external/sbd pcmk_delay_max=30s"): + error("Can't create stonith-sbd primitive") + if not invoke("crm configure property stonith-enabled=true"): + error("Can't enable STONITH for SBD") + elif self.diskless_sbd: + if not invoke("crm configure property stonith-enabled=true stonith-watchdog-timeout=5s"): + error("Can't enable STONITH for diskless SBD") + + def join_sbd(self, peer_host): + """ + Function join_sbd running on join process only + On joining process, check whether peer node has enabled sbd.service + If so, check prerequisites of SBD and verify sbd device on join node + """ + if not os.path.exists(SYSCONFIG_SBD): + return + if not invoke("ssh -o StrictHostKeyChecking=no root@{} systemctl is-enabled sbd.service".format(peer_host)): + return + self._check_environment() + dev_list = self._get_sbd_device_from_config() + if dev_list: + self._verify_sbd_device(dev_list) + status("Got {}SBD configuration".format("" if dev_list else "diskless ")) + self._sbd_service_flag = True + _context = None @@ -413,14 +637,6 @@ def probe_partitions(): status_done() -def configured_sbd_device(): - """ - Gets currently configured SBD device, i.e. what's in /etc/sysconfig/sbd - """ - conf = utils.parse_sysconfig(SYSCONFIG_SBD) - return conf.get("SBD_DEVICE") - - def check_tty(): """ Check for pseudo-tty: Cannot display read prompts without a TTY (bnc#892702) @@ -680,12 +896,7 @@ def init_cluster_local(): if pass_msg: warn("You should change the hacluster password to something more secure!") - # for cluster join, diskless_sbd flag is set in join_cluster() if - # sbd is running on seed host - if (configured_sbd_device() and _context.sbd_device) or _context.diskless_sbd: - invoke("systemctl enable sbd.service") - else: - invoke("systemctl disable sbd.service") + _context.sbd_manager.manage_sbd_service() start_service("pacemaker.service") wait_for_cluster() @@ -1190,8 +1401,7 @@ def is_block_device(dev): from stat import S_ISBLK try: rc = S_ISBLK(os.stat(dev).st_mode) - except OSError as msg: - warn(msg) + except OSError: return False return rc @@ -1344,42 +1554,6 @@ def check_watchdog(): return rc == 0 -def sysconfig_comment_out(scfile, key): - """ - Comments out the given key in the sysconfig file - """ - matcher = re.compile(r'^\s*{}\s*='.format(key)) - outp, ncomments = "", 0 - for line in scfile.readlines(): - if matcher.match(line): - outp += '#' + line - ncomments += 1 - else: - outp += line - return outp, ncomments - - -def init_sbd_diskless(): - """ - Initialize SBD in diskless mode. - """ - status_long("Initializing diskless SBD...") - if os.path.isfile(SYSCONFIG_SBD): - log("Overwriting {} with diskless configuration".format(SYSCONFIG_SBD)) - scfg, nmatches = sysconfig_comment_out(open(SYSCONFIG_SBD), "SBD_DEVICE") - if nmatches > 0: - utils.str2file(scfg, SYSCONFIG_SBD) - else: - log("Creating {} with diskless configuration".format(SYSCONFIG_SBD)) - utils.sysconfig_set(SYSCONFIG_SBD, - SBD_PACEMAKER="yes", - SBD_STARTMODE="always", - SBD_DELAY_START="no", - SBD_WATCHDOG_DEV=detect_watchdog_device()) - csync2_update(SYSCONFIG_SBD) - status_done() - - def init_sbd(): """ Configure SBD (Storage-based fencing). @@ -1387,108 +1561,7 @@ def init_sbd(): SBD can also run in diskless mode if no device is configured. """ - def get_dev_list(dev_list): - result_list = [] - for dev in dev_list: - if ';' in dev: - result_list.extend(dev.strip(';').split(';')) - else: - result_list.append(dev) - return result_list - - # non-interactive case - if _context.sbd_device: - _context.sbd_device = get_dev_list(_context.sbd_device) - if len(_context.sbd_device) > 3: - error("Maximum number of SBD device is 3") - for dev in _context.sbd_device: - if not is_block_device(dev): - error("{} doesn't look like a block device".format(dev)) - # diskless sbd - elif _context.diskless_sbd: - init_sbd_diskless() - return - # interactive case - else: - # SBD device not set up by init_storage (ocfs2 template) and - # also not passed in as command line argument - prompt user - if _context.yes_to_all: - warn("Not configuring SBD (%s left untouched)." % (SYSCONFIG_SBD)) - return - status(""" -Configure SBD: - If you have shared storage, for example a SAN or iSCSI target, - you can use it avoid split-brain scenarios by configuring SBD. - This requires a 1 MB partition, accessible to all nodes in the - cluster. The device path must be persistent and consistent - across all nodes in the cluster, so /dev/disk/by-id/* devices - are a good choice. Note that all data on the partition you - specify here will be destroyed. -""") - - if not confirm("Do you wish to use SBD?"): - warn("Not configuring SBD - STONITH will be disabled.") - # Comment out SBD devices if present - if os.path.isfile(SYSCONFIG_SBD): - scfg, nmatches = sysconfig_comment_out(open(SYSCONFIG_SBD), "SBD_DEVICE") - if nmatches > 0: - utils.str2file(scfg, SYSCONFIG_SBD) - csync2_update(SYSCONFIG_SBD) - return - - if not check_watchdog(): - error("Watchdog device must be configured if want to use SBD!") - - if utils.is_program("sbd") is None: - error("sbd executable not found! Cannot configure SBD.") - - configured_dev = configured_sbd_device() - if configured_dev: - if not confirm("SBD is already configured to use %s - overwrite?" % (configured_dev)): - return - - dev_looks_sane = False - while not dev_looks_sane: - dev = prompt_for_string('Path to storage device (e.g. /dev/disk/by-id/...), or "none", use ";" as separator for multi path', r'none|\/.*') - if dev == "none": - _context.diskless_sbd = True - init_sbd_diskless() - return - dev_list = dev.strip(';').split(';') - if len(dev_list) > 3: - error("Maximum number of SBD device is 3") - continue - for dev_item in dev_list: - if not is_block_device(dev_item): - error("{} doesn't look like a block device".format(dev_item)) - dev_looks_sane = False - break - else: - warn("All data on {} will be destroyed!".format(dev_item)) - if confirm('Are you sure you wish to use this device?'): - dev_looks_sane = True - else: - dev_looks_sane = False - break - - _context.sbd_device = dev_list - - # TODO: need to ensure watchdog is available - # (actually, should work if watchdog unavailable, it'll just whine in the logs...) - # TODO: what about timeouts for multipath devices? - status_long('Initializing SBD...') - for dev in _context.sbd_device: - if not invoke("sbd -d %s create" % (dev)): - error("Failed to initialize SBD device %s" % (dev)) - status_done() - - utils.sysconfig_set(SYSCONFIG_SBD, - SBD_DEVICE=';'.join(_context.sbd_device), - SBD_PACEMAKER="yes", - SBD_STARTMODE="always", - SBD_DELAY_START="no", - SBD_WATCHDOG_DEV=detect_watchdog_device()) - csync2_update(SYSCONFIG_SBD) + _context.sbd_manager.sbd_init() def init_cluster(): @@ -1512,15 +1585,7 @@ op_defaults op-options: timeout=600 record-pending=true rsc_defaults rsc-options: resource-stickiness=1 migration-threshold=3 """) - if configured_sbd_device() and _context.sbd_device: - if not invoke("crm configure primitive stonith-sbd stonith:external/sbd pcmk_delay_max=30s"): - error("Can't create stonith-sbd primitive") - if not invoke("crm configure property stonith-enabled=true"): - error("Can't enable STONITH for SBD") - elif _context.diskless_sbd: - # TODO: configure stonith-watchdog-timeout correctly - if not invoke("crm configure property stonith-enabled=true stonith-watchdog-timeout=5s"): - error("Can't enable STONITH for diskless SBD") + _context.sbd_manager.configure_sbd_resource() def init_vgfs(): @@ -1847,10 +1912,7 @@ def join_cluster(seed_host): csync2_update(corosync.conf()) invoke("ssh -o StrictHostKeyChecking=no root@{} corosync-cfgtool -R".format(seed_host)) - # if no SBD devices are configured, - # check the existing cluster if the sbd service is enabled - if not configured_sbd_device() and invoke("ssh -o StrictHostKeyChecking=no root@{} systemctl is-enabled sbd.service".format(seed_host)): - _context.diskless_sbd = True + _context.sbd_manager.join_sbd(seed_host) if ipv6_flag and not is_unicast: # for ipv6 mcast @@ -2157,6 +2219,7 @@ def bootstrap_init(context): global _context _context = context _context.init_qdevice() + _context.init_sbd_manager() stage = _context.stage if stage is None: @@ -2219,6 +2282,7 @@ def bootstrap_join(context): """ global _context _context = context + _context.init_sbd_manager() check_tty() diff --git a/crmsh/ui_cluster.py b/crmsh/ui_cluster.py index 8a48b4ee..71929559 100644 --- a/crmsh/ui_cluster.py +++ b/crmsh/ui_cluster.py @@ -242,7 +242,7 @@ Note: storage_group = parser.add_argument_group("Storage configuration", "Options for configuring shared storage.") storage_group.add_argument("-p", "--partition-device", dest="shared_device", metavar="DEVICE", help='Partition this shared storage device (only used in "storage" stage)') - storage_group.add_argument("-s", "--sbd-device", dest="sbd_device", metavar="DEVICE", action="append", + storage_group.add_argument("-s", "--sbd-device", dest="sbd_devices", metavar="DEVICE", action="append", help="Block device to use for SBD fencing, use \";\" as separator or -s multiple times for multi path (up to 3 devices)") storage_group.add_argument("-o", "--ocfs2-device", dest="ocfs2_device", metavar="DEVICE", help='Block device to use for OCFS2 (only used in "vgfs" stage)') @@ -257,8 +257,8 @@ Note: if stage not in bootstrap.INIT_STAGES and stage != "": parser.error("Invalid stage (%s)" % (stage)) - if options.template and options.template != "ocfs2": - parser.error("Invalid template (%s)" % (options.template)) + if options.sbd_devices and options.diskless_sbd: + parser.error("Can't use -s and -S options together") # if options.geo and options.name == "hacluster": # parser.error("For a geo cluster, each cluster must have a unique name (use --name to set)") -- 2.21.1
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor