Difference between revisions of "SME10 serviceControl"

From SME Server
Jump to navigationJump to search
(Created page with "Category:SME10-Development")
 
(initial work)
Line 1: Line 1:
 +
=== Previous version boot process and service control ===
 +
Basically we use SysVinit, with initscripts built to handle a specific run level rc7.d. On top of that we use Runit to handle services indexed in /services / and /var/services.
 +
 +
A cli wrapper for the command service  /sbin/e-smith/service has been created so that only  initscripts which exist in run-level 7 can be run. This ensures that  the supervised service is run, if one exists, and protects against  running "service httpd restart". The wrapper will also choose between a sv command for runit processes or regular call to /etc/rc.d/init.d/ scripts.
 +
* /sbin/e-smith/service
 +
<syntaxhighlight lang="bash">
 +
#! /bin/sh
 +
 +
runlevel=$(runlevel | cut -d" " -f2)
 +
 +
if [ "$runlevel" = "4" ]
 +
then
 +
    if ls /etc/rc7.d/S??$1 >/dev/null 2>/dev/null
 +
    then
 +
      script=$(ls /etc/rc7.d/S??$1 | head -1)
 +
      exec $script $2
 +
    fi
 +
 +
    echo "'$1' is not a valid service name" 1>&2
 +
    exit 1
 +
else
 +
    exec /sbin/service "$@"
 +
fi
 +
 +
</syntaxhighlight>
 +
 +
=== Current SME10 alpha boot process and service control ===
 +
Systemd has been designed as the way to handle services upstream. Systemd will override all /etc/rc.d/init.d/ scripts and call to /usr/sbin/service.
 +
 +
We have added a first systemd unit for bootstrap console and make it a drop in replacement of Sysvinit to boot all processes linked in /etc/rc.d/rc7.d/.
 +
 +
From there we could keep this way, or try to move as many process as we can to systemd. This way works, but is even more complex than it was on previous SME version. Plus we can not guarantee without further scripts that one will not be able to do a ''systemctl start httpd''.
 +
 +
a new /sbin/e-smith/service<syntaxhighlight lang="bash" line="1">
 +
#! /bin/sh
 +
# prevent initscript to use systemctl
 +
export SYSTEMCTL_SKIP_REDIRECT=1
 +
. /etc/rc.d/init.d/functions
 +
 +
# what is our current runlevel
 +
runlevel=$(systemctl get-default)
 +
SERVICE=$1
 +
USAGE="Usage: service SERVICENAME [ACTION]"
 +
 +
#if no servicename is provided return usage
 +
if [[ "${SERVICE}" == "" ]]
 +
then
 +
  echo ${USAGE} >&2
 +
  exit
 +
fi
 +
 +
if [ "$runlevel" = "multi-user.target" ]
 +
then
 +
    if ls /etc/rc7.d/S??$1 >/dev/null 2>/dev/null
 +
    then
 +
      script=$(ls /etc/rc7.d/S??$1 | head -1)
 +
      exec $script $2
 +
 +
    elif ls /usr/lib/systemd/system/${SERVICE}.service >/dev/null 2>/dev/null || ls /etc/systemd/system/${SERVICE}.service >/dev/null 2>/dev/null
 +
    then
 +
if [[ "$2" == "" ]] ; then
 +
        echo "'$1' requires an action" 1>&2
 +
        echo ${USAGE} >&2
 +
        exit
 +
        elif  [[ $2 == "status" ]] ; then
 +
        exec /bin/systemctl status -n0 ${SERVICE}
 +
        exit
 +
        elif [[ $2 == "start" ]] ; then
 +
        echo -n  "Starting ${SERVICE}" 2>/dev/null
 +
        elif [[ $2 == "stop" ]] ; then
 +
        echo -n  "Stopping ${SERVICE}" 2>/dev/null
 +
        elif [[ $2 == "restart" ]] ; then
 +
        echo -n  "Restarting ${SERVICE}" 2>/dev/null
 +
        else
 +
        echo -n  "Sending $2 signal to ${SERVICE}" 2>/dev/null
 +
        fi
 +
        /bin/systemctl $2 ${SERVICE}.service> /dev/null
 +
        if [ $? -ne 0 ]; then
 +
        echo_failure
 +
        else
 +
        echo_success
 +
        fi
 +
        echo
 +
        exit
 +
    fi
 +
 +
    echo "'$1' is not a valid service name" 1>&2
 +
    exit 1
 +
else
 +
    exec /sbin/service "$@"
 +
fi
 +
 +
</syntaxhighlight>
 +
 +
And the current controlService perl function from /usr/share/perl5/vendor_perl/esmith/util.pm<syntaxhighlight lang="perl" line="1">
 +
=pod
 +
 +
=head1 SERVICE MANAGEMENT UTILITIES
 +
 +
=head2 serviceControl()
 +
 +
Manage services - stop/start/restart/reload/graceful
 +
 +
Returns 1 for success, 0 if something went wrong, fatal exception on bad
 +
arguments.
 +
 +
    serviceControl(
 +
        NAME=>serviceName,
 +
        ACTION=>start|stop|restart|reload|graceful
 +
        [ BACKGROUND=>true|false (default is false) ]
 +
    );
 +
 +
EXAMPLE:
 +
 +
    serviceControl( NAME=>'httpd-e-smith', ACTION=>'reload' );
 +
 +
NOTES:
 +
 +
The BACKGROUND parameter is optional and can be set to true if
 +
start/stop/restart/etc. is to be done in the background (with
 +
backgroundCommand()) rather than synchronously.
 +
 +
CONVENTIONS:
 +
 +
This command is the supported method for action scripts, blade handlers, etc.,
 +
to start/stop/restart their services. Currently this is done via the rc7
 +
symlinks, but this may change one day. Using this function gives us one
 +
location to change this behaviour if desired, instead of hunting all over
 +
every scrap of code. Please use it.
 +
 +
=cut
 +
 +
sub serviceControl
 +
{
 +
    my %params = @_;
 +
 +
    my $serviceName = $params{NAME};
 +
    unless ( defined $serviceName )
 +
    {
 +
        die "serviceControl: NAME must be specified";
 +
    }
 +
 +
    my $serviceAction = $params{ACTION};
 +
    unless (defined $serviceAction)
 +
    {
 +
        die "serviceControl: ACTION must be specified";
 +
    }
 +
 +
    if ( $serviceAction =~ /^(start|stop|restart|reload|graceful|adjust|svdisable)$/ )
 +
    {
 +
        my ($startScript) = glob("/etc/rc.d/rc7.d/S*$serviceName") ||'' ;
 +
        my ($systemdScript) = "/usr/lib/systemd/system/$serviceName.service" ||'';
 +
 +
        unless ( -e $startScript or -e $systemdScript)
 +
        {
 +
            warn "serviceControl: startScript not found "
 +
              . "for service $serviceName\n";
 +
            return 0;
 +
        }
 +
 +
        if (-e $systemdScript and ! -e $startScript){
 +
            if ($serviceAction =~/^(start|stop|restart|reload)$/) {
 +
                system('/usr/bin/systemctl',"$serviceAction","$serviceName.service") == '0'
 +
                ||    warn "serviceControl: Couldn't " .
 +
                "system( /usr/bin/systemctl $serviceAction $serviceName.service): $!\n";
 +
            }
 +
            else {
 +
                die "serviceControl: systemd doesn't know : systemctl $serviceAction $serviceName.service";
 +
            }
 +
        }
 +
 +
        elsif (-e $startScript) {
 +
            my  $background = $params{'BACKGROUND'} || 'false';
 +
 +
            if ( $background eq 'true' )
 +
            {
 +
            backgroundCommand( 0, $startScript, $serviceAction );
 +
            }
 +
            elsif ( $background eq 'false' )
 +
            {
 +
                unless ( system( $startScript, $serviceAction ) == 0 )
 +
                {
 +
                warn "serviceControl: "
 +
                  . "Couldn't system($startScript, $serviceAction): $!\n";
 +
                return 0;
 +
                }
 +
          }
 +
          else
 +
          {
 +
            die "serviceControl: Unsupported BACKGROUND=>$background";
 +
          }
 +
        }
 +
    }
 +
    else
 +
    {
 +
        die "serviceControl: Unknown serviceAction $serviceAction";
 +
    }
 +
    return 1;
 +
}
 +
 +
</syntaxhighlight>
 +
 +
=== Systemd Integration possibilities ===
 +
this is intended to make an open discussion about possibilities, in order to choose the best SME approach to systemd.
 +
 +
Issue to solve :
 +
* SME vs upstream
 +
* make unit files aware of service status in e-smith db configuration
 +
* have our services boot in the right sequence
 +
* journald vs rsyslogd
 +
* plain file log vs  special file format
 +
* enabling /disabling /masking services
 +
 +
'''Table 1.         Load path when running in system mode (<code>--system</code>).'''      <ref>https://www.freedesktop.org/software/systemd/man/systemd.unit.html</ref>
 +
{| class="wikitable"
 +
!Path
 +
!Description
 +
|-
 +
|<code>/etc/systemd/system.control</code>
 +
| rowspan="2" |Persistent and transient configuration created using the dbus API
 +
|-
 +
|<code>/run/systemd/system.control</code>
 +
|-
 +
|<code>/run/systemd/transient</code>
 +
|Dynamic configuration for transient units
 +
|-
 +
|<code>/run/systemd/generator.early</code>
 +
|Generated units with high priority (see ''<code>early-dir</code>'' in systemd.generator(7))
 +
|-
 +
|<code>/etc/systemd/system</code>
 +
|System units created by the administrator
 +
|-
 +
|<code>/run/systemd/system</code>
 +
|Runtime units
 +
|-
 +
|<code>/run/systemd/generator</code>
 +
|Generated units with medium priority (see ''<code>normal-dir</code>'' in systemd.generator(7))
 +
|-
 +
|<code>/usr/local/lib/systemd/system</code>
 +
|System units installed by the administrator
 +
|-
 +
|<code>/usr/lib/systemd/system</code>
 +
|System units installed by the distribution package manager
 +
|-
 +
|<code>/run/systemd/generator.late</code>
 +
|Generated units with low priority (see ''<code>late-dir</code>'' in systemd.generator(7))
 +
|}
 +
 +
==== generators ====
 +
there have been suggestions about using a generator, it is not clear how and why it would help.  This approach would be more complex. and according to the doc<ref>https://www.freedesktop.org/software/systemd/man/systemd.generator.html#</ref>:
 +
* Units written by generators are removed when the configuration is  reloaded. That means the lifetime of the generated units is closely bound to the reload cycles of '''systemd''' itself.
 +
* Generators should only be used to generate unit files and symlinks to them, not any other kind of configuration. Due to the lifecycle logic mentioned above, generators are not a good fit to generate dynamic configuration for other services. If you need to generate dynamic configuration for other services, do so in normal services you order before the service in question.
 +
systemctl daemon-reload has be run after each modification of a unit. Hence generated files by generators will be erase and generators rerun. <ref>https://www.freedesktop.org/software/systemd/man/systemctl.html#daemon-reload</ref>
 +
 +
==== Overide upstream vendor preset : templated unit files or not ? where ? ====
 +
Thinking of the example of a service unit provided by upstream vendor. Should we simply create a template and expand it over the unit.service in /lib/systemd/system/ or offer a different way to override upstream vendor settings ? Further more should we template our file to point to /lib/ or to /etc/. In other words, should we fight with admin space trying to overwrite changes in its dedicated space or should we fight with upstream vendors and overwrite their files or find a way to override them.
 +
 +
hence the questions
 +
* where: /etc or /lib/
 +
* How : template or not
 +
* How: overwrite or selective overriding
 +
 +
===== unit files created in /etc/systemd/system/ or in /lib/systemd/system/ =====
 +
as a vendor we should work in /lib/systemd/system/, but as an admin helper we might want to play in /etc/systemd/system/ and rather offer a way to admin to use our template-custom or config db.
 +
 +
==== template or not ====
 +
the first easy way to think to override ones service could be to simply template the file  /lib/systemd/system/servicename.service and expand it every time a reconfiguration or a boot occurs.
 +
 +
Going futher we could ensure the stability of the system by making this in /etc/systemd/system/servicename.service
 +
 +
===== unitname.d/file.conf versus overwrite uniname.service =====
 +
dot d folder can be created both int etc and lib from readings and inspection in a few systems.
 +
 +
There are two methods of overriding vendor settings in      unit files: copying the unit file from      <code>/usr/lib/systemd/system</code> to      <code>/etc/systemd/system</code> and modifying the      chosen settings. Alternatively, one can create a directory named      <code>''<code>unit</code>''.d/</code> within      <code>/etc/systemd/system</code> and place a drop-in      file <code>''<code>name</code>''.conf</code>      there that only changes the specific settings one is interested      in. Note that multiple such drop-in files are read if      present, processed in lexicographic order of their filename.
 +
 +
The advantage of the first method is that one easily      overrides the complete unit, the vendor unit is not parsed at      all anymore. It has the disadvantage that improvements to the      unit file by the vendor are not automatically incorporated on      updates.
 +
 +
The advantage of the second method is that one only      overrides the settings one specifically wants, where updates to      the unit by the vendor automatically apply. This has the      disadvantage that some future updates by the vendor might be      incompatible with the local changes. <ref>https://www.freedesktop.org/software/systemd/man/systemd.unit.html</ref>
 +
 +
==== uninstalling /masking unwanted/conflicting services: example of firewalld. ====
 +
we can plan to uninstall firewalld for example, but some packages will reinstall as a requirement. It might even be started, which could conflict with masq.
 +
 +
=== References ===
 +
<references />
 
[[Category:SME10-Development]]
 
[[Category:SME10-Development]]

Revision as of 19:07, 3 December 2019

Previous version boot process and service control

Basically we use SysVinit, with initscripts built to handle a specific run level rc7.d. On top of that we use Runit to handle services indexed in /services / and /var/services.

A cli wrapper for the command service /sbin/e-smith/service has been created so that only initscripts which exist in run-level 7 can be run. This ensures that the supervised service is run, if one exists, and protects against running "service httpd restart". The wrapper will also choose between a sv command for runit processes or regular call to /etc/rc.d/init.d/ scripts.

  • /sbin/e-smith/service
#! /bin/sh

runlevel=$(runlevel | cut -d" " -f2)

if [ "$runlevel" = "4" ]
then
    if ls /etc/rc7.d/S??$1 >/dev/null 2>/dev/null
    then
       script=$(ls /etc/rc7.d/S??$1 | head -1)
       exec $script $2
    fi

    echo "'$1' is not a valid service name" 1>&2
    exit 1
else
    exec /sbin/service "$@"
fi

Current SME10 alpha boot process and service control

Systemd has been designed as the way to handle services upstream. Systemd will override all /etc/rc.d/init.d/ scripts and call to /usr/sbin/service.

We have added a first systemd unit for bootstrap console and make it a drop in replacement of Sysvinit to boot all processes linked in /etc/rc.d/rc7.d/.

From there we could keep this way, or try to move as many process as we can to systemd. This way works, but is even more complex than it was on previous SME version. Plus we can not guarantee without further scripts that one will not be able to do a systemctl start httpd.

a new /sbin/e-smith/service

 1#! /bin/sh
 2# prevent initscript to use systemctl
 3export SYSTEMCTL_SKIP_REDIRECT=1
 4. /etc/rc.d/init.d/functions
 5
 6# what is our current runlevel
 7runlevel=$(systemctl get-default)
 8SERVICE=$1
 9USAGE="Usage: service SERVICENAME [ACTION]"
10
11#if no servicename is provided return usage
12if [[ "${SERVICE}" == "" ]]
13then
14   echo ${USAGE} >&2
15   exit
16fi
17
18if [ "$runlevel" = "multi-user.target" ]
19then
20    if ls /etc/rc7.d/S??$1 >/dev/null 2>/dev/null
21    then
22       script=$(ls /etc/rc7.d/S??$1 | head -1)
23       exec $script $2
24
25    elif ls /usr/lib/systemd/system/${SERVICE}.service >/dev/null 2>/dev/null || ls /etc/systemd/system/${SERVICE}.service >/dev/null 2>/dev/null
26    then
27	 if [[ "$2" == "" ]] ; then
28         echo "'$1' requires an action" 1>&2
29         echo ${USAGE} >&2
30         exit
31        elif  [[ $2 == "status" ]] ; then
32         exec /bin/systemctl status -n0 ${SERVICE}
33         exit
34        elif [[ $2 == "start" ]] ; then
35         echo -n  "Starting ${SERVICE}" 2>/dev/null
36        elif [[ $2 == "stop" ]] ; then
37         echo -n  "Stopping ${SERVICE}" 2>/dev/null
38        elif [[ $2 == "restart" ]] ; then
39         echo -n  "Restarting ${SERVICE}" 2>/dev/null
40        else
41         echo -n  "Sending $2 signal to ${SERVICE}" 2>/dev/null
42        fi
43        /bin/systemctl $2 ${SERVICE}.service> /dev/null
44        if [ $? -ne 0 ]; then
45         echo_failure
46        else
47         echo_success
48        fi
49        echo
50        exit
51    fi
52
53    echo "'$1' is not a valid service name" 1>&2
54    exit 1
55else
56    exec /sbin/service "$@"
57fi

And the current controlService perl function from /usr/share/perl5/vendor_perl/esmith/util.pm

  1=pod
  2
  3=head1 SERVICE MANAGEMENT UTILITIES
  4
  5=head2 serviceControl()
  6
  7Manage services - stop/start/restart/reload/graceful
  8
  9Returns 1 for success, 0 if something went wrong, fatal exception on bad
 10arguments.
 11
 12    serviceControl(
 13        NAME=>serviceName,
 14        ACTION=>start|stop|restart|reload|graceful
 15        [ BACKGROUND=>true|false (default is false) ]
 16    );
 17
 18EXAMPLE:
 19
 20    serviceControl( NAME=>'httpd-e-smith', ACTION=>'reload' );
 21
 22NOTES:
 23
 24The BACKGROUND parameter is optional and can be set to true if
 25start/stop/restart/etc. is to be done in the background (with
 26backgroundCommand()) rather than synchronously.
 27
 28CONVENTIONS:
 29
 30This command is the supported method for action scripts, blade handlers, etc.,
 31to start/stop/restart their services. Currently this is done via the rc7
 32symlinks, but this may change one day. Using this function gives us one
 33location to change this behaviour if desired, instead of hunting all over
 34every scrap of code. Please use it.
 35
 36=cut
 37
 38sub serviceControl
 39{
 40    my %params = @_;
 41
 42    my $serviceName = $params{NAME};
 43    unless ( defined $serviceName )
 44    {
 45        die "serviceControl: NAME must be specified";
 46    }
 47
 48    my $serviceAction = $params{ACTION};
 49    unless (defined $serviceAction)
 50    {
 51        die "serviceControl: ACTION must be specified";
 52    }
 53
 54    if ( $serviceAction =~ /^(start|stop|restart|reload|graceful|adjust|svdisable)$/ )
 55    {
 56        my ($startScript) = glob("/etc/rc.d/rc7.d/S*$serviceName") ||'' ;
 57        my ($systemdScript) = "/usr/lib/systemd/system/$serviceName.service" ||'';
 58
 59        unless ( -e $startScript or -e $systemdScript)
 60        {
 61            warn "serviceControl: startScript not found "
 62              . "for service $serviceName\n";
 63            return 0;
 64        }
 65
 66        if (-e $systemdScript and ! -e $startScript){
 67            if ($serviceAction =~/^(start|stop|restart|reload)$/) {
 68                system('/usr/bin/systemctl',"$serviceAction","$serviceName.service") == '0'
 69                ||    warn "serviceControl: Couldn't " .
 70                "system( /usr/bin/systemctl $serviceAction $serviceName.service): $!\n";
 71            }
 72            else {
 73                die "serviceControl: systemd doesn't know : systemctl $serviceAction $serviceName.service";
 74            }
 75        }
 76
 77        elsif (-e $startScript) {
 78            my  $background = $params{'BACKGROUND'} || 'false';
 79
 80            if ( $background eq 'true' )
 81            {
 82            backgroundCommand( 0, $startScript, $serviceAction );
 83            }
 84            elsif ( $background eq 'false' )
 85            {
 86                unless ( system( $startScript, $serviceAction ) == 0 )
 87                {
 88                warn "serviceControl: "
 89                  . "Couldn't system($startScript, $serviceAction): $!\n";
 90                return 0;
 91                }
 92           }
 93           else
 94           {
 95            die "serviceControl: Unsupported BACKGROUND=>$background";
 96           }
 97        }
 98    }
 99    else
100    {
101        die "serviceControl: Unknown serviceAction $serviceAction";
102    }
103    return 1;
104}

Systemd Integration possibilities

this is intended to make an open discussion about possibilities, in order to choose the best SME approach to systemd.

Issue to solve :

  • SME vs upstream
  • make unit files aware of service status in e-smith db configuration
  • have our services boot in the right sequence
  • journald vs rsyslogd
  • plain file log vs special file format
  • enabling /disabling /masking services

Table 1.  Load path when running in system mode (--system). [1]

Path Description
/etc/systemd/system.control Persistent and transient configuration created using the dbus API
/run/systemd/system.control
/run/systemd/transient Dynamic configuration for transient units
/run/systemd/generator.early Generated units with high priority (see early-dir in systemd.generator(7))
/etc/systemd/system System units created by the administrator
/run/systemd/system Runtime units
/run/systemd/generator Generated units with medium priority (see normal-dir in systemd.generator(7))
/usr/local/lib/systemd/system System units installed by the administrator
/usr/lib/systemd/system System units installed by the distribution package manager
/run/systemd/generator.late Generated units with low priority (see late-dir in systemd.generator(7))

generators

there have been suggestions about using a generator, it is not clear how and why it would help. This approach would be more complex. and according to the doc[2]:

  • Units written by generators are removed when the configuration is reloaded. That means the lifetime of the generated units is closely bound to the reload cycles of systemd itself.
  • Generators should only be used to generate unit files and symlinks to them, not any other kind of configuration. Due to the lifecycle logic mentioned above, generators are not a good fit to generate dynamic configuration for other services. If you need to generate dynamic configuration for other services, do so in normal services you order before the service in question.

systemctl daemon-reload has be run after each modification of a unit. Hence generated files by generators will be erase and generators rerun. [3]

Overide upstream vendor preset : templated unit files or not ? where ?

Thinking of the example of a service unit provided by upstream vendor. Should we simply create a template and expand it over the unit.service in /lib/systemd/system/ or offer a different way to override upstream vendor settings ? Further more should we template our file to point to /lib/ or to /etc/. In other words, should we fight with admin space trying to overwrite changes in its dedicated space or should we fight with upstream vendors and overwrite their files or find a way to override them.

hence the questions

  • where: /etc or /lib/
  • How : template or not
  • How: overwrite or selective overriding
unit files created in /etc/systemd/system/ or in /lib/systemd/system/

as a vendor we should work in /lib/systemd/system/, but as an admin helper we might want to play in /etc/systemd/system/ and rather offer a way to admin to use our template-custom or config db.

template or not

the first easy way to think to override ones service could be to simply template the file /lib/systemd/system/servicename.service and expand it every time a reconfiguration or a boot occurs.

Going futher we could ensure the stability of the system by making this in /etc/systemd/system/servicename.service

unitname.d/file.conf versus overwrite uniname.service

dot d folder can be created both int etc and lib from readings and inspection in a few systems.

There are two methods of overriding vendor settings in unit files: copying the unit file from /usr/lib/systemd/system to /etc/systemd/system and modifying the chosen settings. Alternatively, one can create a directory named unit.d/ within /etc/systemd/system and place a drop-in file name.conf there that only changes the specific settings one is interested in. Note that multiple such drop-in files are read if present, processed in lexicographic order of their filename.

The advantage of the first method is that one easily overrides the complete unit, the vendor unit is not parsed at all anymore. It has the disadvantage that improvements to the unit file by the vendor are not automatically incorporated on updates.

The advantage of the second method is that one only overrides the settings one specifically wants, where updates to the unit by the vendor automatically apply. This has the disadvantage that some future updates by the vendor might be incompatible with the local changes. [4]

uninstalling /masking unwanted/conflicting services: example of firewalld.

we can plan to uninstall firewalld for example, but some packages will reinstall as a requirement. It might even be started, which could conflict with masq.

References