Ejemplo n.º 1
0
 def test_kill_should_run_taskkill(self, mock_subprocess, *args):
     """Kill valid package status."""
     sm = WinSvcManagerNSSM(svc_conf=CONF_STUB)
     sm.kill()
     cmd = "taskkill /f /fi \"SERVICES eq the_displayname\""
     mock_subprocess.assert_called_once_with(cmd,
                                             **RUN_COMMAND_DEFAULT_KWARGS)
Ejemplo n.º 2
0
 def test_status_should_run_status(self, mock_subprocess, *args):
     """Get valid package status."""
     sm = WinSvcManagerNSSM(svc_conf=CONF_STUB)
     sm.status()
     cmd = 'command status the_displayname'
     mock_subprocess.assert_called_once_with(cmd,
                                             **RUN_COMMAND_DEFAULT_KWARGS)
Ejemplo n.º 3
0
 def test_enable_should_set_auto_start(self, mock_subprocess, *args):
     """Enable valid package."""
     sm = WinSvcManagerNSSM(svc_conf=CONF_STUB)
     sm.enable()
     cmd = 'command set the_displayname start SERVICE_AUTO_START'
     mock_subprocess.assert_called_once_with(cmd,
                                             **RUN_COMMAND_DEFAULT_KWARGS)
Ejemplo n.º 4
0
 def test_remove_should_run_remove_command(self, mock_subprocess, *args):
     """Remove valid package."""
     sm = WinSvcManagerNSSM(svc_conf=CONF_STUB)
     sm.remove()
     cmd = 'command remove the_displayname confirm'
     mock_subprocess.assert_called_once_with(cmd,
                                             **RUN_COMMAND_DEFAULT_KWARGS)
Ejemplo n.º 5
0
 def test_stop_timeout_should_run_kill(self, mock_subprocess, *args):
     """Kill valid package after stop issues. #D2IQ-66446"""
     mock_subprocess.side_effect = exceptions.ServiceManagerCommandError()
     mock_kill = mock.Mock()
     sm = WinSvcManagerNSSM(svc_conf=CONF_STUB)
     sm.kill = mock_kill
     sm.stop()
     assert mock_kill.called
Ejemplo n.º 6
0
def test_displayname_before_name():
    """
    If name and displayname are both set, displayname is used
    """
    conf = {
        'service': {
            'application': 'an_app',
            'displayname': 'a_displayname',
            'name': 'a_name',
        }
    }
    opts = {'svc_conf': conf}
    sm = WinSvcManagerNSSM(**opts)
    assert sm.svc_name == 'a_displayname'
    assert sm._get_svc_setup_pchain() == [('install',
                                           ['a_displayname', 'an_app'])]
Ejemplo n.º 7
0
    def __init__(self,
                 pkg_id=None,
                 istor_nodes=None,
                 cluster_conf=None,
                 manifest=None):
        """Constructor.

        :param pkg_id:       PackageId, package ID
        :param istor_nodes:  IStorNodes, DC/OS installation storage nodes (set
                             of pathlib.Path objects)
        :param cluster_conf: dict, configparser.ConfigParser.read_dict()
                             compatible data. DC/OS cluster setup parameters
        :param manifest:     PackageManifest, DC/OS package manifest object
        """
        if manifest is not None:
            assert isinstance(manifest, PackageManifest), (
                f'Argument: manifest:'
                f' Got {type(manifest).__name__} instead of PackageManifest')
            self.manifest = manifest
        else:
            self.manifest = PackageManifest(pkg_id, istor_nodes, cluster_conf)

        if self.manifest.pkg_extcfg:
            self.ext_manager = PkgInstExtrasManager(
                ext_conf=self.manifest.pkg_extcfg)
        else:
            self.ext_manager = None

        if self.manifest.pkg_svccfg:
            self.svc_manager = WinSvcManagerNSSM(
                svc_conf=self.manifest.pkg_svccfg)
        else:
            self.svc_manager = None
Ejemplo n.º 8
0
    def __init__(self,
                 pkg_id: PackageId = None,
                 istor_nodes: IStorNodes = None,
                 cluster_conf: dict = None,
                 extra_context: dict = None,
                 manifest: PackageManifest = None):
        """Constructor.

        :param pkg_id:        PackageId, package ID
        :param istor_nodes:   IStorNodes, DC/OS installation storage nodes (set
                              of pathlib.Path objects)
        :param cluster_conf:  dict, configparser.ConfigParser.read_dict()
                              compatible data. DC/OS cluster setup parameters
        :param extra_context: dict, extra 'key=value' data to be added to the
                              resource rendering context
        :param manifest:      PackageManifest, DC/OS package manifest object
        """
        self.msg_src = self.__class__.__name__

        if manifest is None:
            manifest = PackageManifest(pkg_id=pkg_id,
                                       istor_nodes=istor_nodes,
                                       cluster_conf=cluster_conf,
                                       extra_context=extra_context)
        self.manifest = manifest

        LOG.debug(f'{self.msg_src}: {self.manifest.pkg_id.pkg_id}: Manifest:'
                  f' {self.manifest}')

        self.cfg_manager = PkgConfManager(pkg_manifest=self.manifest)
        LOG.debug(f'{self.msg_src}: {self.manifest.pkg_id.pkg_id}:'
                  f' Package configuration manager: {self.cfg_manager}')

        if self.manifest.pkg_extcfg:
            self.ext_manager = PkgInstExtrasManager(pkg_manifest=self.manifest)
        else:
            self.ext_manager = None
        LOG.debug(f'{self.msg_src}: {self.manifest.pkg_id.pkg_id}:'
                  f' Installation extras manager: {self.ext_manager}')

        if self.manifest.pkg_svccfg:
            self.svc_manager = WinSvcManagerNSSM(
                svc_conf=self.manifest.pkg_svccfg)
        else:
            self.svc_manager = None
        LOG.debug(f'{self.msg_src}: {self.manifest.pkg_id.pkg_id}:'
                  f' Service manager: {self.svc_manager}')
Ejemplo n.º 9
0
def test_no_service():
    """
    Empty configuration fails
    """
    conf = {}
    opts = {'svc_conf': conf}
    with pytest.raises(ServiceConfigError) as e:
        WinSvcManagerNSSM(**opts)
    assert 'Section not found: service' in str(e)
Ejemplo n.º 10
0
def test_no_application():
    """
    Missing application name fails
    """
    conf = {'service': {'displayname': ''}}
    opts = {'svc_conf': conf}
    with pytest.raises(ServiceConfigError) as e:
        WinSvcManagerNSSM(**opts)
    assert 'Required parameter unavailable: application' in str(e)
Ejemplo n.º 11
0
    def test_parameter_names_should_be_same_as_init(self):
        """Initialize class with all parameters."""
        sm = WinSvcManagerNSSM(svc_conf=CONF_STUB)
        assert sm.svc_name == 'the_displayname'
        assert sm.svc_exec == 'the_application'

        intersection = list(
            set(sm.svc_pnames_bulk) & set(CONF_STUB['service'].keys()))
        assert len(intersection) == 19
Ejemplo n.º 12
0
def test_name_implies_displayname():
    """
    Display name can be derived from name
    """
    conf = {
        'service': {
            'application': '',
            'name': 'a_name',
        }
    }
    opts = {'svc_conf': conf}
    sm = WinSvcManagerNSSM(**opts)
    assert sm.svc_name == 'a_name'
    assert set(sm.svc_pnames_bulk) == set(('application', 'name'))
Ejemplo n.º 13
0
    def execute(self):
        """Execute command."""
        msg_src = self.__class__.__name__

        for pkg_manifest in self.config.inst_storage.get_pkgactive():
            pkg_id = pkg_manifest.pkg_id
            LOG.debug(f'{msg_src}: Execute: Manifest: {pkg_manifest.body}')
            svc_conf = cfp.ConfigParser()
            svc_conf.read_dict(pkg_manifest.svc_conf)
            cluster_conf = cfp.ConfigParser()
            cluster_conf.read_dict(self.config.cluster_conf)
            svc_manager = WinSvcManagerNSSM(
                svc_conf=svc_conf, cluster_conf=cluster_conf
            )

            try:
                ret_code, stdout, stderr = svc_manager.status()
            except svcm_exc.ServiceManagerCommandError as e:
                err_msg = (f'Execute: Get service status (initial):'
                           f' {pkg_id.pkg_name}: {e}')
                raise cr_exc.StartCommandError(err_msg)
            else:
                LOG.debug(
                    f'{msg_src}: Execute: Get service status (initial):'
                    f' {pkg_id.pkg_name}: stdout[{stdout}] stderr[{stderr}]'
                )
                svc_status = str(stdout).strip().rstrip('\n')

            if svc_status == SVC_STATUS.STOPPED:
                try:
                    svc_manager.start()
                    ret_code, stdout, stderr = svc_manager.status()
                    LOG.debug(
                        f'{msg_src}: Execute: Get service status (final):'
                        f' {pkg_id.pkg_name}: stdout[{stdout}]'
                        f' stderr[{stderr}]'
                    )
                    svc_status = str(stdout).strip().rstrip('\n')
                    if svc_status != SVC_STATUS.RUNNING:
                        err_msg = (f'Execute: Service failed to start:'
                                   f' {pkg_id.pkg_name}: {svc_status}')
                        raise cr_exc.StartCommandError(err_msg)
                except svcm_exc.ServiceManagerCommandError as e:
                    err_msg = (f'Execute: Get service status (final):'
                               f' {pkg_id.pkg_name}: {e}')
                    raise cr_exc.StartCommandError(err_msg)
            elif svc_status == SVC_STATUS.RUNNING:
                LOG.warning(f'{msg_src}: Execute: Service is already running:'
                            f' {pkg_id.pkg_name}')
            else:
                err_msg = (f'Execute: Invalid service status:'
                           f' {pkg_id.pkg_name}: {svc_status}')
                raise cr_exc.StartCommandError(err_msg)
Ejemplo n.º 14
0
    def __init__(self, pkg_id, pkgrepo_dpath, pkgactive_dpath, cluster_conf):
        """Constructor.

        :param pkg_id:          PackageId, package ID
        :param pkgrepo_dpath:   pathlib.Path, local package repository dir
        :param pkgactive_dpath: pathlib.Path, active packages index dir
        :param cluster_conf:    dict, configparser.ConfigParser.read_dict()
                                compatible data. DC/OS cluster setup parameters
        """
        self.manifest = PackageManifest(pkg_id, pkgrepo_dpath, pkgactive_dpath)

        self.ini_manager = None

        self.svc_conf = cfp.ConfigParser()
        self.svc_conf.read_dict(self.manifest.svc_conf)
        self.cluster_conf = cfp.ConfigParser()
        self.cluster_conf.read_dict(cluster_conf)
        self.svc_manager = WinSvcManagerNSSM(svc_conf=self.svc_conf,
                                             cluster_conf=self.cluster_conf)
Ejemplo n.º 15
0
 def test_display_name_only_in_configuration_should_fail(self):
     """No service configuration application display name only validation exception."""
     conf = {'service': {'displayname': 'test'}}
     with pytest.raises(exceptions.ServiceConfigError):
         WinSvcManagerNSSM(svc_conf=conf)
Ejemplo n.º 16
0
 def test_configuration_without_name_should_fail(self):
     """No service configuration name validation exception."""
     conf = {'service': {'something': ''}}
     with pytest.raises(exceptions.ServiceConfigError):
         WinSvcManagerNSSM(svc_conf=conf)
Ejemplo n.º 17
0
 def test_empty_configuration_should_fail(self):
     """No service configuration validation exception."""
     with pytest.raises(exceptions.ServiceConfigError):
         WinSvcManagerNSSM()
Ejemplo n.º 18
0
def test_all_parameters():
    """
    All parameters get translated to command arguments
    """
    conf = {
        'service': {
            'description': 'the_description',
            'displayname': 'the_displayname',
            'name': 'the_name',
            'application': 'the_application',
            'appdirectory': 'the_appdirectory',
            'appparameters': 'the_appparameters',
            'start': 'the_start',
            'dependonservice': 'the_dependonservice',
            'appstdout': 'the_appstdout',
            'appstderr': 'the_appstderr',
            'appenvironmentextra': 'the_appenvironmentextra',
            'appeventsstartpre': 'the_appevents_start_pre',
            'appeventsstartpost': 'the_appevents_start_post',
            'appeventsstoppre': 'the_appevents_stop_pre',
            'appeventsexitpost': 'the_appevents_exit_post',
            'appeventsrotatepre': 'the_appevents_rotate_pre',
            'appeventsrotatepost': 'the_appevents_rotate_post',
            'appeventspowerchange': 'the_appevents_power_change',
            'appeventspowerresume': 'the_appevents_power_resume',
            'appredirecthook': 'the_appredirecthook',
        }
    }
    opts = {'svc_conf': conf}
    sm = WinSvcManagerNSSM(**opts)
    assert sm.svc_name == 'the_displayname'
    assert sm.svc_exec == 'the_application'
    expected_names = set(conf['service'].keys())
    # if `displayname` is set, then `name` is removed
    expected_names.remove('name')
    assert set(sm.svc_pnames_bulk) == expected_names

    assert sm._get_svc_setup_pchain() == [
        ('install', ['the_displayname', 'the_application']),
        ('set', ['the_displayname', 'description', 'the_description']),
        ('set', ['the_displayname', 'appdirectory', 'the_appdirectory']),
        ('set', ['the_displayname', 'appparameters', 'the_appparameters']),
        ('set', ['the_displayname', 'start', 'the_start']),
        ('set', ['the_displayname', 'dependonservice', 'the_dependonservice']),
        ('set', ['the_displayname', 'appstdout', 'the_appstdout']),
        ('set', ['the_displayname', 'appstderr', 'the_appstderr']),
        ('set',
         ['the_displayname', 'appenvironmentextra',
          'the_appenvironmentextra']),
        ('set', [
            'the_displayname', 'appevents', 'start/pre',
            'the_appevents_start_pre'
        ]),
        ('set', [
            'the_displayname', 'appevents', 'start/post',
            'the_appevents_start_post'
        ]),
        ('set', [
            'the_displayname', 'appevents', 'stop/pre',
            'the_appevents_stop_pre'
        ]),
        ('set', [
            'the_displayname', 'appevents', 'exit/post',
            'the_appevents_exit_post'
        ]),
        ('set', [
            'the_displayname', 'appevents', 'rotate/pre',
            'the_appevents_rotate_pre'
        ]),
        ('set', [
            'the_displayname', 'appevents', 'rotate/post',
            'the_appevents_rotate_post'
        ]),
        ('set', [
            'the_displayname', 'appevents', 'power/change',
            'the_appevents_power_change'
        ]),
        ('set', [
            'the_displayname', 'appevents', 'power/resume',
            'the_appevents_power_resume'
        ]),
        ('set', ['the_displayname', 'appredirecthook', 'the_appredirecthook']),
    ]
Ejemplo n.º 19
0
 def test_empty_exec_path_should_fail(self):
     """Setup package with valid but not non executable configuration."""
     sm = WinSvcManagerNSSM(svc_conf=CONF_STUB)
     with pytest.raises(exceptions.ServiceManagerSetupError):
         sm.setup()
Ejemplo n.º 20
0
 def test_setup_should_exec_18_commands(self, mock_subprocess, *args):
     """Setup valid package."""
     sm = WinSvcManagerNSSM(svc_conf=CONF_STUB)
     sm.setup()
     assert mock_subprocess.call_count == 18
Ejemplo n.º 21
0
 def test_subprocess_error_should_fail(self, *args):
     """Setup package with valid but not non executable configuration."""
     sm = WinSvcManagerNSSM(svc_conf=CONF_STUB)
     with pytest.raises(exceptions.ServiceManagerCommandError):
         sm.setup()
Ejemplo n.º 22
0
class Package:
    """Package manager."""
    def __init__(self, pkg_id: PackageId = None, istor_nodes: IStorNodes = None,
                 cluster_conf: dict = None, extra_context: dict = None,
                 manifest: PackageManifest = None):
        """Constructor.

        :param pkg_id:        PackageId, package ID
        :param istor_nodes:   IStorNodes, DC/OS installation storage nodes (set
                              of pathlib.Path objects)
        :param cluster_conf:  dict, configparser.ConfigParser.read_dict()
                              compatible data. DC/OS cluster setup parameters
        :param extra_context: dict, extra 'key=value' data to be added to the
                              resource rendering context
        :param manifest:      PackageManifest, DC/OS package manifest object
        """
        self.msg_src = self.__class__.__name__

        if manifest is None:
            manifest = PackageManifest(
                pkg_id=pkg_id, istor_nodes=istor_nodes,
                cluster_conf=cluster_conf, extra_context=extra_context
            )
        self.manifest = manifest

        self.cfg_manager = PkgConfManager(pkg_manifest=self.manifest)

        if self.manifest.pkg_extcfg:
            self.ext_manager = PkgInstExtrasManager(pkg_manifest=self.manifest)
        else:
            self.ext_manager = None
        LOG.debug(f'{self.msg_src}: {self.manifest.pkg_id.pkg_id}:'
                  f' Installation extras manager: {self.ext_manager}')

        if self.manifest.pkg_svccfg:
            self.svc_manager = WinSvcManagerNSSM(
                svc_conf=self.manifest.pkg_svccfg
            )
        else:
            self.svc_manager = None
        LOG.debug(f'{self.msg_src}: {self.manifest.pkg_id.pkg_id}:'
                  f' Service manager: {self.svc_manager}')

    @property
    def id(self):
        """"""
        return self.manifest.pkg_id

    def handle_config_setup(self, mheading: str=None):
        """Execute steps on setting up package configuration files.

        :param mheading: str, descriptive heading to be added to error/log
                         messages
        """
        mhd_base = f'{self.msg_src}: {self.id.pkg_name}'
        mheading = f'{mheading}: {mhd_base}' if mheading else mhd_base
        LOG.debug(f'{mheading}: Setup configuration: ...')

        try:
            self.cfg_manager.setup_conf()
        except cfgm_exc.PkgConfNotFoundError as e:
            LOG.debug(f'{mheading}: Setup configuration: NOP')
        else:
            LOG.debug(f'{mheading}: Setup configuration: OK')

    def handle_inst_extras(self, mheading: str=None):
        """Process package's extra installation options.

        :param mheading: str, descriptive heading to be added to error/log
                         messages
        """
        mhd_base = f'{self.msg_src}: {self.id.pkg_name}'
        mheading = f'{mheading}: {mhd_base}' if mheading else mhd_base

        if self.ext_manager:
            LOG.debug(f'{mheading}: Handle extra installation options: ...')
            self.ext_manager.handle_install_extras()
            LOG.debug(f'{mheading}: Handle extra installation options: OK')
        else:
            LOG.debug(f'{mheading}: Handle extra installation options: NOP')

    def handle_uninst_extras(self, mheading: str=None):
        """Process package's extra uninstall options.

        :param mheading: str, descriptive heading to be added to error/log
                         messages
        """
        mhd_base = f'{self.msg_src}: {self.id.pkg_name}'
        mheading = f'{mheading}: {mhd_base}' if mheading else mhd_base

        if self.ext_manager:
            LOG.debug(f'{mheading}: Handle extra uninstall options: ...')
            self.ext_manager.handle_uninstall_extras()
            LOG.debug(f'{mheading}: Handle extra uninstall options: OK')
        else:
            LOG.debug(f'{mheading}: Handle extra uninstall options: NOP')

    def handle_svc_setup(self, mheading: str=None):
        """Execute steps on package's service setup.

        :param mheading: str, descriptive heading to be added to error/log
                         messages
        """
        mhd_base = f'{self.msg_src}: {self.id.pkg_name}'
        mheading = f'{mheading}: {mhd_base}' if mheading else mhd_base
        no_svc_marker = 'service does not exist as an installed service.'

        if self.svc_manager:
            svc_name = self.svc_manager.svc_name
            LOG.debug(f'{mheading}: Setup service: {svc_name}: ...')
            try:
                ret_code, stdout, stderr = self.svc_manager.status()
            except svcm_exc.ServiceManagerCommandError as e:
                exc_msg_line = str(e).replace('\n', '').strip()
                LOG.debug(f'{mheading}: Setup service: {svc_name}:'
                          f' Get initial service status: {exc_msg_line}')
                if not exc_msg_line.endswith(no_svc_marker):
                    err_msg = f'{mheading}: Setup service: {svc_name}:' \
                              f' Get initial service status: {e}'
                    raise svcm_exc.ServiceSetupError(err_msg) from e
                # Setup a service
                try:
                    self.svc_manager.setup()
                except svcm_exc.ServiceManagerCommandError as e:
                    err_msg = f'{mheading}: Setup service: {svc_name}: {e}'
                    raise svcm_exc.ServiceSetupError(err_msg) from e
            else:
                LOG.debug(
                    f'{mheading}: Setup service: {svc_name}: Get initial'
                    f' service status: stdout[{stdout}] stderr[{stderr}]'
                )
                svc_status = str(stdout).strip().rstrip('\n')
                # Wipe existing service
                try:
                    if svc_status != SVC_STATUS.STOPPED:
                        self.svc_manager.stop()

                    self.svc_manager.remove()
                    LOG.debug(f'{mheading}: Wipe existing service:'
                              f' {svc_name}: OK')
                except svcm_exc.ServiceManagerCommandError as e:
                    err_msg = (f'{mheading}: Wipe existing service:'
                               f' {svc_name}: {e}')
                    raise svcm_exc.ServiceSetupError(err_msg) from e
                # Setup a replacement service
                try:
                    self.svc_manager.setup()
                    ret_code, stdout, stderr = (self.svc_manager.status())
                    svc_status = str(stdout).strip().rstrip('\n')
                except svcm_exc.ServiceManagerCommandError as e:
                    err_msg = (f'{mheading}: Setup replacement service:'
                               f' {svc_name}: {e}')
                    raise svcm_exc.ServiceSetupError(err_msg) from e
                else:
                    if svc_status != SVC_STATUS.STOPPED:
                        err_msg = (
                            f'{mheading}: Setup replacement service:'
                            f' {svc_name}: Invalid status: {svc_status}'
                        )
                        raise svcm_exc.ServiceSetupError(err_msg)

            LOG.debug(f'{mheading}: Setup service: {svc_name}: OK')
        else:
            LOG.debug(f'{mheading}: Setup service: NOP')

    def handle_svc_wipe(self, mheading: str=None):
        """Execute steps on package's service wipe off.

        :param mheading: str, descriptive heading to be added to error/log
                         messages
        """
        mhd_base = f'{self.msg_src}: {self.id.pkg_name}'
        mheading = f'{mheading}: {mhd_base}' if mheading else mhd_base
        no_svc_marker = 'service does not exist as an installed service.'

        if self.svc_manager:
            svc_name = self.svc_manager.svc_name
            LOG.debug(f'{mheading}: Wipe service: {svc_name}: ...')
            try:
                ret_code, stdout, stderr = self.svc_manager.status()
            except svcm_exc.ServiceManagerCommandError as e:
                exc_msg_line = str(e).replace('\n', '').strip()
                LOG.debug(f'{mheading}: Wipe service: {svc_name}:'
                          f' Get initial service status: {exc_msg_line}')
                if not exc_msg_line.endswith(no_svc_marker):
                    err_msg = f'{mheading}: Wipe service: {svc_name}: {e}'
                    raise svcm_exc.ServiceWipeError(err_msg) from e
                LOG.debug(f'{mheading}: Wipe service: NOP')
            else:
                LOG.debug(
                    f'{mheading}: Wipe service: {svc_name}: Get initial'
                    f' service status: stdout[{stdout}] stderr[{stderr}]'
                )
                svc_status = str(stdout).strip().rstrip('\n')
                # Try to remove existing service
                try:
                    if svc_status != SVC_STATUS.STOPPED:
                        self.svc_manager.stop()

                    self.svc_manager.remove()
                    LOG.debug(f'{mheading}: Wipe service: {svc_name}: OK')
                except svcm_exc.ServiceManagerCommandError as e:
                    err_msg = (f'{mheading}: Wipe existing service:'
                               f' {svc_name}: {e}')
                    raise svcm_exc.ServiceWipeError(err_msg) from e
        else:
            LOG.debug(f'{mheading}: Wipe service: NOP')

    def handle_svc_start(self, mheading: str=None):
        """Execute steps on package's service start.

        :param mheading: str, descriptive heading to be added to error/log
                         messages
        """
        mhd_base = f'{self.msg_src}: {self.id.pkg_name}'
        mheading = f'{mheading}: {mhd_base}' if mheading else mhd_base
        no_svc_marker = 'service does not exist as an installed service.'
        # TODO: Move stuff from the CmdStart.service_start() method here.
        #       Overall design of this method should resemble one of the
        #       Package.handle_svc_stop() method.

    def handle_svc_stop(self, mheading: str=None):
        """Execute steps on package's service stop.

        :param mheading: str, descriptive heading to be added to error/log
                         messages
        """
        mhd_base = f'{self.msg_src}: {self.id.pkg_name}'
        mheading = f'{mheading}: {mhd_base}' if mheading else mhd_base
        no_svc_marker = 'service does not exist as an installed service.'

        if self.svc_manager:
            svc_name = self.svc_manager.svc_name
            LOG.debug(f'{mheading}: Stop service: {svc_name}: ...')
            try:
                ret_code, stdout, stderr = self.svc_manager.status()
            except svcm_exc.ServiceManagerCommandError as e:
                exc_msg_line = str(e).replace('\n', '').strip()
                LOG.debug(f'{mheading}: Stop service: {svc_name}:'
                          f' Get initial service status: {exc_msg_line}')
                if not exc_msg_line.endswith(no_svc_marker):
                    err_msg = f'{mheading}: Stop service: {svc_name}: {e}'
                    raise svcm_exc.ServiceStopError(err_msg) from e
                LOG.debug(f'{mheading}: Stop service: NOP')
            else:
                LOG.debug(
                    f'{mheading}: Stop service: {svc_name}: Get initial'
                    f' service status: stdout[{stdout}] stderr[{stderr}]'
                )
                svc_status = str(stdout).strip().rstrip('\n')
                # Stop existing service
                try:
                    if svc_status != SVC_STATUS.STOPPED:
                        self.svc_manager.stop()

                    LOG.debug(f'{mheading}: Stop service: {svc_name}: OK')
                except svcm_exc.ServiceManagerCommandError as e:
                    err_msg = f'{mheading}: Stop service: {svc_name}: {e}'
                    raise svcm_exc.ServiceStopError(err_msg) from e
        else:
            LOG.debug(f'{mheading}: Stop service: NOP')

    def handle_vardata_wipe(self, mheading: str=None):
        """Execute steps on wiping of package's variable data.

        :param mheading: str, descriptive heading to be added to error/log
                         messages
        """
        mhd_base = f'{self.msg_src}: {self.id.pkg_name}'
        mheading = f'{mheading}: {mhd_base}' if mheading else mhd_base

        work_dpath = getattr(self.manifest.istor_nodes, ISTOR_NODE.WORK)
        run_dpath = getattr(self.manifest.istor_nodes, ISTOR_NODE.RUN)

        for host_dpath in work_dpath, run_dpath:
            dpath = host_dpath.joinpath(self.id.pkg_name)
            try:
                cm_utl.rmdir(str(dpath), recursive=True)
                dpath.mkdir(parents=True, exist_ok=True)
                LOG.debug(f'{mheading}: Wipe variable data: {dpath}: OK')
            except (OSError, RuntimeError) as e:
                err_msg = (f'{mheading}: Wipe variable data: {dpath}:'
                           f' {type(e).__name__}: {e}')
                raise cr_exc.RCError(err_msg) from e

    def save_manifest(self, mheading: str=None, dpath: Path=None):
        """Save package's manifest to filesystem.

        :param mheading: str, descriptive heading to be added to error/log
                         messages
        :param dpath:    Path, absolute path to the host directory
                         where to save to
        """
        mhd_base = f'{self.msg_src}: {self.id.pkg_name}'
        mheading = f'{mheading}: {mhd_base}' if mheading else mhd_base

        try:
            cm_utl.rmdir(str(dpath), recursive=True)
            dpath.mkdir(parents=True)
            self.manifest.save(dpath)
        except (OSError, RuntimeError, cr_exc.RCError) as e:
            err_msg = f'{mheading}: Register package: {self.id}: {e}'
            raise cr_exc.RCError(err_msg) from e

    def delete_manifest(self, mheading: str=None, dpath: Path=None):
        """Delete package's manifest from filesystem.

        :param mheading: str, descriptive heading to be added to error/log
                         messages
        :param dpath:    Path, absolute path to the host directory
                         where to delete from
        """
        mhd_base = f'{self.msg_src}: {self.id.pkg_name}'
        mheading = f'{mheading}: {mhd_base}' if mheading else mhd_base

        try:
            self.manifest.delete(dpath)
        except (OSError, RuntimeError, cr_exc.RCError) as e:
            err_msg = f'{mheading}: Deregister package: {self.id}: {e}'
            raise cr_exc.RCError(err_msg) from e