pax_global_header00006660000000000000000000000064135542760250014523gustar00rootroot0000000000000052 comment=2d19ba739c7de79acaa482d8d98a0f49b98305ff docker-systemctl-replacement-1.4.3424/000077500000000000000000000000001355427602500175535ustar00rootroot00000000000000docker-systemctl-replacement-1.4.3424/.dockerignore000066400000000000000000000001541355427602500222270ustar00rootroot00000000000000tmp/*/*/*/*/*/*/*/* tmp/*/*/*/*/*/*/* tmp/*/*/*/*/*/* tmp/*/*/*/*/* tmp/*/*/*/* tmp/*/*/* tmp/*/* tmp/* tmp docker-systemctl-replacement-1.4.3424/.gitignore000066400000000000000000000001521355427602500215410ustar00rootroot00000000000000GNUmakefile tmp Software *.retry *.png initialAdminPassword tmp.* .coverage .coverage.* *,cover *.pyc *.e docker-systemctl-replacement-1.4.3424/ARCHITECTURE.md000066400000000000000000000315251355427602500217650ustar00rootroot00000000000000# ARCHITECTURE The implementation of the systemctl replacement script does not follow the model of systemd. Systemd was designed to work as a central daemon in the first place while the systemctl script does not use a daemon at all to provide its functionality. While the result may be very similar there are differences in detail. ## communication sockets The systemd's systemctl program is actually a dumb tool. It does mostly have no function at all other than looking for a communication channel to talk to the systemd daemon. You can ask the systemd daemon to open a TCP port but the default is to use a unix file socket on `/var/run/systemd/private`. So when you run a `systemctl start xy.service` then it will simply send a message on that socket to the systemd daemon which is running on PID-1 of your system (or the docker container created by systemd-nsspawn). Even when running the systemctl replacement script as PID-1 it does not open such a file socket, and none of the calls to `systemctl.py start xy.service` will try to find that kind of socket. Each instance will run on its own. This includes the requirement to open another file socket named `/var/run/systemd/notify` which is used for the services of `Type=notify`. The notify-services are essentially forking-services but instead of assuming that a service is state=active as soon as the xy-start.sh comes back the tool needs to wait till the service has sent a message that startup is complete. Apache is one prominent example of a notify-service. The systemctl replacement's notify-socket will only be created as soon as the systemctl replacment script runs a "start xy.service" and it is closed as soon as the startup-wait has completed. Now what if there are multiple `systemctl.py start` calls that run in parallel? Well, that would lead into problems - so there is some mutex locking needed. Only one systemctl script instance may be running a service-start at a time. This startup locking has been optimized meanwhile. For one thing there is not one notify-socket but each service gets its own. In systemctl script controlled environment you will find a `/var/run/systemd/notify.httpd.service` (or a similar thing in the user-directory for user services). All programs of Type=notify are supposed to watch for the environment variable `$NOTIFY_SOCKET` and while this will be always the same path in a systemd controlled environment it is a different one when a program is started through the systemctl replacement. It works. ## lock files The optimization of per-service notify-sockets has brought about the optimization that the mutex locking for systemctl script actions is not global anymore. In an early version of the systemctl replacement script only one systemctl.py instance was allowed to run but in later versions all the systemctl script instances may run in parallel as long as they try to start/stop a different xy.service. So just like with the notify-socket files there are multiple service-lock files around. They are created just next to notify-socket files, so you can see them popping up as `/var/run/systemd/httpd.service.lock`. There is nothing similar with the original systemd daemon as all actions are run by the single process on PID-1 which can do the serialisation through its own internal event loop. When the systemctl replacement script runs as the init-daemon on PID-1 it does not change its behaviour. It will look for the enabled services and for each of the services a lock-file is created on startup. As soon as the startup is complete they are unlocked. Actually it was a real-life observation when some xy.service was calling a "systemctl start foo.service" during its own startup phase, so the per-service locking is a definite requirement. As the service-locking is somewhat granular, a lot of actions of the systemctl script do not run guarded by an active lock. Obviously `systemctl cat xy.service` does not need a lock at all. At the moment not even `systemctl status xy.service` will ask for a mutex locking although that could theoretically lead into problems when a pid-file is removed midway of the status detection. So far it just works fine. ## optional daemon-reload So far you should have understood that all instances of the systemctl script run independently and the behaviour does not change when the systemctl replacement script is the init-daemon on PID-1. When the systemctl script on PID-1 has started all the services, it will basically sit silent with doing no more actions other than reaping zombies reassigned by the unix kernel. (And waiting for a SIGTERM to go for the shutdown process but that's another topic). For the systemd daemon however the one instance on PID-1 is all there is. So when you make any changes to the xy.service files on disk then it will not have any effect on the next start/stop. You need to initiate some `systemctl daemon-reload` to ask the PID-1 process to scan for changes to the service files. (There is a debug mode where the systemd daemon will watch for that automatically but let's disregard that here as it is not normally enabled). The systemctl replacement script however will scan the service descriptors on each execution. Actually it will not only scan the `xy.service` descriptor file for the service that you like to start/stop but it will always scan all the descriptor files. That's because any file may declare a requirement for another service, especially in the `Requires=`, the `After=` and the `Conflicts=` rules where the latter will say that you need to stop another service before you are allowed to start the current service. Now `Conflicts` is not implemented at the time of writing so that one could optimize to scan only one descriptor file. But the saved time is not worth it. The scanning of the service files is quick enough that you won't even notice that it took a few milliseconds more than systemd systemctl would need for an action. The only thing is that a syntax error in any service descriptor on the disk will result in a warning message flashing by on every call to the systemctl replacement script. The actions on the correctly declared services will not be hampered however. ## overwriting /usr/bin/systemctl The systemctl replacement script is generally shipped as the python implemention of it, named "systemctl.py". You can use the non-installed script right away which is no different than the usage of the installed bin/systemctl replacement. So `systemctl.py start xy.service` and a bin-path replacement `systemctl start xy.service` do work exactly the same. While an installation as /usr/bin/systemctl.py would work fine that is not a good choice however. The reason for that is that other programs will only know about running the `systemctl` tool as the tool is named in the systemd world. That's strictly the case for tools like Ansible/Puppet which will check the target system running "systemctl" commands. You can not tell them to do it different just because of the systemctl.py script invention. There are however occasions where you do run the the systemctl replacement script actions without having it installed in the system as /usr/bin/systemctl. During the development it is even the normal case in the testsuite. Remember that every systemctl script instance runs independently, so it does not matter if one uses the version installed in the /usr/bin directory or the version that comes directly from the git workspace. You can run the systemctl script actions even in a system that is currently controlled by a systemd daemon. That allows you to inspect service descriptor files without running a "system-reload" on the system's systemd daemon. Starting/stopping is actually possible as well in parallel with systemd but I would not recommend that. And that's due to the way status information is stored. ## status files In a systemd controlled environment all status information is kept in memory. The systemd init daemon will start a service program, it will possibly wait for a notify-message, and it can see when a child process dies so that it can be restarted. Essentially, all service programs are child processes of the systemd daemon (disregarding the fact here that service programs may detach but when the systemd daemon is on PID-1 then they will be reattached to it right away). In a systemctl script controlled environment all status information is kept on disk. When the systemctl replacement script starts a service program then it will run the Exec-steps, it will possibly wait for a notify-message, and then it will exit. So the started program will implicitly run unattached. No systemctl script will be on the process table anymore in a docker container (it used to be different in the past - and in some occasions where the execve() has replaced the original process you can see the 'systemctl start' command in the process table but the underlying binary is actually the application). Detecting the 'is-active' status of a service works somewhat different for systemd and the systemctl script. It is only the same when a `PIDFile=` has been declared because both the systemd daemon and the systemctl replacement will read the PID from that file and they will check if the PID is active. A dead process may result in either an "inactive" or "failed" service status depending on whether the service was started or stopped. In the very first versions of the systemctl replacement the script was inventing pid-files when the service descriptor was not declaring one. Especially a `Type=simple` service does not need a PIDFile declaration as it is supposed to run attached as child process of the systemd daemon. With the systemctl replacement however a pid-file was written on `systemctl start xy` and the next call to `systemctl status xy` will look for the invented pid-file, read the PID and check on the status. So here are much more pid-files around in a systemctl script controlled environment than in systemd daemon controlled one. In later versions of the systemctl replacement script (including the current one at the time of writing) that has changed. That's because a `RemainActiveAfterExit` and other modes require a more detailed knowledge of whether a service was supposed to be running, and that a dead process is not problem (it is rendered 'active' on RemainActive). So the systemctl replacement script needs more information bits than just the PID of the started process. Because of that there are `/var/run/xy.service.status` files around. They will almost always contain the PID. But is just a standard json file so it can contain more information bits about a service. When checking a service through the systemctl replacement then it will look for those files. Because the systemd daemon does not write those files a systemctl replacement script call may return a very different status when poking at a service started through it. In a docker container it is usually the case that all services are started through the systemctl replacement script (either from the command line or through the one on PID-1 on docker container start). That allows the status info returned to be the same as in a systemd controlled environment. There are however subtle differences - mostly that's just room for improvement to align the way how a "failed" service is called out. The "active" state is always reported correctly however. That's because deployment tools like Ansible/Puppet will stop execution when not - but these tools do not care much if a service in an unexpected stopped status through a mark as "failed" or "inactive". Whatever. ## boot time check Because of the importance of the status files on disk there is a situation that you may have saved a container to an image with some status files being still around. When you do start a container from the image then the status files will refere to processes from an earlier incarnation. While a PID rollover is only a theoretic threat in a standard unix system it is the normal case for a such a docker container - it is very probable that an old status file remembers a PID that can be detected as running ... but it may be a completely different application binary from a whole different command. Therefore the systemctl replacement script implements a check on the boot time of container (or a real system) by checking for some files in /proc with hopefully /proc/1 to be around. If any status file has a timestamp that is older than the timestamp of the boot PID then these status files are truncated to zero. Effectivly, any old PID that was saved in an image will be disregarded in a new container. Surely, if there are subtle problems with the system clock then you will bump into mysterious problems on service starts through the systemctl replacement script. And some docker versions did really weird things to /proc/1 for unknown reasons - it came back to normal on the next version of docker. It is just that the docker guys don't expect a tool like the systemctl replacement script to be around which is very picky on the timestamps. docker-systemctl-replacement-1.4.3424/BUGS.md000066400000000000000000000034471355427602500206450ustar00rootroot00000000000000# Known Bugs and Peculiar Features ## Firefox Firefox wants to connect to the DBUS. Instead of skipping a missing service gracefully it just stops with a reference to "/etc/machine-id" to not contain a 32-chars ID. Firefox will however start if any such number is written. echo "012345678901234567890123456789012" > /etc/machine-id Theoretically it should be available through docker but that is not always the case, somehow. ## Ansible service module Calling Ansible "service" with "enabled: yes" will fail with "no service or tool found for: my.service" Although "my.service" is mentioned it really thinks that the tool /usr/bin/systemctl does not exist - altough it does. The reason is an additional sanity check whether systemd is really managing the system - and it wants to fallback to "chkconfig" otherwise. The test (seen in Ansible 2.1) can be defeated by mkdir /run/systemd/system # or /dev/.run/systemd or /dev/.systemd Sadly it must be done before calling ansible's "service" module (and indrectly the systemctl replacement) so there is no help to make this just on a temporary basis. If you have installed the "initscripts" package then it will work without such a workaround because chkconfig exists and handles the "enabled" thing correctly (but only for the existing SysV style init.d services). ## Restart=on-failure Because the systemctl replacement script is not a daemon it will not watch over the started applications. As such any option in a service unit file like "Restart=on-failure" is disregarded. As a designer of a docker application container one should take that as a warning - the process is buggy and it may break. And so will your containered service. If you need a fallback solutation then the container clould application should monitor the docker container. docker-systemctl-replacement-1.4.3424/EUPL-LICENSE.md000066400000000000000000000333101355427602500217220ustar00rootroot00000000000000## EUROPEAN UNION PUBLIC LICENCE v. 1.2 EUPL (C) the European Union 2007, 2016 This European Union Public Licence (the EUPL) applies to the Work (as defined below) which is provided under the terms of this Licence. Any use of the Work, other than as authorised under this Licence is prohibited (to the extent such use is covered by a right of the copyright holder of the Work). The Original Work is provided under the terms of this Licence when the Licensor (as defined below) has placed the following notice immediately following the copyright notice for the Original Work: Licensed under the EUPL or has expressed by any other means his willingness to license under the EUPL. ### 1.Definitions In this Licence, the following terms have the following meaning: - 'The Licence': this Licence. - 'The Original Work': the work or software distributed or communicated by the Licensor under this Licence, available as Source Code and also as Executable Code as the case may be. - 'Derivative Works': the works or software that could be created by the Licensee, based upon the Original Work or modifications thereof. This Licence does not define the extent of modification or dependence on the Original Work required in order to classify a work as a Derivative Work; this extent is determined by copyright law applicable in the country mentioned in Article 15. - 'The Work': the Original Work or its Derivative Works. - 'The Source Code': the human-readable form of the Work which is the most convenient for people to study and modify. - 'The Executable Code': any code which has generally been compiled and which is meant to be interpreted by a computer as a program. - 'The Licensor': the natural or legal person that distributes or communicates the Work under the Licence. - 'Contributor(s)': any natural or legal person who modifies the Work under the Licence, or otherwise contributes to the creation of a Derivative Work. - 'The Licensee or You': any natural or legal person who makes any usage of the Work under the terms of the Licence. - 'Distribution or Communication': any act of selling, giving, lending, renting, distributing, communicating, transmitting, or otherwise making available, online or offline, copies of the Work or providing access to its essential functionalities at the disposal of any other natural or legal person. ### 2. Scope of the rights granted by the Licence The Licensor hereby grants You a worldwide, royalty-free, non-exclusive, sublicensable licence to do the following, for the duration of copyright vested in the Original Work: - use the Work in any circumstance and for all usage, - reproduce the Work, - modify the Work, and make Derivative Works based upon the Work, - communicate to the public, including the right to make available or display the Work or copies thereof to the public and perform publicly, as the case may be, the Work, - distribute the Work or copies thereof, - lend and rent the Work or copies thereof, - sublicense rights in the Work or copies thereof. Those rights can be exercised on any media, supports and formats, whether now known or later invented, as far as the applicable law permits so. In the countries where moral rights apply, the Licensor waives his right to exercise his moral right to the extent allowed by law in order to make effective the licence of the economic rights here above listed. The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to any patents held by the Licensor, to the extent necessary to make use of the rights granted on the Work under this Licence. ### 3. Communication of the Source Code The Licensor may provide the Work either in its Source Code form, or as Executable Code. If the Work is provided as Executable Code, the Licensor provides in addition a machine-readable copy of the Source Code of the Work along with each copy of the Work that the Licensor distributes or indicates, in a notice following the copyright notice attached to the Work, a repository where the Source Code is easily and freely accessible for as long as the Licensor continues to distribute or communicate the Work. ### 4. Limitations on copyright Nothing in this Licence is intended to deprive the Licensee of the benefits from any exception or limitation to the exclusive rights of the rights owners in the Work, of the exhaustion of those rights or of other applicable limitations thereto. ### 5. Obligations of the Licensee The grant of the rights mentioned above is subject to some restrictions and obligations imposed on the Licensee. Those obligations are the following: **Attribution right**: The Licensee shall keep intact all copyright, patent or trademarks notices and all notices that refer to the Licence and to the disclaimer of warranties. The Licensee must include a copy of such notices and a copy of the Licence with every copy of the Work he/she distributes or communicates. The Licensee must cause any Derivative Work to carry prominent notices stating that the Work has been modified and the date of modification. **Copyleft clause**: If the Licensee distributes or communicates copies of the Original Works or Derivative Works, this Distribution or Communication will be done under the terms of this Licence or of a later version of this Licence unless the Original Work is expressly distributed only under this version of the Licence for example by communicating EUPL v. 1.2 only. The Licensee (becoming Licensor) cannot offer or impose any additional terms or conditions on the Work or Derivative Work that alter or restrict the terms of the Licence. **Compatibility clause**: If the Licensee Distributes or Communicates Derivative Works or copies thereof based upon both the Work and another work licensed under a Compatible Licence, this Distribution or Communication can be done under the terms of this Compatible Licence. For the sake of this clause, Compatible Licence refers to the licences listed in the appendix attached to this Licence. Should the Licensee's obligations under the Compatible Licence conflict with his/her obligations under this Licence, the obligations of the Compatible Licence shall prevail. **Provision of Source Code**: When distributing or communicating copies of the Work, the Licensee will provide a machine-readable copy of the Source Code or indicate a repository where this Source will be easily and freely available for as long as the Licensee continues to distribute or communicate the Work. **Legal Protection**: This Licence does not grant permission to use the trade names, trademarks, service marks, or names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the copyright notice. ### 6. Chain of Authorship The original Licensor warrants that the copyright in the Original Work granted hereunder is owned by him/her or licensed to him/her and that he/she has the power and authority to grant the Licence. Each Contributor warrants that the copyright in the modifications he/she brings to the Work are owned by him/her or licensed to him/her and that he/she has the power and authority to grant the Licence. Each time You accept the Licence, the original Licensor and subsequent Contributors grant You a licence to their contributions to the Work, under the terms of this Licence. ### 7.Disclaimer of Warranty The Work is a work in progress, which is continuously improved by numerous Contributors. It is not a finished work and may therefore contain defects or bugs inherent to this type of development. For the above reason, the Work is provided under the Licence on an as is basis and without warranties of any kind concerning the Work, including without limitation merchantability, fitness for a particular purpose, absence of defects or errors, accuracy, non-infringement of intellectual property rights other than copyright as stated in Article 6 of this Licence. This disclaimer of warranty is an essential part of the Licence and a condition for the grant of any rights to the Work. ### 8. Disclaimer of Liability Except in the cases of wilful misconduct or damages directly caused to natural persons, the Licensor will in no event be liable for any direct or indirect, material or moral, damages of any kind, arising out of the Licence or of the use of the Work, including without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, loss of data or any commercial damage, even if the Licensor has been advised of the possibility of such damage. However, the Licensor will be liable under statutory product liability laws as far such laws apply to the Work. ### 9. Additional agreements While distributing the Work, You may choose to conclude an additional agreement, defining obligations or services consistent with this Licence. However, if accepting obligations, You may act only on your own behalf and on your sole responsibility, not on behalf of the original Licensor or any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against such Contributor by the fact You have accepted any warranty or additional liability. ### 10. Acceptance of the Licence The provisions of this Licence can be accepted by clicking on an icon I agree placed under the bottom of a window displaying the text of this Licence or by affirming consent in any other similar way, in accordance with the rules of applicable law. Clicking on that icon indicates your clear and irrevocable acceptance of this Licence and all of its terms and conditions. Similarly, you irrevocably accept this Licence and all of its terms and conditions by exercising any rights granted to You by Article 2 of this Licence, such as the use of the Work, the creation by You of a Derivative Work or the Distribution or Communication by You of the Work or copies thereof. ### 11. Information to the public In case of any Distribution or Communication of the Work by means of electronic communication by You (for example, by offering to download the Work from a remote location) the distribution channel or media (for example, a website) must at least provide to the public the information requested by the applicable law regarding the Licensor, the Licence and the way it may be accessible, concluded, stored and reproduced by the Licensee. ### 12. Termination of the Licence The Licence and the rights granted hereunder will terminate automatically upon any breach by the Licensee of the terms of the Licence. Such a termination will not terminate the licences of any person who has received the Work from the Licensee under the Licence, provided such persons remain in full compliance with the Licence. ### 13. Miscellaneous Without prejudice of Article 9 above, the Licence represents the complete agreement between the Parties as to the Work. If any provision of the Licence is invalid or unenforceable under applicable law, this will not affect the validity or enforceability of the Licence as a whole. Such provision will be construed or reformed so as necessary to make it valid and enforceable. The European Commission may publish other linguistic versions or new versions of this Licence or updated versions of the Appendix, so far this is required and reasonable, without reducing the scope of the rights granted by the Licence. New versions of the Licence will be published with a unique version number. All linguistic versions of this Licence, approved by the European Commission, have identical value. Parties can take advantage of the linguistic version of their choice. ### 14. Jurisdiction Without prejudice to specific agreement between parties, - any litigation resulting from the interpretation of this License, arising between the European Union institutions, bodies, offices or agencies, as a Licensor, and any Licensee, will be subject to the jurisdiction of the Court of Justice of the European Union, as laid down in article 272 of the Treaty on the Functioning of the European Union, - any litigation arising between other parties and resulting from the interpretation of this License, will be subject to the exclusive jurisdiction of the competent court where the Licensor resides or conducts its primary business. ### 15. Applicable Law Without prejudice to specific agreement between parties, - this Licence shall be governed by the law of the European Union Member State where the Licensor has his seat, resides or has his registered office, - this licence shall be governed by Belgian law if the Licensor has no seat, residence or registered office inside a European Union Member State. ## Appendix Compatible Licences according to Article 5 EUPL are: - GNU General Public License (GPL) v. 2, v. 3 - GNU Affero General Public License (AGPL) v. 3 - Open Software License (OSL) v. 2.1, v. 3.0 - Eclipse Public License (EPL) v. 1.0 - CeCILL v. 2.0, v. 2.1 - Mozilla Public Licence (MPL) v. 2 - GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3 - Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for works other than software - European Union Public Licence (EUPL) v. 1.1, v. 1.2 - Qubec Free and Open-Source Licence Reciprocity (LiLiQ-R) or Strong Reciprocity (LiLiQ-R+). The European Commission may update this Appendix to later versions of the above licences without producing a new version of the EUPL, as long as they provide the rights granted in Article 2 of this Licence and protect the covered Source Code from exclusive appropriation. All other changes or additions to this Appendix require the production of a new EUPL version. docker-systemctl-replacement-1.4.3424/EXTRA-CONFIGS.md000066400000000000000000000116151355427602500220520ustar00rootroot00000000000000# Extra Configs and Features ## DROP-IN FILES The systemd service descriptors do already have the feature of drop-in configurations. So when there is a docker container with these files: /usr/lib/systemd/system/some.service /usr/lib/systemd/system/some.service.d/extra.conf /etc/systemd/system/some.service.d/override.conf then ALL these files will be parsed. Therefore some `*.rpm` program may install the `some.service` file but you can always override the settings with your `override.conf`. In general the new lines will be added to the existing config entries. So when you have # some.service [Service] Environment=A=1 B=2 ExecStartPre=/usr/bin/somecheck # some.service.d/extra.conf [Service] Environment=C=2 ExecStartPre=/usr/bin/morechecks then both of these commands are being executed and the environment contains A, B and C variables for all commands. There is a feature where you can also replace the original settings completely by giving an option with an empty argument: # some.service [Service] Environment=A=1 B=2 ExecStart=/usr/bin/startup $A $B # some.service.d/override.conf [Service] Environment=C=2 ExecStart= ExecStart=/usr/bin/startup $A $B $C In the latter case, only one ExecStart command will be executed having three arguments. All these are standard features of systemd. In general they are meant to be used by configuration programs that can add or change some environment variables materializing the extra settings as an extra.conf instead of changing the original service descriptor file. However the way to append/replace a setting is completely generic - it is part of the parser and not of the interpreter for the service definitions. Thus it works also for the Exec-parts. ## STANDARD DEFINITION https://www.freedesktop.org/software/systemd/man/systemd.unit.html > Along with a unit file `foo.service`, a "drop-in" directory > `foo.service.d/` may exist. All files with the suffix `".conf"` > from this directory will be parsed after the unit file itself > is parsed. This is useful to alter or add configuration settings > for a unit, without having to modify unit files. Drop-in files > must contain appropriate section headers. For instantiated units, > this logic will first look for the instance `".d/"` subdirectory > (e.g. `"foo@bar.service.d/"`) and read its `".conf"` files, > followed by the template `".d/"` subdirectory (e.g. > `"foo@.service.d/"`) and the `".conf"` files there. Moreover for > units names containing dashes (`"-"`), the set of directories > generated by truncating the unit name after all dashes is searched > too. Specifically, for a unit name `foo-bar-baz.service` not only > the regular drop-in directory `foo-bar-baz.service.d/` is searched > but also both `foo-bar-.service.d/` and `foo-.service.d/`. This is > useful for defining common drop-ins for a set of related units, > whose names begin with a common prefix. This scheme is particularly > useful for mount, automount and slice units, whose systematic > naming structure is built around dashes as component separators. > Note that equally named drop-in files further down the prefix > hierarchy override those further up, i.e. > `foo-bar-.service.d/10-override.conf` overrides > `foo-.service.d/10-override.conf`. > In addition to `/etc/systemd/system`, the drop-in `".d/"` directories > for system services can be placed in `/usr/lib/systemd/system` or > `/run/systemd/system` directories. Drop-in files in `/etc` take > precedence over those in `/run` which in turn take precedence over > those in `/usr/lib`. Drop-in files under any of these directories > take precedence over unit files wherever located. Multiple drop-in > files with different names are applied in lexicographic order, > regardless of which of the directories they reside in. Note that the last paragraph is a bit misleading as "predence" is defined for `*.conf` files of the same name. An `"extra.conf"` in the `/etc` area will mask an `"extra.conf"` in `/usr/lib`. Otherwise the order of conf-files is just lexicographic irrespective of the location, so that administrators will often choose to prefix the conf-files by number to ensure proper inclusion order, i.e. using some `"01-extra.conf"` to `"99-override.conf"` file names. As for the systemctl replacement script the feature of a shortened `foo-.service.d` is not implemented. (The systemctl script does also ignore many of the standard definitions about template service files). Therefore overrides must be specific for each service and not a group of them. ## EXAMPLE /etc/systemd/system/some.service /etc/systemd/system/some.service.d/extra.conf /etc/systemd/system/some.service.d/zen.conf /usr/lib/system/some.service.d/addon.conf /usr/lib/system/some.service.d/override.conf The parsing order is this some.service addon.conf extra.conf override.conf zen.conf docker-systemctl-replacement-1.4.3424/HISTORY.md000066400000000000000000000230551355427602500212430ustar00rootroot00000000000000# HISTORY ## ExecStart Wrapper In the beginning there was no systemctl replacement idea. Instead of that there had been an observation that people did create docker-entrypoint.sh scripts that were wrong - and if they are correct then they did look very similar to the lines written in the ExecStart and ExecStop lines from the systemd service descriptor from the upstream software vendor. Have a look at "grep Exec /usr/lib/systemd/system/postfix.service" ExecStart=/usr/sbin/postfix start ExecReload=/usr/sbin/postfix reload ExecStop=/usr/sbin/postfix stop In fact, many software vendors were shipping with systemd service descriptors at the time that were simply relaying start/stop to the scripts also called from the sysv init.d script. That way one could easily support non-systemd and systemd controlled target environments. So back in late 2015 and early 2016 there was a generic docker-entrypoint shell script that was grepping for ExecStart in a given systemd x.service script. This had two theoretical advantages: * changes in the ExecStart from the upstream software vendor would automatically be picked up by every rebuild of a docker image. * it was possible to intercept the SIGTERM signal from "docker stop" to also run the ExecStop sequence from the same given systemd x.service This worked good enough for Type=forking services with very simple ExecStart/ExecStop definitions. It did mark the start because instead of giving the full path to the systemd service descriptor file one could just let the script search the usual locations itself. Simply do "docker-start.sh postfix.service". Supporting Type=simple is also possible that way as it does basically just boil down to check for ExecStart and to run a final shell "exec $ExecStart" to have a "docker stop" signal be delivered directly to the process. Almost too simple. ## Unit Parser It became soon apparent that the docker-entrypoint wrapper script does only work for very simple systemd service descriptors. Especially one could see EnvironmentFile parts to be required to start up a service correctly, and it would be nice to support also definitions for doing an ExecReload. Also the changes from upstream vendors were not so simple as expected, so a Python script was born to analyze the structure of the systemd service descriptor. In the beginning that was just wrapping Python's ConfigParser to read the ini-style descriptor files. However be aware that a lot of service descriptor files may fail to load with that. After all, a "UnitConfig" parser was started to get the format correctly. It is still at the heart of the current systemctl replacement script - completely rewritten of course because the service descriptors are not really `*.ini` files. If you ever want to analyze systemd files then you better take that as an example how to do it. The rest is simple - at some point in May 2016 a systemctl.py script was born that did understand start/stop/reload commands. It was able to source EnvironmentFile parts and to run services of Type=simple and Type=forking by running the ExecStart=parts through calling a subprocess shell. (yes, a shell!) By June 2016 an optionparser was added that would allow to get all commands of that systemctl could handle. Most of them were not implemented however - they were just in the "systemctl help" list. For your reference that script is the first version in the github tree - just note that it did already have the invention to generate a pid-file when no explicit one was given so that `is_active_from` can tell the correct status. https://github.com/gdraheim/docker-systemctl-replacement/commit/883d7e2022fe81d1dcdf9b5ead9215eb1167bd5c ## Ansible Helper The reason for the summer project came from using Ansible to provision docker containers. There have been scripts around that were already able to deploy to the labs virtual machines. In some parts they were deploying a systemd service descriptor and the following tasks would "enable" the service for the next reboot. Using docker instead of virtual machines would simply lower the amount of resources needed to test deployment scripts. Having a systemctl-replacement script around makes the development turnaround times so much faster. And for a start you do really only need to implement enable/disable/status in addition to start/stop/reload. When that is ready, just overwrite /usr/bin/systemctl in the container and you can use the earlier Ansible scripts to work on them as if they are virtual machines. It took however to early 2017 that a project came around where one would not only target one container - but the provision scripts were switching back and forth to get the interconnections correctly configured. Using some systemctl replacement did require it to work exactly like systemd - no failure in restarting a service was allowed anymore. So essentially, the first script was around for a year that the next iterations came along. And a lot of commits happened after that time. Note how one of the first versions starts to implement `show_unit` because that is how Ansible reads the systemd descriptor files as well as checking the current `active` status from the service in the container. https://github.com/gdraheim/docker-systemctl-replacement/commit/b985e110946316d7e19258436cac1ea25a21c259 ## Reap Zombies The next mark comes from implementing "systemctl default" to start all enabled services in a container after restart. In a way that combines the original idea for a startup wrapper serving as the docker-entrypoint with the start/stop commands for single services. One of the biggest inovations at that point was to also check the zombie processes around. Because the Ansible deployments did stop services quite a number of times leaving the killed processes around. That called for a zombie reaper to be implemented - something that /sbin/init would do normally but there is no such thing in a docker container. Having that in the code, the basic structure of the project was ready - with the systemctl-replacement-script to be really a service manager for docker containers that want to be almost like virtual machines. https://github.com/gdraheim/docker-systemctl-replacement/commit/3515f94bb6d1fdbede68b560ea47b403bc081dbd ## First Release As there are three stages above, the first real version number for the published code was set at `0.3`. As the systemctl replacement project became an independent project from the Ansible code some finer versioning was needed with 0.3.1124 being the first real github version number. https://github.com/gdraheim/docker-systemctl-replacement/commit/d7bbdd13b86620a9e1eee70522862a868b9053fb However that one was not officially tagged as a release. The first release happend on the next day at version 0.4.1125. Note how the versioning scheme works - the four digits at the end represent a date value, while the two numbers in front are incremented manually. The date value is (Years from BASEYEAR, week number of the year, day number of the week) and it is updated by `make version`. The README.md created for this first official release is basically the same as the one two years later. https://github.com/gdraheim/docker-systemctl-replacement/releases/tag/v0.4.1125 ## Testsuite In the first release only a "Maketests" file did exist, covering as much as three Dockerfiles. As it was inconvenient to switch back and forth to the other project, some Ansible code was copied over for testing in release 0.5. With more and more dockerfiles, that became the testsuite for quite a while. It took to release 0.7 that a testsuite.py file exists but that was only a wrapper to run the old dockerfile builds and the old ansible scripts. Basically, the old test cases did get a number! Using experiences from another project, the unittest.main loop is not used but a different cli where one can run a test case by the prefix of its method name - for example `testsuite.py test_6001`. The old file "Maketests" was replaced by that. For release 0.8 the systemctl script was enhanced with an extra option "--root=path" to be able to test functionality without starting a docker container (something that was required before). For release 0.9 the testsuite.py was enhanced to use "coverage2" when running sytemctl.py so that one could know how many testcases were still needed. This was done with the intention that an upcoming release 1.0 should only be published with good coverage. In fact, the dot-zero release had over 90% line coverage already. ## Bugs and Features With release 1.0 to also know about reordering of services, the systemctl.py replacement script was about feature complete. After that time the changes are tracked using the GitHub issue list as well as mentioning the changes in the release notes attached to the git tags / github releases. Be aware that the systemctl replacement does not want to mimic the systemd behaviour in all detail. There had been always the expectation that at some point the docker cluster managers will be able to run containers with some systemd emulation themselves (atleast rkt does). So essentially, the systemctl.py script would become obsolete soon - however, years later the situation has not changed much and the script is even used for other purposes by now. So let's close this history wrapup with a final remark: most Linux distributions are run by systemd at the time of writing this text. But systemd is a flawed solution - not only because it breaks the Unix Philosophy of "Do One Thing and Do It Well". There are many quirks in the service descriptors and the whole functionality is badly documented - we're all just lucky that it works at all. SystemD is a bug itself. docker-systemctl-replacement-1.4.3424/INIT-DAEMON.md000066400000000000000000000132551355427602500216070ustar00rootroot00000000000000# INIT-DAEMON By tradition a docker container is an enhanced version of a chroot environment. Through remapping Process IDs it does however contain a new PID-1 where an init daemon would live. ## Problems with PID 1 in Docker The command listed as "CMD" in the docker image properties or being given as docker-run argument will become the PID-1 of a new container. Actually it takes the place that would traditionally be used by the /sbin/init process which has a special functionality in unix'ish operating systems. For one part, it is there to start up the system to a specific runlevel. What is known as runlevel "5" in SysV is the "multi-user.target" in SystemD. When the system is halted it will send a stop to other processes giving them a chance to shutdown cleanly. The docker-stop command will send a SIGTERM to PID-1 in the container - but NOT to any other process. If the CMD is the actual application (java -jar whatever) then this works fine as it will also clean up its subprocesses. If you need multiple processes then most developers tend to write a shell script ("run.sh") to start them up but they forget to intercept the SIGTERM in the shell script including a handler to shutdown started subprocesses. After a grace period (of 10 seconds by default) the docker-stop will turn to send SIGKILL to all remaining processes in the container. So the application data is not ensured to be clean at that point. Another part of PID 1 is that it will adopt all background processes where the parent has already died (or where the process has explicitly "disown"ed the subprocess). This is the case for many `*.service` definitions with a type of "forking". In general these have use a PIDfile to know how to send a SIGTERM signal to the background process. But without an init-process at PID-1 this will create zombie processes in the docker container that will continue to exist forever. As such one would need to implement a so-called "zombie reaper" functionality for the process at PID-1. This is not easy to be done in a plain shell script. But there are number of implementations available already that can do it. And this script will also serve as a docker-init-replacement. ## The docker-init-replacement When a "docker-stop" sends a SIGTERM to PID-1 then it may expect that it indirectly runs "systemctl halt" to shut down the running services. The docker-systemctl-replacement does implement that function - so where "systemctl default" will run a "systemctl start" on all "is-enabled" services, the inverse "system halt" command will run a "systemctl stop" on all those services. Of course it would be possible to write a second script to implement the docker-init-replacement functionality but it is integrated here. Just run the systemctl replacemnt as the PID-1 process and it will implicitly call its functionality of "systemctl -1 default", and upon receiving a SIGTERM from docker-stop it will run its "systemctl halt" implementation. Here "default" is the standard command to start all services in the multi-user target. The new option "--init" (or the command "init") will keep the script running as a zombie reaper. (NOTE: if it is not PID-1 then it defaults "systemctl list-units"). As a hint: the SystemD "systemctl enable" command will read the "WantedBy" of the referenced `*.service` script. For all the targets listed in there it will create a symlink - for the common "multi-user.target" this will be in /etc/systemd/system/multi-user.target.wants/. The docker-systemctl-replacement knows about that and it is used. And as another hint: if you run the non-installed script as "systemctl.py init" then pressing Control-C will result in an interpretation of "systemctl halt" as well, so one can test the correct interpretion of the "wants". ## Installation as an init-replacement As for the systemctl replacement it is best to overwrite the original /usr/bin/systemctl because a number of tools call that indirectly to start/stop services. This is very true for Ansible's 'service' module. For an init-replacement it may be placed anywhere in the docker image, let's say it is /usr/bin/systemctl.py. It will be activated by listing that path in the CMD element of a Dockerfile. However this is rarely used on mostly just to have bin-alternative setup. If you do not use a Dockerfile (for example using an Ansible script for deployment to a docker container) then you can add the CMD or ENTRYPOINT attribute upon the next "docker commit" like this: docker commit -c "CMD ['/usr/bin/systemctl']" \ -m "" So if the systemctl replacement script is being installed as /usr/bin/systemctl anyway then you just reference it like that. If only a specific set of services shall be run then one can exchange the "default" command with an explicit start list (and be sure to activate the continued execution as an init process): /usr/bin/systemctl init mongodb myapp (equivalent with `systemctl start --init mongodb myapp`) ## Remember the stop grace timeout Note that the docker daemon will send a SIGTERM to the PID 1 of a docker container that will result in the stop-behaviour of the "systemctl init" loop. However the docker daemon will only wait 10 seconds by default and when the container has not stopped completely it will send a SIGKILL to all the remaining processes in the container. For system services those 10 seconds are way too short. Be sure to increase that to atleast 100 seconds to allow the normal service scripts to bring down the applications completely. How to increase the timeout depends on the tool however, for example: docker stop --time 100 running docker run --stop-timeout 100 --name running image docker-compose.yml: stop_grace_period: 100 docker-systemctl-replacement-1.4.3424/Makefile000066400000000000000000000132451355427602500212200ustar00rootroot00000000000000F= files/docker/systemctl.py B= 2016 version1: @ grep -l __version__ */*.??* */*/*.??* | { while read f; do echo $$f; done; } version: @ grep -l __version__ */*.??* */*/*.??* *.py | { while read f; do : \ ; Y=`date +%Y` ; X=$$(expr $$Y - $B); D=`date +%W%u` ; sed -i \ -e "/^ *__version__/s/[.]-*[0123456789][0123456789][0123456789]*\"/.$$X$$D\"/" \ -e "/^ *__version__/s/[.]\\([0123456789]\\)\"/.\\1.$$X$$D\"/" \ -e "/^ *__copyright__/s/(C) [0123456789]*-[0123456789]*/(C) $B-$$Y/" \ -e "/^ *__copyright__/s/(C) [0123456789]* /(C) $$Y /" \ $$f; done; } @ grep ^__version__ files/*/*.??* help: python files/docker/systemctl.py help 3: cp -v files/docker/systemctl.py files/docker/systemctl3.py sed -i -e "s|/usr/bin/python|/usr/bin/python3|" files/docker/systemctl3.py diff -U1 files/docker/systemctl.py files/docker/systemctl3.py || true alltests: CH CP UA DJ .PHONY: tests tests: alltests CH centos-httpd.dockerfile: ; ./testsuite.py test_6001 CP centos-postgres.dockerfile: ; ./testsuite.py test_6002 UA ubuntu-apache2.dockerfile: ; ./testsuite.py test_6005 DJ docker-jenkins: ; ./testsuite.py test_900* COVERAGE=--coverage est_%: ; rm .coverage* ; ./testsuite.py "t$@" -vv --coverage test_%: ; ./testsuite.py "$@" -vv real_%: ; ./testsuite.py "$@" -vv test: ; $(MAKE) "test_[1234]" st_%: ; $(MAKE) 3 && ./testsuite.py "te$@" -vv $(WITH3) WITH3 = --python=/usr/bin/python3 --with=files/docker/systemctl3.py todo/test%: ; ./testsuite.py "$(notdir $@)" -vv --todo 15.0/test%: ; ./testsuite.py "$(notdir $@)" -vv --image=opensuse/leap:15.0 42.3/test%: ; ./testsuite.py "$(notdir $@)" -vv --image=opensuse:42.3 42.2/test%: ; ./testsuite.py "$(notdir $@)" -vv --image=opensuse:42.2 18.04/test%: ; ./testsuite.py "$(notdir $@)" -vv --image=ubuntu:18.04 16.04/test%: ; ./testsuite.py "$(notdir $@)" -vv --image=ubuntu:16.04 7.5/test%: ; ./testsuite.py "$(notdir $@)" -vv --image=centos:7.5.1804 7.4/test%: ; ./testsuite.py "$(notdir $@)" -vv --image=centos:7.4.1708 7.3/test%: ; ./testsuite.py "$(notdir $@)" -vv --image=centos:7.3.1611 15.0/st%: ; $(MAKE) 3 && ./testsuite.py "te$(notdir $@)" -vv --image=opensuse/leap:15.0 $(WITH3) 42.3/st%: ; $(MAKE) 3 && ./testsuite.py "te$(notdir $@)" -vv --image=opensuse:42.3 $(WITH3) 42.2/st%: ; $(MAKE) 3 && ./testsuite.py "te$(notdir $@)" -vv --image=opensuse:42.2 $(WITH3) 18.04/st%: ; $(MAKE) 3 && ./testsuite.py "te$(notdir $@)" -vv --image=ubuntu:18.04 $(WITH3) 16.04/st%: ; $(MAKE) 3 && ./testsuite.py "te$(notdir $@)" -vv --image=ubuntu:16.04 $(WITH3) nightrun: checkall $(MAKE) checks checkall: $(MAKE) "test_[1234]" $(MAKE) "7.5/test_[567]" $(MAKE) "7.4/test_[567]" $(MAKE) "7.3/test_[567]" $(MAKE) "18.04/test_[567]" $(MAKE) "16.04/test_[567]" $(MAKE) "15.0/test_[567]" $(MAKE) "42.3/test_[567]" $(MAKE) "18.04/st_[567]" $(MAKE) "16.04/st_[567]" $(MAKE) "15.0/st_[567]" $(MAKE) "42.3/st_[567]" check: check2018 @ echo please run 'make checks' now 18 check2018: ; ./testsuite.py -vv --opensuse=15.0 --centos=7.5 --ubuntu=18.04 17 check2017: ; ./testsuite.py -vv --opensuse=42.3 --centos=7.4 --ubuntu=16.04 16 check2016: ; ./testsuite.py -vv --opensuse=42.2 --centos=7.3 --ubuntu=16.04 2/test_%: $(MAKE) tmp_systemctl_py_2 ./testsuite.py -vv $(notdir $@) --sometime=666 \ '--with=tmp/systemctl.py' --python=/usr/bin/python 3/test_%: $(MAKE) tmp_systemctl_py_3 ./testsuite.py -vv $(notdir $@) --sometime=666 \ '--with=tmp/systemctl.py' --python=/usr/bin/python3 2/est_%: $(MAKE) tmp_systemctl_py_2 ./testsuite.py -vv t$(notdir $@) --sometime=666 --coverage \ '--with=tmp/systemctl.py' --python=/usr/bin/python 3/est_%: $(MAKE) tmp_systemctl_py_3 ./testsuite.py -vv t$(notdir $@) --sometime=666 --coverage \ '--with=tmp/systemctl.py' --python=/usr/bin/python3 check2: $(MAKE) tmp_systemctl_py_2 ./testsuite.py -vv \ '--with=tmp/systemctl.py' --python=/usr/bin/python check3: $(MAKE) tmp_systemctl_py_3 ./testsuite.py -vv \ '--with=tmp/systemctl.py' --python=/usr/bin/python3 checks: checks.1 checks.2 checks.3 checks.4 checks.1: - rm .coverage* checks.2: $(MAKE) checks2_coverage for i in .coverage*; do mv $$i $$i.cov2; done checks.3: $(MAKE) checks3_coverage for i in .coverage*; do mv $$i $$i.cov3; done checks.4: coverage combine && coverage report && coverage annotate ls -l tmp/systemctl.py,cover @ echo ".... are you ready for 'make checkall' ?" checks2: rm .coverage* ; $(MAKE) checks2_coverage checks2_coverage: $(MAKE) tmp_systemctl_py_2 ./testsuite.py -vv --coverage \ '--with=tmp/systemctl.py' checks3: rm .coverage* ; $(MAKE) checks3_coverage checks3_coverage: $(MAKE) tmp_systemctl_py_3 ./testsuite.py -vv --coverage \ '--with=tmp/systemctl.py' --python=/usr/bin/python3 coverage: coverage2 coverage2: $(MAKE) tmp_systemctl_py_2 rm .coverage* ; ./testsuite.py -vv --coverage test_1 test_2 test_3 test_4 test_6 \ '--with=tmp/systemctl.py' coverage3: $(MAKE) tmp_systemctl_py_3 rm .coverage* ; ./testsuite.py -vv --coverage test_1 test_2 test_3 test_4 test_6 \ '--with=tmp/systemctl.py' --python=/usr/bin/python3 tmp_systemctl_py_2: @ test -d tmp || mkdir tmp @ cp files/docker/systemctl.py tmp/systemctl.py tmp_systemctl_py_3: @ test -d tmp || mkdir tmp @ cp files/docker/systemctl.py tmp/systemctl.py @ sed -i -e "s|/usr/bin/python|/usr/bin/python3|" tmp/systemctl.py clean: - rm .coverage* - rm -rf tmp/tmp.test_* - rm -rf tmp/systemctl.py box: docker rm -f $@ ; docker run -d --name $@ --rm=true centos:centos7 sleep 600 docker cp files/docker/systemctl.py box:/usr/bin/systemctl docker exec box systemctl daemon-reload -vvv @ echo : docker exec -it box bash docker-systemctl-replacement-1.4.3424/README.md000066400000000000000000000226261355427602500210420ustar00rootroot00000000000000# docker systemctl replacement This script may be used to overwrite "/usr/bin/systemctl". It will execute the systemctl commands without SystemD! This is used to test deployment of services with a docker container as the target host. Just as on a real machine you can use "systemctl start" and "systemctl enable" and other commands to bring up services for further configuration and testing. Information from "systemctl show" allows deployment automation tools to work seemlessly. This script can also be run as docker-init of a docker container (i.e. the main "CMD" on PID 1) where it will automatically bring up all enabled services in the "multi-user.target" and where it will reap all zombies from background processes in the container. When running a "docker stop" on such a container it will also bring down all configured services correctly before exit. ## docker exec lamp-stack-container systemctl list-units --state=running httpd.service loaded active running The Apache HTTP Server mariadb.service loaded active running MariaDB database server ## docker exec lamp-stack-container pstree -ap systemctl,1 /usr/bin/systemctl |-httpd,7 -DFOREGROUND | |-httpd,9 -DFOREGROUND | |-httpd,10 -DFOREGROUND `-mysqld_safe,44 /usr/bin/mysqld_safe --basedir=/usr `-mysqld,187 --basedir=/usr --datadir=/var/lib/mysql |-{mysqld},191 |-{mysqld},192 ## Problems with SystemD in Docker The background for this script is the inability to run a SystemD daemon easily inside a docker container. There have been multiple workarounds with varying complexity and actual functionality. (The systemd-nsspawn tool is supposed to help with running systemd in a container but only rkt with CoreOs is using it so far). Most people have come to take the easy path and to create a startup shell script for the docker container that will bring up the service processes one by one. Essentially one would read the documentation or the SystemD `*.service` scripts of the application to see how that would be done. By using this replacement script a programmer can skip that step. ## Service Manager The systemctl-replacement script does cover the functionality of a service manager where commands like `systemctl start xx` are executed. This is achieved by parsing the `*.service` files that are installed by the standard application packages (rpm, deb) in the container. These service unit descriptors define the actual commands to start/stop a service in their ExecStart/ExecStop settings. When installing systemctl.py as /usr/bin/systemctl in a container then it provides enough functionality that deployment scripts for virtual machines continue to work unchanged when trying to start/stop, enable/disable or mask/unmask a service in a container. This is also true for deployment tools like Ansible. As of version 2.0 and later Ansible is able to connect to docker containers directly without the help of a ssh-daemon in the container. Just make your inventory look like [frontend] my_frontend_1 ansible_connection=docker Based on that `ansible_connection` one can enable the systemctl-replacement to intercept subsequent calls to `"service:"` steps. Effectivly Ansible scripts that shall be run on real virtual machines can be tested with docker containers. However in newer centos/ubuntu images you need to check for python first. - copy: src="files/docker/systemctl.py" dest="/usr/bin/systemctl" - package: name="python" - file: name="/run/systemd/system/" state="directory" - service: name="dbus.service" state="stopped" See [SERVICE-MANAGER](SERVICE-MANAGER.md) for more details. --- ## Problems with PID 1 in Docker The command listed as "CMD" in the docker image properties or being given as docker-run argument will become the PID-1 of a new container. Actually it takes the place that would traditionally be used by the /sbin/init process which has a special functionality in unix'ish operating systems. The docker-stop command will send a SIGTERM to PID-1 in the container - but NOT to any other process. If the CMD is the actual application (exec java -jar whatever) then this works fine as it will also clean up its subprocesses. In many other cases it is not sufficient leaving [zombie processes](https://www.howtogeek.com/119815/) around. Zombie processes may also occur when a master process does not do a `wait` for its children or the children were explicitly "disown"ed to run as a daemon themselves. The systemctl replacment script can help here as it implements the "zombie reaper" functionality that the standard unix init daemon would provide. Otherwise the zombie PIDs would continue to live forever (as long as the container is running) filling also the process table of the docker host as the init daemon of the host does not reap them. ## Init Daemon Another function of the init daemon is to startup the default system services. What has been known as runlevel in SystemV is now "multi-user.target" or "graphical.target" in a SystemD environment. Let's assume that a system has been configured with some "systemctl enable xx" services. When a virtual machine starts then these services are started as well. The systemctl-replacement script does provide this functionality for a docker container, thereby implementing "systemctl default" for usage in inside a container. The "systemctl halt" command is also implemented allowing to stop all services that are found as "is-enabled" services that have been run upon container start. It does execute all the "systemctl stop xx" commands to bring down the enabled services correctly. This is most useful when the systemctl replacement script has been run as the entrypoint of a container - so when a "docker stop" sends a SIGTERM to the container's PID-1 then all the services are shut down before exiting the container. This can be permanently achieved by registering the systemctl replacement script as the CMD attribute of an image, perhaps by a "docker commit" like this: docker commit -c "CMD ['/usr/bin/systemctl']" \ -m "" After all it allows to use a docker container to be more like a virtual machine with multiple services running at the same time in the same context. See [INIT-DAEMON](INIT-DAEMON.md) for more details. --- ## Testsuite and Examples There is an extensive testsuite in the project that allows for a high line coverage of the tool. All the major functionality of the systemctl replacement script is being tested so that its usage in continuous development pipeline will no break on updates of the script. If the systemctl.py script has some important changes in the implementation details it will be marked with an update of the major version. Please run the `testsuite.py` or `make check` upon providing a patch. It takes a couple of minutes because it may download a number of packages during provisioning - but with the help of the [docker-mirror-packages-repo](https://github.com/gdraheim/docker-mirror-packages-repo) scripting this can be reduced a lot (it even runs without internet connection). Some real world examples have been cut out into a seperate project. This includes dockerfile and ansible based tests to provide common applications like webservers, databases and even a Jenkins application. You may want to have a look at [gdraheim/docker-systemctl-images](https://github.com/gdraheim/docker-systemctl-images) list. See [TESTSUITE](TESTUITE.md) for more details. ## Development Although this script has been developed for quite a while, it does only implement a limited number of commands. It does not cover all commands of "systemctl" and it will not cover all the functionality of SystemD. The implementation tries to align with SystemD's systemctl commands as close as possible as quite some third party tools are interpreting the output of it. However the implemented software [ARCHITECTURE](ARCHITECTURE.md) is very different. The systemctl replacement script has a long [HISTORY](HISTORY.md) now with over a [thousand commits on github](https://github.com/gdraheim/docker-systemctl-replacement/tree/master) (mostly for the testsuite). It has also garnered some additional functionality like the [USERMODE](USERMODE.md) which is specifically targeted at running docker containers. See the [RELEASENOTES](RELEASENOTES.md) for the latest achievements. The choice of the [EUPL-LICENSE](EUPL-LICENSE.md) is intentionally permissive to allow you to copy the script to your project. Sadly the functionality of SystemD's systemctl is badly documented so that much of the current implementation is done by trial and fixing the errors. Some [BUGS](BUGS.md) are actually in other tools and need to be circumvented. As most programmers tend to write very simple `*.service` files it works in a surprising number of cases however. But definitly not all. So if there is a problem, use the [github issue tracker](https://github.com/gdraheim/docker-systemctl-replacement/issues) to make me aware of it. In general it is not needed to emulate every feature as [EXTRA-CONFIGS](EXTRA-CONFIGS.md) can help. And I take patches. ;) ## The author Guido Draheim is working as a freelance consultant for multiple big companies in Germany. This script is related to the current surge of DevOps topics which often use docker as a lightweight replacement for cloud containers or even virtual machines. It makes it easier to test deployments in the standard build pipelines of development teams. docker-systemctl-replacement-1.4.3424/RELEASENOTES.md000066400000000000000000000126211355427602500217700ustar00rootroot00000000000000**please change stable container dockerfiles to download from LTS branch/v1.4** RELEASE 1.4 Additional features have been put into the 1.4 series of systemctl.py. The commands of "mask"/"unmask"/"set-default"/"get-default" have been implemented where the "default" runlevel is only modified by not checked by systemctl.py. The PID-1 will now assemble all journal logs of services and print them from the init_loop to stdout so that "docker logs" shows the status logs of all the services currently running. Changes include that /var/run/systemd/notify is not the default notify_socket anymore but instead each service has its own ../notify.A.service channel. Along with the A.service.lock behaviour one may safely run many systemctl.py in parallel that will not overlap in getting text. The switch to fork/execve in 1.3 had been allowing some process to leave zombies or even block the master systemctl.py which has been resolved now. After all, the service.pid parts are completely gone with the MainPID register in the service.status files now. Checking the (non)active status has been stabilized from these changes. The support for usermode containers had been already present in the 1.3 series but is now tested and documented. The docker-systemctl-images sister project will show a number of examples of it where the PID-1 of a container is not run as root but as a specific user - only allowing to start services with the same user association. The testsuite has been expanded a lot to support testing usermode containers which can not be done just by running systemctl --root=path. Additionally the testing times do increase from testing various pairs of python version and linux distributions and their versions. Thus --python and --image can be used to get to the details of compatibility. After all, the coverage is now at 92%. The question of how to let a PID-1 systemctl.py exit the container has been put into a new state. When running "systemctl.py init A B" then PID-1 will end when both A and B have turned to a non-active status. When running a plain "systemctl.py" then it is the same as "systemctl.py init" which is the same as "systemctl.py --init default", all of which will never stop. So this PID-1 is identical with /sbin/init that keeps it up. However PID-1 will not run forever in that "init" mode. You can always send a SIGQUIT, SIGINT, SIGTERM to PID-1. Upon SIGQUIT the PID-1 systemctl.py will wait for all processed in the container to have ended. The other signals will make it leave. Last not least there are some minor adjustement like adding to the list of expanded special vars like "%t" runtime. And it was interesting to see how "postgresql-setup initdb" runs "systemctl show -p Environment" expecting to get a list of settings in one line. Also when using "User=" then the started services should also get the default group of that system user. RELEASE 1.4.2456 There a couple of bugfixes and little enhancements asking for an intermediate patch release. Be sure to update any 1.4 script to this version soon. Some of the `%x` special vars were not expanded, and some of the environment variables like `$HOME` were not present or incorrect. Functionality of the `mask` command has been improved as well. The release does also include the extension of the testsuite that was otherwise intended for RELEASE 1.5 where different versions of common distro images are included in the nighrun tests. It did uncover a bug in ubuntu `kill` by the way that may got unnoticed by some application packages so far. RELEASE 1.4.3000 There are a couple of bugfixes. The most prominent one is the proper support of drop-in overide configs. This can be very useful in scenarios where one wants to install a `*.rpm` from an upstream distributor adding some additional parts only in the case of a docker container image. That has been described more prominently in [EXTRA-CONFIGS.md]. The general README itself contains an easier introduction with a hint on how a multi-service container looks like from the inside. That should make some visual impression on everyone who has already worked with containers so far. RELEASE 1.4.3424 There are a couple of bugfixes. The most prominent is a missing 'break' in the InitLoop (from --init), so that a SIGTERM to PID 1 of a container did not initiate a 'halt' shutdown sequence. However that was the intention in support of a 'docker stop container'. The ExecStartPre subprocess and the change to WorkingDir did not fail even when they should. This is fixed now but it may provoke some new errors downstream. Which is a good thing as the same may happen to that service on a systemd controlled machine. Other than that, testcases were updated to opensuse/leap:15.1. The helper project 'docker-mirror-packages-repo' does even support more version but that is not part of the v1.4 anymore. Since early 2019 there has been an LTS bugfix branch for v1.4 already. The difference to the 'master' branch was minimal however. By November 2019 the 'master' branch will be switched to v1.5 which has a couple of functional changes. Some projects did not download from LTS branch/v1.4 but directly from master - they should change their processes soon as the next rebuild from 'master' may change some behaviour. Specifically, the v1.4 generation was implicitly "Restart=no" but the v1.5 generation will enable "Restart=always" by default (running from the init loop, i.e. if systemctl.py is running on PID 1 or started with --init). docker-systemctl-replacement-1.4.3424/SERVICE-MANAGER.md000066400000000000000000000173501355427602500222530ustar00rootroot00000000000000# Service Manager The systemctl-replacement script is not really a replacement for SystemD itself. Instead it covers enough functionality that one does not need to run a SystemD daemon in a docker container in order to start/stop/run services in it. In an operating system that is based on SystemD, the older unix scripts like "service xx start" and "service xx stop" are diverted to run "systemctl start xx" and "systemctl stop xx". Additionally deployment tools like Ansible will ask for the status of a service by executing the "systemctl" tool. Hence overwriting /usr/bin/systemctl is sufficient to intercept all service management requests. ## Problems with SystemD in Docker The background for this script is the inability to run a SystemD daemon easily inside a docker container. There have been multiple workarounds with varying complexity and actual functionality. The systemd-nsspawn tool is supposed to help with running systemd in a container - but only rkt with its CoreOs is using it so far. Most people have come to take the easy path and to create a startup shell script for the docker container that will bring up the service processes one by one. Essentially one would read the documentation or the SystemD `*.service` scripts of the application to see how that would be done. By using this replacement script a programmer can skip that step. As a historic background this script was born when the deployment targets shifted from RHEL6 (with initscripts) to RHEL7 (with SystemD) and suddenly even a simple call to "service app start" would result in errors from a missing SystemD-daemon. By using this docker systemctl replacment script one could continue with the original installers. Please note that this systemctl replacement will also read and interpret initscripts in /etc/init.d/. As such you can also deploy older applications with a classic SysV-style start/stop script and they are handled similar to how SystemD would care for them. ## Usage along with Ansible This script has been tested often along with deployments using Ansible. As of version 2.0 and later Ansible is able to connect to docker containers directly without the help of a ssh-daemon in the container and a known-ip of the container. Just make your inventory look like [frontend] my_frontend_1 ansible_connection=docker With a local docker container the turnaround times of testing a deployment script are a lot shorter. If there is an error it is much faster to get a fresh target host and to have the basic deployment parts run on it. With the help of docker-compose one can bring up a number of hosts as docker containers and to use Ansible to orchestrate the deployment. Some of the containers will actually be interconnected with each other and the deployed services will only work with some other service of another container to be running. This is the main difference over Dockerfiles which can only build one container. And a Dockerfile is extremely limited if the deployment includes the configuration of services using their http-based API. Not so with a deployment tool like Ansible. ## Installation as a systemctl-replacement Simply overwrite /usr/bin/systemctl - but remember that any package installation may update the real 'systemd' so that you have to do the overwrite after every major package installation. Or just do it firsthand. - name: update systemd package: name="systemd" state="latest" when: "ansible_connection == 'docker'" - name: install systemctl.py copy: src="files/docker/systemctl.py" dest="/usr/bin/systemctl" when: "ansible_connection == 'docker'" Note that such a setup will also work when using Ansible's service module to start/stop/enable services on a target host. On a systemd-controlled operating system the old "service" script will delegate commands to systemctl anyway. ## Diverted commands When the systemctl-replacement has been installed as /usr/bin/systemctl then it is also executed when then following commands are run service xx start => systemctl start xx service xx stop => systemctl stop xx service xx status => systemctl status xx service xx reload => systemctl reload xx chkconfig xx => systemctl is-enabled xx chkconfig xx on => systemctl enable xx chkconfig xx off => systemctl disable xx You can see that when enabling the implicit logging for the systemctl replacement script by doing a `touch /var/log/systemctl.log` ## Python Installation At the time that systemctl.py was started all the docker images of the established Linux distros (redhat/centos, ubuntu, opensuse) had Python preinstalled - just as on the real machines where these distros are installed, as Python is used heavily in configuration scripts for these operating systems. However Python has since been removed in later versions of the official docker images of these Linux distros in an attempt to lower the size of the images used to build application containers. As such you need to extend the initial setup steps for systemctl.py - in order to make it work, you need to overwrite /usr/bin/systemctl and to install Python. - name: install systemctl.py copy: src="files/docker/systemctl.py" dest="/usr/bin/systemctl" when: "ansible_connection == 'docker'" - name: install python for systemctl.py package: name="python" when: "ansible_connection == 'docker'" You can also use `systemctl3.py` along with `python3` for the same effect. ## Ansible Workaround There are a number of cases where systemd is installed in a target operating system but systemd does not control the system services. So the makers of Ansible try to be clever (since about version 2.2) checking for a target to be systemd-controlled for real. This is done like this in `ansible/modules/system/service.py` def check_systemd(): # tools must be installed if location.get('systemctl', False): # this should show if systemd is the boot init system # these mirror systemd's own sd_boot test for canary in ["/run/systemd/system/", "/dev/.run/systemd/", "/dev/.systemd/"]: if os.path.exists(canary): return True # If all else fails, check if init is the systemd command, using comm as cmdline could be symlink try: f = open('/proc/1/comm', 'r') except IOError: # If comm doesn't exist, old kernel, no systemd return False for line in f: if 'systemd' in line: return True return False In order to defeat these extra checks it is a good idea to simply create the run-directory that is used by systemctl replacement script anyway. However this must be done before starting any Ansible "service:" call. If you do run some direct shell "systemctl" (or non-installed "systemctl.py") command before that time then you would not need it. - hosts: a gather_facts: no pre_tasks: - name: install systemctl.py copy: src="files/docker/systemctl.py" dest="/usr/bin/systemctl" when: "ansible_connection == 'docker'" - name: install python for systemctl.py package: name=python when: "ansible_connection == 'docker'" - name: ensure run directory for ansible check_systemd file: name="/run/systemd/system/" state="directory" when: "ansible_connection == 'docker'" tasks: - name: check some service service: name="dbus.service" state="stopped" If you do not create the run-directory then Ansible will fall back into thinking it is a SystemV like operating system with all services living in the `/etc/init.d` world. As a result all the calls to "service:" will result in "Could not find the requested service dbus.service: host". docker-systemctl-replacement-1.4.3424/TESTSUITE.md000066400000000000000000000067361355427602500215020ustar00rootroot00000000000000# The Testsuite Before any release "make check" should report no problems. Some testcases are skipped as they are known bugs or they point to missing features of the systemctl replacement script. That's okay. The testsuite is organized by four digit numbers. In all documents (and issue tickets) only the number is used to identify the relevant testcase. Four digits are used to put related testcase into a common group, it is not related to the amount of testcases (other than having enough free numbers in between the groups to allow for later addition of testcases into an existing group). ## running testcases by number The standard python unittest.main() is not used. Instead there is a slightly different frontend that allows to run testcases by number in the style of "test_4032". So all the testcases do have a number and a longer function name where the remainder of the function name does not need to be used to run a test. It may be easiest to say * make test_4032 Actually the implementation is checking for the given testcase by checking for test function names with that prefix. This will also allow to run a group of tests with a common prefix. The number will ensure that they are being run in an expected order, so that "make test_900" will run the (up to) ten testcases of test_9000...test_9009 in that order. ## docker-container or subdir-root The systemctl script supports a non-standard option '--root' which will ensure that *.service files and other data are being searched below that point. So with --root=/tmp/run2 you can install a /tmp/run2/etc/systemd/system/zzc.service to be the only file being interpreted by the systemctl replacement. If there is no --root option being used then the systemctl script will inspect the standard systemd locations on the disk. This can be handy in some circumstance but for that test cases that is only an option inside a docker container where one can control the files that are being installed as test data. Note that * the testcases 1000...4999 are using a --root=subdir environment * the testcases 5000...9999 will start a docker container to work. Also remember that not all systemctl replacment script commands have been checked to interpret the --root=subdir option correctly. Be wary when you run systemctl.py outside of a docker container. ## python line coverage There is an option --coverage on the testsuite which will use an install pycoverage (in /usr/bin/coverage2) tool to gather the testsuite coverage of the systemctl.py replacement script. The result will be in files/docker/systemctl.py,cover ! You can use "make check" to simply start all testscases including gathering the coverage. Note however that only the subdir-root testcases count for the python coverage. (The pycoverage tool is currently not installed into a docker container and its results are not pulled out for being added to the general line coverage). Therefore * only the testcase 1000...4999 count for line coverage * running sinle testcases like "make test_4032" has no coverage ## junit xml results The script may also write a junit xml report instead of the standard python testcase results. However this is not part of the source tree - you need to grab an "xmlrunner.py" copy and install it to the place where "testsuite.py" shall be run. That's because a junit xml report does only make sense along with a Jenkins build server and/or a Sonar server setup where the results should be gathered. That's not being used for the opensource Github version at the moment. docker-systemctl-replacement-1.4.3424/USERMODE.md000066400000000000000000000143251355427602500213250ustar00rootroot00000000000000# The User Mode Be warned, the systemctl script's user-mode is not identical with the systemd user-manager behaviour. Both concepts can be explicitly selected when running "systemctl --user" however. For the systemctl replacement script it enables the user-mode while for systemd it opens the socket to send commands to the user-manager. Such a user-manager exists when you have a graphical desktop running. In reality, the user-manager feature has been scarcily used. The systemd guys do even think about droppping the 'systemctl --user' level altogether. Even when you are not root, all commands default to "systemctl --system" anyway, assuming a systemd controlled operating system. Anyway, the distinction of user/system level is the reason that most developers will need to install their new *.service files into a subdirectory of /etc/systemd or /usr/lib/systemd. If you look closer then you will see that there are two subdirectories, and there are actually service files in your /usr/lib/systemd/user/ directory. Did you ever think about why the *.service install directory is named /usr/lib/systemd/system/.? Well, here's why. Let's get back to systemctl.py. It does behave similar to systemd's systemctl in that it will only search x/systemd/system directories when in system-mode. It ignores any files in x/systemd/user directories. However if in user-mode then x/systemd/user directories are searched - and just like the systemd user-manager it will also find *.service files under the user's home directory at `"~/.config/systemd/user"` and `"~.local/share/systemd/user"` So when running "systemctl --user list-units" you will get a very different list of services. That accounts for both the systemctl script and systemd's systemctl. Give it a try. ## User Containers The existance of a user-level would have been totally irrelevant if it would not have been for a rule from OpenShift in that processes in a container must not try to be run as a virtual root user. In fact, all docker containers are started with an explicit "docker run --user=foo" value. So when the systemctl replacement script is registered as the CMD of the container then it will not have root permissions. Now that can make for a problem when you consider that services may want to read/write global /var/xyz directories. If systemctl.py is started in a user container then it can not start services that expect to be run with root permissions. It just does not make sense. Luckily, most modern *.service files do name an explicit "User=target". So in a user container, the systemctl replacement script can start them when the target user is the same as the container has been started under. Effectivly, when systemctl script is running as PID-1 it will filter all *.service files to the current user of the container. Theoretically you can still start multiple enabled services through the systemctl script in a single container. Practically it does not happen very often as a tomcat.service has "User=tomcat" and a postgresql.service has "User=postgres". There is no way to run them both in a user container at the same time. Unless of course you change those lines while preparing such a docker image to have the same "User=target". ## User Mode The systemctl.py user-mode is automatically selected when it runs as PID-1 and it finds itself to be not root (the re-mapped virtual root of course). As described above, only systemd/system files with the same running user may be started/stopped. The systemctl default-services list of enabled services will only show those. Adding to that, the systemctl replacment script will also offer to start/stop systemd/user services as they are naturally designed to be started as non-root processes. And that's the basic difference between systemd's user-manager and systemctl.py user-mode - when you select "systemctl --user" then our script will allow operations on all systemd/user services plus all systemd/system services with a matching "User=target". The systemd's systemctl will however only offer systemd/user services. Having systemd/user services in user-mode allows a developer to explicitly prepare a service to be run under a user-manager. In fact one can find a lot of examples that were meant to be run as user containers where the actually application is deployed to a virtual /home/foo in the container image. When taking advantage of the systemctl replacement script then a developer can just as well add a /home/foo/.config/systemd/user/foo.service file. Such a deployment script will not only allow such an application to be run in a user-container but it will also work in a classic virtual machine with a user-manager. ## Other Paths In the general case, developers do not need to think about that too much. Just deploy a systemd/system service and when there is a "User=target" line in the configuration then the docker image can be started as a user container. You can the use systemctl script just like with classic containers. When a developer takes advantage of deploying to /home/foo then you need to be aware however that some other directories are searched for these user services. And the special variables like %t in a *.service file may in fact point to a different directory. The freedeskop standard for XDG directories has the details what to think about. ## User Images At the url https://github.com/gdraheim/docker-systemctl-images you find a sister project that has examples where the systemctl replacment script is used to help creating docker images. Not only are there classic images where the systemctl script will run as PID-1 in root mode - there are also docker images which have a user-mode configuration. In fact, the Dockerfile "USER foo" directive is meant to force an image into be run as a user container by default. So any of those images will have the systemctl script in user-mode. Naturally, the docker-systemctl-images can show user-mode images for single applications like Apache and MariaDB. But having a full LAMP stack in single container is not possible for user-mode. Anyway, it just works - you can use the systemctl replacement script even for user containers. No need write special docker-entrypoint scripts, just install the service, enable it, name the "USER target" in the Dockerfile, and then you can run the container. docker-systemctl-replacement-1.4.3424/files/000077500000000000000000000000001355427602500206555ustar00rootroot00000000000000docker-systemctl-replacement-1.4.3424/files/docker/000077500000000000000000000000001355427602500221245ustar00rootroot00000000000000docker-systemctl-replacement-1.4.3424/files/docker/systemctl.py000077500000000000000000006065161355427602500245460ustar00rootroot00000000000000#! /usr/bin/python from __future__ import print_function __copyright__ = "(C) 2016-2019 Guido U. Draheim, licensed under the EUPL" __version__ = "1.4.3424" import logging logg = logging.getLogger("systemctl") import re import fnmatch import shlex import collections import errno import os import sys import signal import time import socket import datetime import fcntl if sys.version[0] == '2': string_types = basestring BlockingIOError = IOError else: string_types = str xrange = range COVERAGE = os.environ.get("SYSTEMCTL_COVERAGE", "") DEBUG_AFTER = os.environ.get("SYSTEMCTL_DEBUG_AFTER", "") or False EXIT_WHEN_NO_MORE_PROCS = os.environ.get("SYSTEMCTL_EXIT_WHEN_NO_MORE_PROCS", "") or False EXIT_WHEN_NO_MORE_SERVICES = os.environ.get("SYSTEMCTL_EXIT_WHEN_NO_MORE_SERVICES", "") or False FOUND_OK = 0 FOUND_INACTIVE = 2 FOUND_UNKNOWN = 4 # defaults for options _extra_vars = [] _force = False _full = False _now = False _no_legend = False _no_ask_password = False _preset_mode = "all" _quiet = False _root = "" _unit_type = None _unit_state = None _unit_property = None _show_all = False _user_mode = False # common default paths _default_target = "multi-user.target" _system_folder1 = "/etc/systemd/system" _system_folder2 = "/var/run/systemd/system" _system_folder3 = "/usr/lib/systemd/system" _system_folder4 = "/lib/systemd/system" _system_folder9 = None _user_folder1 = "~/.config/systemd/user" _user_folder2 = "/etc/systemd/user" _user_folder3 = "~.local/share/systemd/user" _user_folder4 = "/usr/lib/systemd/user" _user_folder9 = None _init_folder1 = "/etc/init.d" _init_folder2 = "/var/run/init.d" _init_folder9 = None _preset_folder1 = "/etc/systemd/system-preset" _preset_folder2 = "/var/run/systemd/system-preset" _preset_folder3 = "/usr/lib/systemd/system-preset" _preset_folder4 = "/lib/systemd/system-preset" _preset_folder9 = None SystemCompatibilityVersion = 219 SysInitTarget = "sysinit.target" SysInitWait = 5 # max for target EpsilonTime = 0.1 MinimumYield = 0.5 MinimumTimeoutStartSec = 4 MinimumTimeoutStopSec = 4 DefaultTimeoutStartSec = int(os.environ.get("SYSTEMCTL_TIMEOUT_START_SEC", 90)) # official value DefaultTimeoutStopSec = int(os.environ.get("SYSTEMCTL_TIMEOUT_STOP_SEC", 90)) # official value DefaultMaximumTimeout = int(os.environ.get("SYSTEMCTL_MAXIMUM_TIMEOUT", 200)) # overrides all other InitLoopSleep = int(os.environ.get("SYSTEMCTL_INITLOOP", 5)) ProcMaxDepth = 100 MaxLockWait = None # equals DefaultMaximumTimeout DefaultPath = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ResetLocale = ["LANG", "LANGUAGE", "LC_CTYPE", "LC_NUMERIC", "LC_TIME", "LC_COLLATE", "LC_MONETARY", "LC_MESSAGES", "LC_PAPER", "LC_NAME", "LC_ADDRESS", "LC_TELEPHONE", "LC_MEASUREMENT", "LC_IDENTIFICATION", "LC_ALL"] # The systemd default is NOTIFY_SOCKET="/var/run/systemd/notify" _notify_socket_folder = "/var/run/systemd" # alias /run/systemd _pid_file_folder = "/var/run" _journal_log_folder = "/var/log/journal" _systemctl_debug_log = "/var/log/systemctl.debug.log" _systemctl_extra_log = "/var/log/systemctl.log" _default_targets = [ "poweroff.target", "rescue.target", "sysinit.target", "basic.target", "multi-user.target", "graphical.target", "reboot.target" ] _feature_targets = [ "network.target", "remote-fs.target", "local-fs.target", "timers.target", "nfs-client.target" ] _all_common_targets = [ "default.target" ] + _default_targets + _feature_targets # inside a docker we pretend the following _all_common_enabled = [ "default.target", "multi-user.target", "remote-fs.target" ] _all_common_disabled = [ "graphical.target", "resue.target", "nfs-client.target" ] _runlevel_mappings = {} # the official list _runlevel_mappings["0"] = "poweroff.target" _runlevel_mappings["1"] = "rescue.target" _runlevel_mappings["2"] = "multi-user.target" _runlevel_mappings["3"] = "multi-user.target" _runlevel_mappings["4"] = "multi-user.target" _runlevel_mappings["5"] = "graphical.target" _runlevel_mappings["6"] = "reboot.target" _sysv_mappings = {} # by rule of thumb _sysv_mappings["$local_fs"] = "local-fs.target" _sysv_mappings["$network"] = "network.target" _sysv_mappings["$remote_fs"] = "remote-fs.target" _sysv_mappings["$timer"] = "timers.target" def shell_cmd(cmd): return " ".join(["'%s'" % part for part in cmd]) def to_int(value, default = 0): try: return int(value) except: return default def to_list(value): if isinstance(value, string_types): return [ value ] return value def unit_of(module): if "." not in module: return module + ".service" return module def os_path(root, path): if not root: return path if not path: return path while path.startswith(os.path.sep): path = path[1:] return os.path.join(root, path) def os_getlogin(): """ NOT using os.getlogin() """ import pwd return pwd.getpwuid(os.geteuid()).pw_name def get_runtime_dir(): explicit = os.environ.get("XDG_RUNTIME_DIR", "") if explicit: return explicit user = os_getlogin() return "/tmp/run-"+user def get_home(): explicit = os.environ.get("HOME", "") if explicit: return explicit return os.path.expanduser("~") def _var_path(path): """ assumes that the path starts with /var - when in user mode it shall be moved to /run/user/1001/run/ or as a fallback path to /tmp/run-{user}/ so that you may find /var/log in /tmp/run-{user}/log ..""" if path.startswith("/var"): runtime = get_runtime_dir() # $XDG_RUNTIME_DIR if not os.path.isdir(runtime): os.makedirs(runtime) os.chmod(runtime, 0o700) return re.sub("^(/var)?", get_runtime_dir(), path) return path def shutil_setuid(user = None, group = None): """ set fork-child uid/gid (returns pw-info env-settings)""" if group: import grp gid = grp.getgrnam(group).gr_gid os.setgid(gid) logg.debug("setgid %s '%s'", gid, group) if user: import pwd pw = pwd.getpwnam(user) if not group: gid = pw.pw_gid os.setgid(gid) logg.debug("setgid %s", gid) uid = pw.pw_uid os.setuid(uid) logg.debug("setuid %s '%s'", uid, user) home = pw.pw_dir shell = pw.pw_shell logname = pw.pw_name return { "USER": user, "LOGNAME": logname, "HOME": home, "SHELL": shell } return {} def shutil_truncate(filename): """ truncates the file (or creates a new empty file)""" filedir = os.path.dirname(filename) if not os.path.isdir(filedir): os.makedirs(filedir) f = open(filename, "w") f.write("") f.close() # http://stackoverflow.com/questions/568271/how-to-check-if-there-exists-a-process-with-a-given-pid def pid_exists(pid): """Check whether pid exists in the current process table.""" if pid is None: return False return _pid_exists(int(pid)) def _pid_exists(pid): """Check whether pid exists in the current process table. UNIX only. """ if pid < 0: return False if pid == 0: # According to "man 2 kill" PID 0 refers to every process # in the process group of the calling process. # On certain systems 0 is a valid PID but we have no way # to know that in a portable fashion. raise ValueError('invalid PID 0') try: os.kill(pid, 0) except OSError as err: if err.errno == errno.ESRCH: # ESRCH == No such process return False elif err.errno == errno.EPERM: # EPERM clearly means there's a process to deny access to return True else: # According to "man 2 kill" possible error values are # (EINVAL, EPERM, ESRCH) raise else: return True def pid_zombie(pid): """ may be a pid exists but it is only a zombie """ if pid is None: return False return _pid_zombie(int(pid)) def _pid_zombie(pid): """ may be a pid exists but it is only a zombie """ if pid < 0: return False if pid == 0: # According to "man 2 kill" PID 0 refers to every process # in the process group of the calling process. # On certain systems 0 is a valid PID but we have no way # to know that in a portable fashion. raise ValueError('invalid PID 0') check = "/proc/%s/status" % pid try: for line in open(check): if line.startswith("State:"): return "Z" in line except IOError as e: if e.errno != errno.ENOENT: logg.error("%s (%s): %s", check, e.errno, e) return False return False def checkstatus(cmd): if cmd.startswith("-"): return False, cmd[1:] else: return True, cmd # https://github.com/phusion/baseimage-docker/blob/rel-0.9.16/image/bin/my_init def ignore_signals_and_raise_keyboard_interrupt(signame): signal.signal(signal.SIGTERM, signal.SIG_IGN) signal.signal(signal.SIGINT, signal.SIG_IGN) raise KeyboardInterrupt(signame) class SystemctlConfigParser: """ A *.service files has a structure similar to an *.ini file but it is actually not like it. Settings may occur multiple times in each section and they create an implicit list. In reality all the settings are globally uniqute, so that an 'environment' can be printed without adding prefixes. Settings are continued with a backslash at the end of the line. """ def __init__(self, defaults=None, dict_type=None, allow_no_value=False): self._defaults = defaults or {} self._dict_type = dict_type or collections.OrderedDict self._allow_no_value = allow_no_value self._conf = self._dict_type() self._files = [] def defaults(self): return self._defaults def sections(self): return list(self._conf.keys()) def add_section(self, section): if section not in self._conf: self._conf[section] = self._dict_type() def has_section(self, section): return section in self._conf def has_option(self, section, option): if section not in self._conf: return False return option in self._conf[section] def set(self, section, option, value): if section not in self._conf: self._conf[section] = self._dict_type() if option not in self._conf[section]: self._conf[section][option] = [ value ] else: self._conf[section][option].append(value) if value is None: self._conf[section][option] = [] def get(self, section, option, default = None, allow_no_value = False): allow_no_value = allow_no_value or self._allow_no_value if section not in self._conf: if default is not None: return default if allow_no_value: return None logg.warning("section {} does not exist".format(section)) logg.warning(" have {}".format(self.sections())) raise AttributeError("section {} does not exist".format(section)) if option not in self._conf[section]: if default is not None: return default if allow_no_value: return None raise AttributeError("option {} in {} does not exist".format(option, section)) if not self._conf[section][option]: # i.e. an empty list if default is not None: return default if allow_no_value: return None raise AttributeError("option {} in {} is None".format(option, section)) return self._conf[section][option][0] # the first line in the list of configs def getlist(self, section, option, default = None, allow_no_value = False): allow_no_value = allow_no_value or self._allow_no_value if section not in self._conf: if default is not None: return default if allow_no_value: return [] logg.warning("section {} does not exist".format(section)) logg.warning(" have {}".format(self.sections())) raise AttributeError("section {} does not exist".format(section)) if option not in self._conf[section]: if default is not None: return default if allow_no_value: return [] raise AttributeError("option {} in {} does not exist".format(option, section)) return self._conf[section][option] # returns a list, possibly empty def read(self, filename): return self.read_sysd(filename) def read_sysd(self, filename): initscript = False initinfo = False section = None nextline = False name, text = "", "" if os.path.isfile(filename): self._files.append(filename) for orig_line in open(filename): if nextline: text += orig_line if text.rstrip().endswith("\\") or text.rstrip().endswith("\\\n"): text = text.rstrip() + "\n" else: self.set(section, name, text) nextline = False continue line = orig_line.strip() if not line: continue if line.startswith("#"): continue if line.startswith(";"): continue if line.startswith(".include"): logg.error("the '.include' syntax is deprecated. Use x.service.d/ drop-in files!") includefile = re.sub(r'^\.include[ ]*', '', line).rstrip() if not os.path.isfile(includefile): raise Exception("tried to include file that doesn't exist: %s" % includefile) self.read_sysd(includefile) continue if line.startswith("["): x = line.find("]") if x > 0: section = line[1:x] self.add_section(section) continue m = re.match(r"(\w+) *=(.*)", line) if not m: logg.warning("bad ini line: %s", line) raise Exception("bad ini line") name, text = m.group(1), m.group(2).strip() if text.endswith("\\") or text.endswith("\\\n"): nextline = True text = text + "\n" else: # hint: an empty line shall reset the value-list self.set(section, name, text and text or None) def read_sysv(self, filename): """ an LSB header is scanned and converted to (almost) equivalent settings of a SystemD ini-style input """ initscript = False initinfo = False section = None if os.path.isfile(filename): self._files.append(filename) for orig_line in open(filename): line = orig_line.strip() if line.startswith("#"): if " BEGIN INIT INFO" in line: initinfo = True section = "init.d" if " END INIT INFO" in line: initinfo = False if initinfo: m = re.match(r"\S+\s*(\w[\w_-]*):(.*)", line) if m: key, val = m.group(1), m.group(2).strip() self.set(section, key, val) continue description = self.get("init.d", "Description", "") if description: self.set("Unit", "Description", description) check = self.get("init.d", "Required-Start","") if check: for item in check.split(" "): if item.strip() in _sysv_mappings: self.set("Unit", "Requires", _sysv_mappings[item.strip()]) provides = self.get("init.d", "Provides", "") if provides: self.set("Install", "Alias", provides) # if already in multi-user.target then start it there. runlevels = self.get("init.d", "Default-Start","") if runlevels: for item in runlevels.split(" "): if item.strip() in _runlevel_mappings: self.set("Install", "WantedBy", _runlevel_mappings[item.strip()]) self.set("Service", "Type", "sysv") def filenames(self): return self._files # UnitConfParser = ConfigParser.RawConfigParser UnitConfParser = SystemctlConfigParser class SystemctlConf: def __init__(self, data, module = None): self.data = data # UnitConfParser self.env = {} self.status = None self.masked = None self.module = module self.drop_in_files = {} self._root = _root self._user_mode = _user_mode def os_path(self, path): return os_path(self._root, path) def os_path_var(self, path): if self._user_mode: return os_path(self._root, _var_path(path)) return os_path(self._root, path) def loaded(self): files = self.data.filenames() if self.masked: return "masked" if len(files): return "loaded" return "" def filename(self): """ returns the last filename that was parsed """ files = self.data.filenames() if files: return files[0] return None def overrides(self): """ drop-in files are loaded alphabetically by name, not by full path """ return [ self.drop_in_files[name] for name in sorted(self.drop_in_files) ] def name(self): """ the unit id or defaults to the file name """ name = self.module or "" filename = self.filename() if filename: name = os.path.basename(filename) return self.get("Unit", "Id", name) def set(self, section, name, value): return self.data.set(section, name, value) def get(self, section, name, default, allow_no_value = False): return self.data.get(section, name, default, allow_no_value) def getlist(self, section, name, default = None, allow_no_value = False): return self.data.getlist(section, name, default or [], allow_no_value) def getbool(self, section, name, default = None): value = self.data.get(section, name, default or "no") if value: if value[0] in "TtYy123456789": return True return False class PresetFile: def __init__(self): self._files = [] self._lines = [] def filename(self): """ returns the last filename that was parsed """ if self._files: return self._files[-1] return None def read(self, filename): self._files.append(filename) for line in open(filename): self._lines.append(line.strip()) return self def get_preset(self, unit): for line in self._lines: m = re.match(r"(enable|disable)\s+(\S+)", line) if m: status, pattern = m.group(1), m.group(2) if fnmatch.fnmatchcase(unit, pattern): logg.debug("%s %s => %s [%s]", status, pattern, unit, self.filename()) return status return None ## with waitlock(conf): self.start() class waitlock: def __init__(self, conf): self.conf = conf # currently unused self.opened = None self.lockfolder = conf.os_path_var(_notify_socket_folder) try: folder = self.lockfolder if not os.path.isdir(folder): os.makedirs(folder) except Exception as e: logg.warning("oops, %s", e) def lockfile(self): unit = "" if self.conf: unit = self.conf.name() return os.path.join(self.lockfolder, str(unit or "global") + ".lock") def __enter__(self): try: lockfile = self.lockfile() lockname = os.path.basename(lockfile) self.opened = os.open(lockfile, os.O_RDWR | os.O_CREAT, 0o600) for attempt in xrange(int(MaxLockWait or DefaultMaximumTimeout)): try: logg.debug("[%s] %s. trying %s _______ ", os.getpid(), attempt, lockname) fcntl.flock(self.opened, fcntl.LOCK_EX | fcntl.LOCK_NB) st = os.fstat(self.opened) if not st.st_nlink: logg.debug("[%s] %s. %s got deleted, trying again", os.getpid(), attempt, lockname) os.close(self.opened) self.opened = os.open(lockfile, os.O_RDWR | os.O_CREAT, 0o600) continue content = "{ 'systemctl': %s, 'lock': '%s' }\n" % (os.getpid(), lockname) os.write(self.opened, content.encode("utf-8")) logg.debug("[%s] %s. holding lock on %s", os.getpid(), attempt, lockname) return True except BlockingIOError as e: whom = os.read(self.opened, 4096) os.lseek(self.opened, 0, os.SEEK_SET) logg.info("[%s] %s. systemctl locked by %s", os.getpid(), attempt, whom.rstrip()) time.sleep(1) # until MaxLockWait continue logg.error("[%s] not able to get the lock to %s", os.getpid(), lockname) except Exception as e: logg.warning("[%s] oops %s, %s", os.getpid(), str(type(e)), e) #TODO# raise Exception("no lock for %s", self.unit or "global") return False def __exit__(self, type, value, traceback): try: os.lseek(self.opened, 0, os.SEEK_SET) os.ftruncate(self.opened, 0) if "removelockfile" in COVERAGE: # actually an optional implementation lockfile = self.lockfile() lockname = os.path.basename(lockfile) os.unlink(lockfile) # ino is kept allocated because opened by this process logg.debug("[%s] lockfile removed for %s", os.getpid(), lockname) fcntl.flock(self.opened, fcntl.LOCK_UN) os.close(self.opened) # implies an unlock but that has happend like 6 seconds later self.opened = None except Exception as e: logg.warning("oops, %s", e) def must_have_failed(waitpid, cmd): # found to be needed on ubuntu:16.04 to match test result from ubuntu:18.04 and other distros # .... I have tracked it down that python's os.waitpid() returns an exitcode==0 even when the # .... underlying process has actually failed with an exitcode<>0. It is unknown where that # .... bug comes from but it seems a bit serious to trash some very basic unix functionality. # .... Essentially a parent process does not get the correct exitcode from its own children. if cmd and cmd[0] == "/bin/kill": pid = None for arg in cmd[1:]: if not arg.startswith("-"): pid = arg if pid is None: # unknown $MAINPID if not waitpid.returncode: logg.error("waitpid %s did return %s => correcting as 11", cmd, waitpid.returncode) waitpidNEW = collections.namedtuple("waitpidNEW", ["pid", "returncode", "signal" ]) waitpid = waitpidNEW(waitpid.pid, 11, waitpid.signal) return waitpid def subprocess_waitpid(pid): waitpid = collections.namedtuple("waitpid", ["pid", "returncode", "signal" ]) run_pid, run_stat = os.waitpid(pid, 0) return waitpid(run_pid, os.WEXITSTATUS(run_stat), os.WTERMSIG(run_stat)) def subprocess_testpid(pid): testpid = collections.namedtuple("testpid", ["pid", "returncode", "signal" ]) run_pid, run_stat = os.waitpid(pid, os.WNOHANG) if run_pid: return testpid(run_pid, os.WEXITSTATUS(run_stat), os.WTERMSIG(run_stat)) else: return testpid(pid, None, 0) def parse_unit(name): # -> object(prefix, instance, suffix, ...., name, component) unit_name, suffix = name, "" has_suffix = name.rfind(".") if has_suffix > 0: unit_name = name[:has_suffix] suffix = name[has_suffix+1:] prefix, instance = unit_name, "" has_instance = unit_name.find("@") if has_instance > 0: prefix = unit_name[:has_instance] instance = unit_name[has_instance+1:] component = "" has_component = prefix.rfind("-") if has_component > 0: component = prefix[has_component+1:] UnitName = collections.namedtuple("UnitName", ["name", "prefix", "instance", "suffix", "component" ]) return UnitName(name, prefix, instance, suffix, component) def time_to_seconds(text, maximum = None): if maximum is None: maximum = DefaultMaximumTimeout value = 0 for part in str(text).split(" "): item = part.strip() if item == "infinity": return maximum if item.endswith("m"): try: value += 60 * int(item[:-1]) except: pass # pragma: no cover if item.endswith("min"): try: value += 60 * int(item[:-3]) except: pass # pragma: no cover elif item.endswith("ms"): try: value += int(item[:-2]) / 1000. except: pass # pragma: no cover elif item.endswith("s"): try: value += int(item[:-1]) except: pass # pragma: no cover elif item: try: value += int(item) except: pass # pragma: no cover if value > maximum: return maximum if not value: return 1 return value def seconds_to_time(seconds): seconds = float(seconds) mins = int(int(seconds) / 60) secs = int(int(seconds) - (mins * 60)) msecs = int(int(seconds * 1000) - (secs * 1000 + mins * 60000)) if mins and secs and msecs: return "%smin %ss %sms" % (mins, secs, msecs) elif mins and secs: return "%smin %ss" % (mins, secs) elif secs and msecs: return "%ss %sms" % (secs, msecs) elif mins and msecs: return "%smin %sms" % (mins, msecs) elif mins: return "%smin" % (mins) else: return "%ss" % (secs) def getBefore(conf): result = [] beforelist = conf.getlist("Unit", "Before", []) for befores in beforelist: for before in befores.split(" "): name = before.strip() if name and name not in result: result.append(name) return result def getAfter(conf): result = [] afterlist = conf.getlist("Unit", "After", []) for afters in afterlist: for after in afters.split(" "): name = after.strip() if name and name not in result: result.append(name) return result def compareAfter(confA, confB): idA = confA.name() idB = confB.name() for after in getAfter(confA): if after == idB: logg.debug("%s After %s", idA, idB) return -1 for after in getAfter(confB): if after == idA: logg.debug("%s After %s", idB, idA) return 1 for before in getBefore(confA): if before == idB: logg.debug("%s Before %s", idA, idB) return 1 for before in getBefore(confB): if before == idA: logg.debug("%s Before %s", idB, idA) return -1 return 0 def sortedAfter(conflist, cmp = compareAfter): # the normal sorted() does only look at two items # so if "A after C" and a list [A, B, C] then # it will see "A = B" and "B = C" assuming that # "A = C" and the list is already sorted. # # To make a totalsorted we have to create a marker # that informs sorted() that also B has a relation. # It only works when 'after' has a direction, so # anything without 'before' is a 'after'. In that # case we find that "B after C". class SortTuple: def __init__(self, rank, conf): self.rank = rank self.conf = conf sortlist = [ SortTuple(0, conf) for conf in conflist] for check in xrange(len(sortlist)): # maxrank = len(sortlist) changed = 0 for A in xrange(len(sortlist)): for B in xrange(len(sortlist)): if A != B: itemA = sortlist[A] itemB = sortlist[B] before = compareAfter(itemA.conf, itemB.conf) if before > 0 and itemA.rank <= itemB.rank: if DEBUG_AFTER: # pragma: no cover logg.info(" %-30s before %s", itemA.conf.name(), itemB.conf.name()) itemA.rank = itemB.rank + 1 changed += 1 if before < 0 and itemB.rank <= itemA.rank: if DEBUG_AFTER: # pragma: no cover logg.info(" %-30s before %s", itemB.conf.name(), itemA.conf.name()) itemB.rank = itemA.rank + 1 changed += 1 if not changed: if DEBUG_AFTER: # pragma: no cover logg.info("done in check %s of %s", check, len(sortlist)) break # because Requires is almost always the same as the After clauses # we are mostly done in round 1 as the list is in required order for conf in conflist: if DEBUG_AFTER: # pragma: no cover logg.debug(".. %s", conf.name()) for item in sortlist: if DEBUG_AFTER: # pragma: no cover logg.info("(%s) %s", item.rank, item.conf.name()) sortedlist = sorted(sortlist, key = lambda item: -item.rank) for item in sortedlist: if DEBUG_AFTER: # pragma: no cover logg.info("[%s] %s", item.rank, item.conf.name()) return [ item.conf for item in sortedlist ] class Systemctl: def __init__(self): # from command line options or the defaults self._extra_vars = _extra_vars self._force = _force self._full = _full self._init = _init self._no_ask_password = _no_ask_password self._no_legend = _no_legend self._now = _now self._preset_mode = _preset_mode self._quiet = _quiet self._root = _root self._show_all = _show_all self._unit_property = _unit_property self._unit_state = _unit_state self._unit_type = _unit_type # some common constants that may be changed self._systemd_version = SystemCompatibilityVersion self._pid_file_folder = _pid_file_folder self._journal_log_folder = _journal_log_folder # and the actual internal runtime state self._loaded_file_sysv = {} # /etc/init.d/name => config data self._loaded_file_sysd = {} # /etc/systemd/system/name.service => config data self._file_for_unit_sysv = None # name.service => /etc/init.d/name self._file_for_unit_sysd = None # name.service => /etc/systemd/system/name.service self._preset_file_list = None # /etc/systemd/system-preset/* => file content self._default_target = _default_target self._sysinit_target = None self.exit_when_no_more_procs = EXIT_WHEN_NO_MORE_PROCS or False self.exit_when_no_more_services = EXIT_WHEN_NO_MORE_SERVICES or False self._user_mode = _user_mode self._user_getlogin = os_getlogin() self._log_file = {} # init-loop self._log_hold = {} # init-loop def user(self): return self._user_getlogin def user_mode(self): return self._user_mode def user_folder(self): for folder in self.user_folders(): if folder: return folder raise Exception("did not find any systemd/user folder") def system_folder(self): for folder in self.system_folders(): if folder: return folder raise Exception("did not find any systemd/system folder") def init_folders(self): if _init_folder1: yield _init_folder1 if _init_folder2: yield _init_folder2 if _init_folder9: yield _init_folder9 def preset_folders(self): if _preset_folder1: yield _preset_folder1 if _preset_folder2: yield _preset_folder2 if _preset_folder3: yield _preset_folder3 if _preset_folder4: yield _preset_folder4 if _preset_folder9: yield _preset_folder9 def user_folders(self): if _user_folder1: yield os.path.expanduser(_user_folder1) if _user_folder2: yield os.path.expanduser(_user_folder2) if _user_folder3: yield os.path.expanduser(_user_folder3) if _user_folder4: yield os.path.expanduser(_user_folder4) if _user_folder9: yield os.path.expanduser(_user_folder9) def system_folders(self): if _system_folder1: yield _system_folder1 if _system_folder2: yield _system_folder2 if _system_folder3: yield _system_folder3 if _system_folder4: yield _system_folder4 if _system_folder9: yield _system_folder9 def sysd_folders(self): """ if --user then these folders are preferred """ if self.user_mode(): for folder in self.user_folders(): yield folder if True: for folder in self.system_folders(): yield folder def scan_unit_sysd_files(self, module = None): # -> [ unit-names,... ] """ reads all unit files, returns the first filename for the unit given """ if self._file_for_unit_sysd is None: self._file_for_unit_sysd = {} for folder in self.sysd_folders(): if not folder: continue folder = os_path(self._root, folder) if not os.path.isdir(folder): continue for name in os.listdir(folder): path = os.path.join(folder, name) if os.path.isdir(path): continue service_name = name if service_name not in self._file_for_unit_sysd: self._file_for_unit_sysd[service_name] = path logg.debug("found %s sysd files", len(self._file_for_unit_sysd)) return list(self._file_for_unit_sysd.keys()) def scan_unit_sysv_files(self, module = None): # -> [ unit-names,... ] """ reads all init.d files, returns the first filename when unit is a '.service' """ if self._file_for_unit_sysv is None: self._file_for_unit_sysv = {} for folder in self.init_folders(): if not folder: continue folder = os_path(self._root, folder) if not os.path.isdir(folder): continue for name in os.listdir(folder): path = os.path.join(folder, name) if os.path.isdir(path): continue service_name = name + ".service" # simulate systemd if service_name not in self._file_for_unit_sysv: self._file_for_unit_sysv[service_name] = path logg.debug("found %s sysv files", len(self._file_for_unit_sysv)) return list(self._file_for_unit_sysv.keys()) def unit_sysd_file(self, module = None): # -> filename? """ file path for the given module (systemd) """ self.scan_unit_sysd_files() if module and module in self._file_for_unit_sysd: return self._file_for_unit_sysd[module] if module and unit_of(module) in self._file_for_unit_sysd: return self._file_for_unit_sysd[unit_of(module)] return None def unit_sysv_file(self, module = None): # -> filename? """ file path for the given module (sysv) """ self.scan_unit_sysv_files() if module and module in self._file_for_unit_sysv: return self._file_for_unit_sysv[module] if module and unit_of(module) in self._file_for_unit_sysv: return self._file_for_unit_sysv[unit_of(module)] return None def unit_file(self, module = None): # -> filename? """ file path for the given module (sysv or systemd) """ path = self.unit_sysd_file(module) if path is not None: return path path = self.unit_sysv_file(module) if path is not None: return path return None def is_sysv_file(self, filename): """ for routines that have a special treatment for init.d services """ self.unit_file() # scan all if not filename: return None if filename in self._file_for_unit_sysd.values(): return False if filename in self._file_for_unit_sysv.values(): return True return None # not True def is_user_conf(self, conf): if not conf: return False # no such conf >> ignored filename = conf.filename() if filename and "/user/" in filename: return True return False def not_user_conf(self, conf): """ conf can not be started as user service (when --user)""" if not conf: return True # no such conf >> ignored if not self.user_mode(): logg.debug("%s no --user mode >> accept", conf.filename()) return False if self.is_user_conf(conf): logg.debug("%s is /user/ conf >> accept", conf.filename()) return False # to allow for 'docker run -u user' with system services user = self.expand_special(conf.get("Service", "User", ""), conf) if user and user == self.user(): logg.debug("%s with User=%s >> accept", conf.filename(), user) return False return True def find_drop_in_files(self, unit): """ search for some.service.d/extra.conf files """ result = {} basename_d = unit + ".d" for folder in self.sysd_folders(): if not folder: continue folder = os_path(self._root, folder) override_d = os_path(folder, basename_d) if not os.path.isdir(override_d): continue for name in os.listdir(override_d): path = os.path.join(override_d, name) if os.path.isdir(path): continue if not path.endswith(".conf"): continue if name not in result: result[name] = path return result def load_sysd_template_conf(self, module): # -> conf? """ read the unit template with a UnitConfParser (systemd) """ if "@" in module: unit = parse_unit(module) service = "%s@.service" % unit.prefix return self.load_sysd_unit_conf(service) return None def load_sysd_unit_conf(self, module): # -> conf? """ read the unit file with a UnitConfParser (systemd) """ path = self.unit_sysd_file(module) if not path: return None if path in self._loaded_file_sysd: return self._loaded_file_sysd[path] masked = None if os.path.islink(path) and os.readlink(path).startswith("/dev"): masked = os.readlink(path) drop_in_files = {} data = UnitConfParser() if not masked: data.read_sysd(path) drop_in_files = self.find_drop_in_files(os.path.basename(path)) # load in alphabetic order, irrespective of location for name in sorted(drop_in_files): path = drop_in_files[name] data.read_sysd(path) conf = SystemctlConf(data, module) conf.masked = masked conf.drop_in_files = drop_in_files conf._root = self._root self._loaded_file_sysd[path] = conf return conf def load_sysv_unit_conf(self, module): # -> conf? """ read the unit file with a UnitConfParser (sysv) """ path = self.unit_sysv_file(module) if not path: return None if path in self._loaded_file_sysv: return self._loaded_file_sysv[path] data = UnitConfParser() data.read_sysv(path) conf = SystemctlConf(data, module) conf._root = self._root self._loaded_file_sysv[path] = conf return conf def load_unit_conf(self, module): # -> conf | None(not-found) """ read the unit file with a UnitConfParser (sysv or systemd) """ try: conf = self.load_sysd_unit_conf(module) if conf is not None: return conf conf = self.load_sysd_template_conf(module) if conf is not None: return conf conf = self.load_sysv_unit_conf(module) if conf is not None: return conf except Exception as e: logg.warning("%s not loaded: %s", module, e) return None def default_unit_conf(self, module, description = None): # -> conf """ a unit conf that can be printed to the user where attributes are empty and loaded() is False """ data = UnitConfParser() data.set("Unit","Id", module) data.set("Unit", "Names", module) data.set("Unit", "Description", description or ("NOT-FOUND "+module)) # assert(not data.loaded()) conf = SystemctlConf(data, module) conf._root = self._root return conf def get_unit_conf(self, module): # -> conf (conf | default-conf) """ accept that a unit does not exist and return a unit conf that says 'not-loaded' """ conf = self.load_unit_conf(module) if conf is not None: return conf return self.default_unit_conf(module) def match_sysd_templates(self, modules = None, suffix=".service"): # -> generate[ unit ] """ make a file glob on all known template units (systemd areas). It returns no modules (!!) if no modules pattern were given. The module string should contain an instance name already. """ modules = to_list(modules) if not modules: return self.scan_unit_sysd_files() for item in sorted(self._file_for_unit_sysd.keys()): if "@" not in item: continue service_unit = parse_unit(item) for module in modules: if "@" not in module: continue module_unit = parse_unit(module) if service_unit.prefix == module_unit.prefix: yield "%s@%s.%s" % (service_unit.prefix, module_unit.instance, service_unit.suffix) def match_sysd_units(self, modules = None, suffix=".service"): # -> generate[ unit ] """ make a file glob on all known units (systemd areas). It returns all modules if no modules pattern were given. Also a single string as one module pattern may be given. """ modules = to_list(modules) self.scan_unit_sysd_files() for item in sorted(self._file_for_unit_sysd.keys()): if not modules: yield item elif [ module for module in modules if fnmatch.fnmatchcase(item, module) ]: yield item elif [ module for module in modules if module+suffix == item ]: yield item def match_sysv_units(self, modules = None, suffix=".service"): # -> generate[ unit ] """ make a file glob on all known units (sysv areas). It returns all modules if no modules pattern were given. Also a single string as one module pattern may be given. """ modules = to_list(modules) self.scan_unit_sysv_files() for item in sorted(self._file_for_unit_sysv.keys()): if not modules: yield item elif [ module for module in modules if fnmatch.fnmatchcase(item, module) ]: yield item elif [ module for module in modules if module+suffix == item ]: yield item def match_units(self, modules = None, suffix=".service"): # -> [ units,.. ] """ Helper for about any command with multiple units which can actually be glob patterns on their respective unit name. It returns all modules if no modules pattern were given. Also a single string as one module pattern may be given. """ found = [] for unit in self.match_sysd_units(modules, suffix): if unit not in found: found.append(unit) for unit in self.match_sysd_templates(modules, suffix): if unit not in found: found.append(unit) for unit in self.match_sysv_units(modules, suffix): if unit not in found: found.append(unit) return found def list_service_unit_basics(self): """ show all the basic loading state of services """ filename = self.unit_file() # scan all result = [] for name, value in self._file_for_unit_sysd.items(): result += [ (name, "SysD", value) ] for name, value in self._file_for_unit_sysv.items(): result += [ (name, "SysV", value) ] return result def list_service_units(self, *modules): # -> [ (unit,loaded+active+substate,description) ] """ show all the service units """ result = {} active = {} substate = {} description = {} for unit in self.match_units(modules): result[unit] = "not-found" active[unit] = "inactive" substate[unit] = "dead" description[unit] = "" try: conf = self.get_unit_conf(unit) result[unit] = "loaded" description[unit] = self.get_description_from(conf) active[unit] = self.get_active_from(conf) substate[unit] = self.get_substate_from(conf) except Exception as e: logg.warning("list-units: %s", e) if self._unit_state: if self._unit_state not in [ result[unit], active[unit], substate[unit] ]: del result[unit] return [ (unit, result[unit] + " " + active[unit] + " " + substate[unit], description[unit]) for unit in sorted(result) ] def show_list_units(self, *modules): # -> [ (unit,loaded,description) ] """ [PATTERN]... -- List loaded units. If one or more PATTERNs are specified, only units matching one of them are shown. NOTE: This is the default command.""" hint = "To show all installed unit files use 'systemctl list-unit-files'." result = self.list_service_units(*modules) if self._no_legend: return result found = "%s loaded units listed." % len(result) return result + [ "", found, hint ] def list_service_unit_files(self, *modules): # -> [ (unit,enabled) ] """ show all the service units and the enabled status""" logg.debug("list service unit files for %s", modules) result = {} enabled = {} for unit in self.match_units(modules): result[unit] = None enabled[unit] = "" try: conf = self.get_unit_conf(unit) if self.not_user_conf(conf): result[unit] = None continue result[unit] = conf enabled[unit] = self.enabled_from(conf) except Exception as e: logg.warning("list-units: %s", e) return [ (unit, enabled[unit]) for unit in sorted(result) if result[unit] ] def each_target_file(self): folders = self.system_folders() if self.user_mode(): folders = self.user_folders() for folder in folders: if not os.path.isdir(folder): continue for filename in os.listdir(folder): if filename.endswith(".target"): yield (filename, os.path.join(folder, filename)) def list_target_unit_files(self, *modules): # -> [ (unit,enabled) ] """ show all the target units and the enabled status""" enabled = {} targets = {} for target, filepath in self.each_target_file(): logg.info("target %s", filepath) targets[target] = filepath enabled[target] = "static" for unit in _all_common_targets: targets[unit] = None enabled[unit] = "static" if unit in _all_common_enabled: enabled[unit] = "enabled" if unit in _all_common_disabled: enabled[unit] = "disabled" return [ (unit, enabled[unit]) for unit in sorted(targets) ] def show_list_unit_files(self, *modules): # -> [ (unit,enabled) ] """[PATTERN]... -- List installed unit files List installed unit files and their enablement state (as reported by is-enabled). If one or more PATTERNs are specified, only units whose filename (just the last component of the path) matches one of them are shown. This command reacts to limitations of --type being --type=service or --type=target (and --now for some basics).""" if self._now: result = self.list_service_unit_basics() elif self._unit_type == "target": result = self.list_target_unit_files() elif self._unit_type == "service": result = self.list_service_unit_files() elif self._unit_type: logg.warning("unsupported unit --type=%s", self._unit_type) result = [] else: result = self.list_target_unit_files() result += self.list_service_unit_files(*modules) if self._no_legend: return result found = "%s unit files listed." % len(result) return [ ("UNIT FILE", "STATE") ] + result + [ "", found ] ## ## def get_description(self, unit, default = None): return self.get_description_from(self.load_unit_conf(unit)) def get_description_from(self, conf, default = None): # -> text """ Unit.Description could be empty sometimes """ if not conf: return default or "" description = conf.get("Unit", "Description", default or "") return self.expand_special(description, conf) def read_pid_file(self, pid_file, default = None): pid = default if not pid_file: return default if not os.path.isfile(pid_file): return default if self.truncate_old(pid_file): return default try: # some pid-files from applications contain multiple lines for line in open(pid_file): if line.strip(): pid = to_int(line.strip()) break except Exception as e: logg.warning("bad read of pid file '%s': %s", pid_file, e) return pid def wait_pid_file(self, pid_file, timeout = None): # -> pid? """ wait some seconds for the pid file to appear and return the pid """ timeout = int(timeout or (DefaultTimeoutStartSec/2)) timeout = max(timeout, (MinimumTimeoutStartSec)) dirpath = os.path.dirname(os.path.abspath(pid_file)) for x in xrange(timeout): if not os.path.isdir(dirpath): time.sleep(1) # until TimeoutStartSec/2 continue pid = self.read_pid_file(pid_file) if not pid: time.sleep(1) # until TimeoutStartSec/2 continue if not pid_exists(pid): time.sleep(1) # until TimeoutStartSec/2 continue return pid return None def test_pid_file(self, unit): # -> text """ support for the testsuite.py """ conf = self.get_unit_conf(unit) return self.pid_file_from(conf) or self.status_file_from(conf) def pid_file_from(self, conf, default = ""): """ get the specified pid file path (not a computed default) """ pid_file = conf.get("Service", "PIDFile", default) return self.expand_special(pid_file, conf) def read_mainpid_from(self, conf, default): """ MAINPID is either the PIDFile content written from the application or it is the value in the status file written by this systemctl.py code """ pid_file = self.pid_file_from(conf) if pid_file: return self.read_pid_file(pid_file, default) status = self.read_status_from(conf) return status.get("MainPID", default) def clean_pid_file_from(self, conf): pid_file = self.pid_file_from(conf) if pid_file and os.path.isfile(pid_file): try: os.remove(pid_file) except OSError as e: logg.warning("while rm %s: %s", pid_file, e) self.write_status_from(conf, MainPID=None) def get_status_file(self, unit): # for testing conf = self.get_unit_conf(unit) return self.status_file_from(conf) def status_file_from(self, conf, default = None): if default is None: default = self.default_status_file(conf) if conf is None: return default status_file = conf.get("Service", "StatusFile", default) # this not a real setting, but do the expand_special anyway return self.expand_special(status_file, conf) def default_status_file(self, conf): # -> text """ default file pattern where to store a status mark """ folder = conf.os_path_var(self._pid_file_folder) name = "%s.status" % conf.name() return os.path.join(folder, name) def clean_status_from(self, conf): status_file = self.status_file_from(conf) if os.path.exists(status_file): os.remove(status_file) conf.status = {} def write_status_from(self, conf, **status): # -> bool(written) """ if a status_file is known then path is created and the give status is written as the only content. """ status_file = self.status_file_from(conf) if not status_file: logg.debug("status %s but no status_file", conf.name()) return False dirpath = os.path.dirname(os.path.abspath(status_file)) if not os.path.isdir(dirpath): os.makedirs(dirpath) if conf.status is None: conf.status = self.read_status_from(conf) if True: for key in sorted(status.keys()): value = status[key] if key.upper() == "AS": key = "ActiveState" if key.upper() == "EXIT": key = "ExecMainCode" if value is None: try: del conf.status[key] except KeyError: pass else: conf.status[key] = value try: with open(status_file, "w") as f: for key in sorted(conf.status): value = conf.status[key] if key == "MainPID" and str(value) == "0": logg.warning("ignore writing MainPID=0") continue content = "{}={}\n".format(key, str(value)) logg.debug("writing to %s\n\t%s", status_file, content.strip()) f.write(content) except IOError as e: logg.error("writing STATUS %s: %s\n\t to status file %s", status, e, status_file) return True def read_status_from(self, conf, defaults = None): status_file = self.status_file_from(conf) status = {} if hasattr(defaults, "keys"): for key in defaults.keys(): status[key] = defaults[key] elif isinstance(defaults, string_types): status["ActiveState"] = defaults if not status_file: logg.debug("no status file. returning %s", status) return status if not os.path.isfile(status_file): logg.debug("no status file: %s\n returning %s", status_file, status) return status if self.truncate_old(status_file): logg.debug("old status file: %s\n returning %s", status_file, status) return status try: logg.debug("reading %s", status_file) for line in open(status_file): if line.strip(): m = re.match(r"(\w+)[:=](.*)", line) if m: key, value = m.group(1), m.group(2) if key.strip(): status[key.strip()] = value.strip() elif line in [ "active", "inactive", "failed"]: status["ActiveState"] = line else: logg.warning("ignored %s", line.strip()) except: logg.warning("bad read of status file '%s'", status_file) return status def get_status_from(self, conf, name, default = None): if conf.status is None: conf.status = self.read_status_from(conf) return conf.status.get(name, default) def set_status_from(self, conf, name, value): if conf.status is None: conf.status = self.read_status_from(conf) if value is None: try: del conf.status[name] except KeyError: pass else: conf.status[name] = value # def wait_boot(self, hint = None): booted = self.get_boottime() while True: now = time.time() if booted + EpsilonTime <= now: break time.sleep(EpsilonTime) logg.info(" %s ................. boot sleep %ss", hint or "", EpsilonTime) def get_boottime(self): if "oldest" in COVERAGE: return self.get_boottime_oldest() for pid in xrange(10): proc = "/proc/%s/status" % pid try: if os.path.exists(proc): return os.path.getmtime(proc) except Exception as e: # pragma: nocover logg.warning("could not access %s: %s", proc, e) return self.get_boottime_oldest() def get_boottime_oldest(self): # otherwise get the oldest entry in /proc booted = time.time() for name in os.listdir("/proc"): proc = "/proc/%s/status" % name try: if os.path.exists(proc): ctime = os.path.getmtime(proc) if ctime < booted: booted = ctime except Exception as e: # pragma: nocover logg.warning("could not access %s: %s", proc, e) return booted def get_filetime(self, filename): return os.path.getmtime(filename) def truncate_old(self, filename): filetime = self.get_filetime(filename) boottime = self.get_boottime() if isinstance(filetime, float): filetime -= EpsilonTime if filetime >= boottime : logg.debug(" file time: %s", datetime.datetime.fromtimestamp(filetime)) logg.debug(" boot time: %s", datetime.datetime.fromtimestamp(boottime)) return False # OK logg.info("truncate old %s", filename) logg.info(" file time: %s", datetime.datetime.fromtimestamp(filetime)) logg.info(" boot time: %s", datetime.datetime.fromtimestamp(boottime)) try: shutil_truncate(filename) except Exception as e: logg.warning("while truncating: %s", e) return True # truncated def getsize(self, filename): if not filename: return 0 if not os.path.isfile(filename): return 0 if self.truncate_old(filename): return 0 try: return os.path.getsize(filename) except Exception as e: logg.warning("while reading file size: %s\n of %s", e, filename) return 0 # def read_env_file(self, env_file): # -> generate[ (name,value) ] """ EnvironmentFile= is being scanned """ if env_file.startswith("-"): env_file = env_file[1:] if not os.path.isfile(os_path(self._root, env_file)): return try: for real_line in open(os_path(self._root, env_file)): line = real_line.strip() if not line or line.startswith("#"): continue m = re.match(r"(?:export +)?([\w_]+)[=]'([^']*)'", line) if m: yield m.group(1), m.group(2) continue m = re.match(r'(?:export +)?([\w_]+)[=]"([^"]*)"', line) if m: yield m.group(1), m.group(2) continue m = re.match(r'(?:export +)?([\w_]+)[=](.*)', line) if m: yield m.group(1), m.group(2) continue except Exception as e: logg.info("while reading %s: %s", env_file, e) def read_env_part(self, env_part): # -> generate[ (name, value) ] """ Environment== is being scanned """ ## systemd Environment= spec says it is a space-seperated list of ## assignments. In order to use a space or an equals sign in a value ## one should enclose the whole assignment with double quotes: ## Environment="VAR1=word word" VAR2=word3 "VAR3=$word 5 6" ## and the $word is not expanded by other environment variables. try: for real_line in env_part.split("\n"): line = real_line.strip() for found in re.finditer(r'\s*("[\w_]+=[^"]*"|[\w_]+=\S*)', line): part = found.group(1) if part.startswith('"'): part = part[1:-1] name, value = part.split("=", 1) yield name, value except Exception as e: logg.info("while reading %s: %s", env_part, e) def show_environment(self, unit): """ [UNIT]. -- show environment parts """ conf = self.load_unit_conf(unit) if conf is None: logg.error("Unit %s could not be found.", unit) return False if _unit_property: return conf.getlist("Service", _unit_property) return self.get_env(conf) def extra_vars(self): return self._extra_vars # from command line def get_env(self, conf): env = os.environ.copy() for env_part in conf.getlist("Service", "Environment", []): for name, value in self.read_env_part(self.expand_special(env_part, conf)): env[name] = value # a '$word' is not special here for env_file in conf.getlist("Service", "EnvironmentFile", []): for name, value in self.read_env_file(self.expand_special(env_file, conf)): env[name] = self.expand_env(value, env) logg.debug("extra-vars %s", self.extra_vars()) for extra in self.extra_vars(): if extra.startswith("@"): for name, value in self.read_env_file(extra[1:]): logg.info("override %s=%s", name, value) env[name] = self.expand_env(value, env) else: for name, value in self.read_env_part(extra): logg.info("override %s=%s", name, value) env[name] = value # a '$word' is not special here return env def expand_env(self, cmd, env): def get_env1(m): if m.group(1) in env: return env[m.group(1)] logg.debug("can not expand $%s", m.group(1)) return "" # empty string def get_env2(m): if m.group(1) in env: return env[m.group(1)] logg.debug("can not expand ${%s}", m.group(1)) return "" # empty string # maxdepth = 20 expanded = re.sub("[$](\w+)", lambda m: get_env1(m), cmd.replace("\\\n","")) for depth in xrange(maxdepth): new_text = re.sub("[$][{](\w+)[}]", lambda m: get_env2(m), expanded) if new_text == expanded: return expanded expanded = new_text logg.error("shell variable expansion exceeded maxdepth %s", maxdepth) return expanded def expand_special(self, cmd, conf = None): """ expand %i %t and similar special vars. They are being expanded before any other expand_env takes place which handles shell-style $HOME references. """ def sh_escape(value): return "'" + value.replace("'","\\'") + "'" def get_confs(conf): confs={ "%": "%" } if not conf: return confs unit = parse_unit(conf.name()) confs["N"] = unit.name confs["n"] = sh_escape(unit.name) confs["P"] = unit.prefix confs["p"] = sh_escape(unit.prefix) confs["I"] = unit.instance confs["i"] = sh_escape(unit.instance) confs["J"] = unit.component confs["j"] = sh_escape(unit.component) confs["f"] = sh_escape(conf.filename()) VARTMP = "/var/tmp" TMP = "/tmp" RUN = "/run" DAT = "/var/lib" LOG = "/var/log" CACHE = "/var/cache" CONFIG = "/etc" HOME = "/root" USER = "root" UID = 0 SHELL = "/bin/sh" if self.is_user_conf(conf): USER = os_getlogin() HOME = get_home() RUN = os.environ.get("XDG_RUNTIME_DIR", get_runtime_dir()) CONFIG = os.environ.get("XDG_CONFIG_HOME", HOME + "/.config") CACHE = os.environ.get("XDG_CACHE_HOME", HOME + "/.cache") SHARE = os.environ.get("XDG_DATA_HOME", HOME + "/.local/share") DAT = CONFIG LOG = os.path.join(CONFIG, "log") SHELL = os.environ.get("SHELL", SHELL) VARTMP = os.environ.get("TMPDIR", os.environ.get("TEMP", os.environ.get("TMP", VARTMP))) TMP = os.environ.get("TMPDIR", os.environ.get("TEMP", os.environ.get("TMP", TMP))) confs["V"] = os_path(self._root, VARTMP) confs["T"] = os_path(self._root, TMP) confs["t"] = os_path(self._root, RUN) confs["S"] = os_path(self._root, DAT) confs["s"] = SHELL confs["h"] = HOME confs["u"] = USER confs["C"] = os_path(self._root, CACHE) confs["E"] = os_path(self._root, CONFIG) return confs def get_conf1(m): confs = get_confs(conf) if m.group(1) in confs: return confs[m.group(1)] logg.warning("can not expand %%%s", m.group(1)) return "''" # empty escaped string return re.sub("[%](.)", lambda m: get_conf1(m), cmd) def exec_cmd(self, cmd, env, conf = None): """ expand ExecCmd statements including %i and $MAINPID """ cmd1 = cmd.replace("\\\n","") # according to documentation the %n / %% need to be expanded where in # most cases they are shell-escaped values. So we do it before shlex. cmd2 = self.expand_special(cmd1, conf) # according to documentation, when bar="one two" then the expansion # of '$bar' is ["one","two"] and '${bar}' becomes ["one two"]. We # tackle that by expand $bar before shlex, and the rest thereafter. def get_env1(m): if m.group(1) in env: return env[m.group(1)] logg.debug("can not expand $%s", m.group(1)) return "" # empty string def get_env2(m): if m.group(1) in env: return env[m.group(1)] logg.debug("can not expand ${%s}", m.group(1)) return "" # empty string cmd3 = re.sub("[$](\w+)", lambda m: get_env1(m), cmd2) newcmd = [] for part in shlex.split(cmd3): newcmd += [ re.sub("[$][{](\w+)[}]", lambda m: get_env2(m), part) ] return newcmd def path_journal_log(self, conf): # never None """ /var/log/zzz.service.log or /var/log/default.unit.log """ filename = os.path.basename(conf.filename() or "") unitname = (conf.name() or "default")+".unit" name = filename or unitname log_folder = conf.os_path_var(self._journal_log_folder) log_file = name.replace(os.path.sep,".") + ".log" if log_file.startswith("."): log_file = "dot."+log_file return os.path.join(log_folder, log_file) def open_journal_log(self, conf): log_file = self.path_journal_log(conf) log_folder = os.path.dirname(log_file) if not os.path.isdir(log_folder): os.makedirs(log_folder) return open(os.path.join(log_file), "a") def chdir_workingdir(self, conf): """ if specified then change the working directory """ # the original systemd will start in '/' even if User= is given if self._root: os.chdir(self._root) workingdir = conf.get("Service", "WorkingDirectory", "") if workingdir: ignore = False if workingdir.startswith("-"): workingdir = workingdir[1:] ignore = True into = os_path(self._root, self.expand_special(workingdir, conf)) try: logg.debug("chdir workingdir '%s'", into) os.chdir(into) return False except Exception as e: if not ignore: logg.error("chdir workingdir '%s': %s", into, e) return into else: logg.debug("chdir workingdir '%s': %s", into, e) return None return None def notify_socket_from(self, conf, socketfile = None): """ creates a notify-socket for the (non-privileged) user """ NotifySocket = collections.namedtuple("NotifySocket", ["socket", "socketfile" ]) notify_socket_folder = conf.os_path_var(_notify_socket_folder) notify_name = "notify." + str(conf.name() or "systemctl") notify_socket = os.path.join(notify_socket_folder, notify_name) socketfile = socketfile or notify_socket if len(socketfile) > 100: logg.debug("https://unix.stackexchange.com/questions/367008/%s", "why-is-socket-path-length-limited-to-a-hundred-chars") logg.debug("old notify socketfile (%s) = %s", len(socketfile), socketfile) notify_socket_folder = re.sub("^(/var)?", get_runtime_dir(), _notify_socket_folder) notify_name = notify_name[0:min(100-len(notify_socket_folder),len(notify_name))] socketfile = os.path.join(notify_socket_folder, notify_name) # occurs during testsuite.py for ~user/test.tmp/root path logg.info("new notify socketfile (%s) = %s", len(socketfile), socketfile) try: if not os.path.isdir(os.path.dirname(socketfile)): os.makedirs(os.path.dirname(socketfile)) if os.path.exists(socketfile): os.unlink(socketfile) except Exception as e: logg.warning("error %s: %s", socketfile, e) sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) sock.bind(socketfile) os.chmod(socketfile, 0o777) # the service my run under some User=setting return NotifySocket(sock, socketfile) def read_notify_socket(self, notify, timeout): notify.socket.settimeout(timeout or DefaultMaximumTimeout) result = "" try: result, client_address = notify.socket.recvfrom(4096) if result: result = result.decode("utf-8") result_txt = result.replace("\n","|") result_len = len(result) logg.debug("read_notify_socket(%s):%s", result_len, result_txt) except socket.timeout as e: if timeout > 2: logg.debug("socket.timeout %s", e) return result def wait_notify_socket(self, notify, timeout, pid = None): if not os.path.exists(notify.socketfile): logg.info("no $NOTIFY_SOCKET exists") return {} # logg.info("wait $NOTIFY_SOCKET, timeout %s", timeout) results = {} seenREADY = None for attempt in xrange(timeout+1): if pid and not self.is_active_pid(pid): logg.info("dead PID %s", pid) return results if not attempt: # first one time.sleep(1) # until TimeoutStartSec continue result = self.read_notify_socket(notify, 1) # sleep max 1 second if not result: # timeout time.sleep(1) # until TimeoutStartSec continue for name, value in self.read_env_part(result): results[name] = value if name == "READY": seenREADY = value if name in ["STATUS", "ACTIVESTATE"]: logg.debug("%s: %s", name, value) # TODO: update STATUS -> SubState if seenREADY: break if not seenREADY: logg.info(".... timeout while waiting for 'READY=1' status on $NOTIFY_SOCKET") logg.debug("notify = %s", results) try: notify.socket.close() except Exception as e: logg.debug("socket.close %s", e) return results def start_modules(self, *modules): """ [UNIT]... -- start these units /// SPECIAL: with --now or --init it will run the init-loop and stop the units afterwards """ found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] init = self._now or self._init return self.start_units(units, init) and found_all def start_units(self, units, init = None): """ fails if any unit does not start /// SPECIAL: may run the init-loop and stop the named units afterwards """ self.wait_system() done = True started_units = [] for unit in self.sortedAfter(units): started_units.append(unit) if not self.start_unit(unit): done = False if init: logg.info("init-loop start") sig = self.init_loop_until_stop(started_units) logg.info("init-loop %s", sig) for unit in reversed(started_units): self.stop_unit(unit) return done def start_unit(self, unit): conf = self.load_unit_conf(unit) if conf is None: logg.debug("unit could not be loaded (%s)", unit) logg.error("Unit %s could not be found.", unit) return False if self.not_user_conf(conf): logg.error("Unit %s not for --user mode", unit) return False return self.start_unit_from(conf) def get_TimeoutStartSec(self, conf): timeout = conf.get("Service", "TimeoutSec", DefaultTimeoutStartSec) timeout = conf.get("Service", "TimeoutStartSec", timeout) return time_to_seconds(timeout, DefaultMaximumTimeout) def start_unit_from(self, conf): if not conf: return False if self.syntax_check(conf) > 100: return False with waitlock(conf): logg.debug(" start unit %s => %s", conf.name(), conf.filename()) return self.do_start_unit_from(conf) def do_start_unit_from(self, conf): timeout = self.get_TimeoutStartSec(conf) doRemainAfterExit = conf.getbool("Service", "RemainAfterExit", "no") runs = conf.get("Service", "Type", "simple").lower() env = self.get_env(conf) self.exec_check_service(conf, env, "Exec") # all... # for StopPost on failure: returncode = 0 service_result = "success" if True: if runs in [ "simple", "forking", "notify" ]: env["MAINPID"] = str(self.read_mainpid_from(conf, "")) for cmd in conf.getlist("Service", "ExecStartPre", []): check, cmd = checkstatus(cmd) newcmd = self.exec_cmd(cmd, env, conf) logg.info(" pre-start %s", shell_cmd(newcmd)) forkpid = os.fork() if not forkpid: self.execve_from(conf, newcmd, env) # pragma: nocover run = subprocess_waitpid(forkpid) logg.debug(" pre-start done (%s) <-%s>", run.returncode or "OK", run.signal or "") if run.returncode and check: logg.error("the ExecStartPre control process exited with error code") active = "failed" self.write_status_from(conf, AS=active ) return False if runs in [ "sysv" ]: status_file = self.status_file_from(conf) if True: exe = conf.filename() cmd = "'%s' start" % exe env["SYSTEMCTL_SKIP_REDIRECT"] = "yes" newcmd = self.exec_cmd(cmd, env, conf) logg.info("%s start %s", runs, shell_cmd(newcmd)) forkpid = os.fork() if not forkpid: # pragma: no cover os.setsid() # detach child process from parent self.execve_from(conf, newcmd, env) run = subprocess_waitpid(forkpid) self.set_status_from(conf, "ExecMainCode", run.returncode) logg.info("%s start done (%s) <-%s>", runs, run.returncode or "OK", run.signal or "") active = run.returncode and "failed" or "active" self.write_status_from(conf, AS=active ) return True elif runs in [ "oneshot" ]: status_file = self.status_file_from(conf) if self.get_status_from(conf, "ActiveState", "unknown") == "active": logg.warning("the service was already up once") return True for cmd in conf.getlist("Service", "ExecStart", []): check, cmd = checkstatus(cmd) newcmd = self.exec_cmd(cmd, env, conf) logg.info("%s start %s", runs, shell_cmd(newcmd)) forkpid = os.fork() if not forkpid: # pragma: no cover os.setsid() # detach child process from parent self.execve_from(conf, newcmd, env) run = subprocess_waitpid(forkpid) if run.returncode and check: returncode = run.returncode service_result = "failed" logg.error("%s start %s (%s) <-%s>", runs, service_result, run.returncode or "OK", run.signal or "") break logg.info("%s start done (%s) <-%s>", runs, run.returncode or "OK", run.signal or "") if True: self.set_status_from(conf, "ExecMainCode", returncode) active = returncode and "failed" or "active" self.write_status_from(conf, AS=active) elif runs in [ "simple" ]: status_file = self.status_file_from(conf) pid = self.read_mainpid_from(conf, "") if self.is_active_pid(pid): logg.warning("the service is already running on PID %s", pid) return True if doRemainAfterExit: logg.debug("%s RemainAfterExit -> AS=active", runs) self.write_status_from(conf, AS="active") cmdlist = conf.getlist("Service", "ExecStart", []) for idx, cmd in enumerate(cmdlist): logg.debug("ExecStart[%s]: %s", idx, cmd) for cmd in cmdlist: pid = self.read_mainpid_from(conf, "") env["MAINPID"] = str(pid) newcmd = self.exec_cmd(cmd, env, conf) logg.info("%s start %s", runs, shell_cmd(newcmd)) forkpid = os.fork() if not forkpid: # pragma: no cover os.setsid() # detach child process from parent self.execve_from(conf, newcmd, env) self.write_status_from(conf, MainPID=forkpid) logg.info("%s started PID %s", runs, forkpid) env["MAINPID"] = str(forkpid) time.sleep(MinimumYield) run = subprocess_testpid(forkpid) if run.returncode is not None: logg.info("%s stopped PID %s (%s) <-%s>", runs, run.pid, run.returncode or "OK", run.signal or "") if doRemainAfterExit: self.set_status_from(conf, "ExecMainCode", run.returncode) active = run.returncode and "failed" or "active" self.write_status_from(conf, AS=active) if run.returncode: service_result = "failed" break elif runs in [ "notify" ]: # "notify" is the same as "simple" but we create a $NOTIFY_SOCKET # and wait for startup completion by checking the socket messages pid = self.read_mainpid_from(conf, "") if self.is_active_pid(pid): logg.error("the service is already running on PID %s", pid) return False notify = self.notify_socket_from(conf) if notify: env["NOTIFY_SOCKET"] = notify.socketfile logg.debug("use NOTIFY_SOCKET=%s", notify.socketfile) if doRemainAfterExit: logg.debug("%s RemainAfterExit -> AS=active", runs) self.write_status_from(conf, AS="active") cmdlist = conf.getlist("Service", "ExecStart", []) for idx, cmd in enumerate(cmdlist): logg.debug("ExecStart[%s]: %s", idx, cmd) mainpid = None for cmd in cmdlist: mainpid = self.read_mainpid_from(conf, "") env["MAINPID"] = str(mainpid) newcmd = self.exec_cmd(cmd, env, conf) logg.info("%s start %s", runs, shell_cmd(newcmd)) forkpid = os.fork() if not forkpid: # pragma: no cover os.setsid() # detach child process from parent self.execve_from(conf, newcmd, env) # via NOTIFY # self.write_status_from(conf, MainPID=forkpid) logg.info("%s started PID %s", runs, forkpid) mainpid = forkpid self.write_status_from(conf, MainPID=mainpid) env["MAINPID"] = str(mainpid) time.sleep(MinimumYield) run = subprocess_testpid(forkpid) if run.returncode is not None: logg.info("%s stopped PID %s (%s) <-%s>", runs, run.pid, run.returncode or "OK", run.signal or "") if doRemainAfterExit: self.set_status_from(conf, "ExecMainCode", run.returncode or 0) active = run.returncode and "failed" or "active" self.write_status_from(conf, AS=active) if run.returncode: service_result = "failed" break if service_result in [ "success" ] and mainpid: logg.debug("okay, wating on socket for %ss", timeout) results = self.wait_notify_socket(notify, timeout, mainpid) if "MAINPID" in results: new_pid = results["MAINPID"] if new_pid and to_int(new_pid) != mainpid: logg.info("NEW PID %s from sd_notify (was PID %s)", new_pid, mainpid) self.write_status_from(conf, MainPID=new_pid) mainpid = new_pid logg.info("%s start done %s", runs, mainpid) pid = self.read_mainpid_from(conf, "") if pid: env["MAINPID"] = str(pid) else: service_result = "timeout" # "could not start service" elif runs in [ "forking" ]: pid_file = self.pid_file_from(conf) for cmd in conf.getlist("Service", "ExecStart", []): check, cmd = checkstatus(cmd) newcmd = self.exec_cmd(cmd, env, conf) if not newcmd: continue logg.info("%s start %s", runs, shell_cmd(newcmd)) forkpid = os.fork() if not forkpid: # pragma: no cover os.setsid() # detach child process from parent self.execve_from(conf, newcmd, env) logg.info("%s started PID %s", runs, forkpid) run = subprocess_waitpid(forkpid) if run.returncode and check: returncode = run.returncode service_result = "failed" logg.info("%s stopped PID %s (%s) <-%s>", runs, run.pid, run.returncode or "OK", run.signal or "") if pid_file and service_result in [ "success" ]: pid = self.wait_pid_file(pid_file) # application PIDFile logg.info("%s start done PID %s [%s]", runs, pid, pid_file) if pid: env["MAINPID"] = str(pid) if not pid_file: time.sleep(MinimumTimeoutStartSec) logg.warning("No PIDFile for forking %s", conf.filename()) status_file = self.status_file_from(conf) self.set_status_from(conf, "ExecMainCode", returncode) active = returncode and "failed" or "active" self.write_status_from(conf, AS=active) else: logg.error("unsupported run type '%s'", runs) return False # POST sequence active = self.is_active_from(conf) if not active: logg.warning("%s start not active", runs) # according to the systemd documentation, a failed start-sequence # should execute the ExecStopPost sequence allowing some cleanup. env["SERVICE_RESULT"] = service_result for cmd in conf.getlist("Service", "ExecStopPost", []): check, cmd = checkstatus(cmd) newcmd = self.exec_cmd(cmd, env, conf) logg.info("post-fail %s", shell_cmd(newcmd)) forkpid = os.fork() if not forkpid: self.execve_from(conf, newcmd, env) # pragma: nocover run = subprocess_waitpid(forkpid) logg.debug("post-fail done (%s) <-%s>", run.returncode or "OK", run.signal or "") return False else: for cmd in conf.getlist("Service", "ExecStartPost", []): check, cmd = checkstatus(cmd) newcmd = self.exec_cmd(cmd, env, conf) logg.info("post-start %s", shell_cmd(newcmd)) forkpid = os.fork() if not forkpid: self.execve_from(conf, newcmd, env) # pragma: nocover run = subprocess_waitpid(forkpid) logg.debug("post-start done (%s) <-%s>", run.returncode or "OK", run.signal or "") return True def extend_exec_env(self, env): env = env.copy() # implant DefaultPath into $PATH path = env.get("PATH", DefaultPath) parts = path.split(os.pathsep) for part in DefaultPath.split(os.pathsep): if part and part not in parts: parts.append(part) env["PATH"] = str(os.pathsep).join(parts) # reset locale to system default for name in ResetLocale: if name in env: del env[name] locale = {} for var, val in self.read_env_file("/etc/locale.conf"): locale[var] = val env[var] = val if "LANG" not in locale: env["LANG"] = locale.get("LANGUAGE", locale.get("LC_CTYPE", "C")) return env def execve_from(self, conf, cmd, env): """ this code is commonly run in a child process // returns exit-code""" runs = conf.get("Service", "Type", "simple").lower() logg.debug("%s process for %s", runs, conf.filename()) inp = open("/dev/zero") out = self.open_journal_log(conf) os.dup2(inp.fileno(), sys.stdin.fileno()) os.dup2(out.fileno(), sys.stdout.fileno()) os.dup2(out.fileno(), sys.stderr.fileno()) runuser = self.expand_special(conf.get("Service", "User", ""), conf) rungroup = self.expand_special(conf.get("Service", "Group", ""), conf) envs = shutil_setuid(runuser, rungroup) badpath = self.chdir_workingdir(conf) # some dirs need setuid before if badpath: logg.error("(%s): bad workingdir: '%s'", shell_cmd(cmd), badpath) sys.exit(1) env = self.extend_exec_env(env) env.update(envs) # set $HOME to ~$USER try: if "spawn" in COVERAGE: os.spawnvpe(os.P_WAIT, cmd[0], cmd, env) sys.exit(0) else: # pragma: nocover os.execve(cmd[0], cmd, env) except Exception as e: logg.error("(%s): %s", shell_cmd(cmd), e) sys.exit(1) def test_start_unit(self, unit): """ helper function to test the code that is normally forked off """ conf = self.load_unit_conf(unit) env = self.get_env(conf) for cmd in conf.getlist("Service", "ExecStart", []): newcmd = self.exec_cmd(cmd, env, conf) return self.execve_from(conf, newcmd, env) return None def stop_modules(self, *modules): """ [UNIT]... -- stop these units """ found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] return self.stop_units(units) and found_all def stop_units(self, units): """ fails if any unit fails to stop """ self.wait_system() done = True for unit in self.sortedBefore(units): if not self.stop_unit(unit): done = False return done def stop_unit(self, unit): conf = self.load_unit_conf(unit) if conf is None: logg.error("Unit %s could not be found.", unit) return False if self.not_user_conf(conf): logg.error("Unit %s not for --user mode", unit) return False return self.stop_unit_from(conf) def get_TimeoutStopSec(self, conf): timeout = conf.get("Service", "TimeoutSec", DefaultTimeoutStartSec) timeout = conf.get("Service", "TimeoutStopSec", timeout) return time_to_seconds(timeout, DefaultMaximumTimeout) def stop_unit_from(self, conf): if not conf: return False if self.syntax_check(conf) > 100: return False with waitlock(conf): logg.info(" stop unit %s => %s", conf.name(), conf.filename()) return self.do_stop_unit_from(conf) def do_stop_unit_from(self, conf): timeout = self.get_TimeoutStopSec(conf) runs = conf.get("Service", "Type", "simple").lower() env = self.get_env(conf) self.exec_check_service(conf, env, "ExecStop") returncode = 0 service_result = "success" if runs in [ "sysv" ]: status_file = self.status_file_from(conf) if True: exe = conf.filename() cmd = "'%s' stop" % exe env["SYSTEMCTL_SKIP_REDIRECT"] = "yes" newcmd = self.exec_cmd(cmd, env, conf) logg.info("%s stop %s", runs, shell_cmd(newcmd)) forkpid = os.fork() if not forkpid: self.execve_from(conf, newcmd, env) # pragma: nocover run = subprocess_waitpid(forkpid) if run.returncode: self.set_status_from(conf, "ExecStopCode", run.returncode) self.write_status_from(conf, AS="failed") else: self.clean_status_from(conf) # "inactive" return True elif runs in [ "oneshot" ]: status_file = self.status_file_from(conf) if self.get_status_from(conf, "ActiveState", "unknown") == "inactive": logg.warning("the service is already down once") return True for cmd in conf.getlist("Service", "ExecStop", []): check, cmd = checkstatus(cmd) logg.debug("{env} %s", env) newcmd = self.exec_cmd(cmd, env, conf) logg.info("%s stop %s", runs, shell_cmd(newcmd)) forkpid = os.fork() if not forkpid: self.execve_from(conf, newcmd, env) # pragma: nocover run = subprocess_waitpid(forkpid) if run.returncode and check: returncode = run.returncode service_result = "failed" break if True: if returncode: self.set_status_from(conf, "ExecStopCode", returncode) self.write_status_from(conf, AS="failed") else: self.clean_status_from(conf) # "inactive" ### fallback Stop => Kill for ["simple","notify","forking"] elif not conf.getlist("Service", "ExecStop", []): logg.info("no ExecStop => systemctl kill") if True: self.do_kill_unit_from(conf) self.clean_pid_file_from(conf) self.clean_status_from(conf) # "inactive" elif runs in [ "simple", "notify" ]: status_file = self.status_file_from(conf) size = os.path.exists(status_file) and os.path.getsize(status_file) logg.info("STATUS %s %s", status_file, size) pid = 0 for cmd in conf.getlist("Service", "ExecStop", []): check, cmd = checkstatus(cmd) env["MAINPID"] = str(self.read_mainpid_from(conf, "")) newcmd = self.exec_cmd(cmd, env, conf) logg.info("%s stop %s", runs, shell_cmd(newcmd)) forkpid = os.fork() if not forkpid: self.execve_from(conf, newcmd, env) # pragma: nocover run = subprocess_waitpid(forkpid) run = must_have_failed(run, newcmd) # TODO: a workaround # self.write_status_from(conf, MainPID=run.pid) # no ExecStop if run.returncode and check: returncode = run.returncode service_result = "failed" break pid = env.get("MAINPID",0) if pid: if self.wait_vanished_pid(pid, timeout): self.clean_pid_file_from(conf) self.clean_status_from(conf) # "inactive" else: logg.info("%s sleep as no PID was found on Stop", runs) time.sleep(MinimumTimeoutStopSec) pid = self.read_mainpid_from(conf, "") if not pid or not pid_exists(pid) or pid_zombie(pid): self.clean_pid_file_from(conf) self.clean_status_from(conf) # "inactive" elif runs in [ "forking" ]: status_file = self.status_file_from(conf) pid_file = self.pid_file_from(conf) for cmd in conf.getlist("Service", "ExecStop", []): active = self.is_active_from(conf) if pid_file: new_pid = self.read_mainpid_from(conf, "") if new_pid: env["MAINPID"] = str(new_pid) check, cmd = checkstatus(cmd) logg.debug("{env} %s", env) newcmd = self.exec_cmd(cmd, env, conf) logg.info("fork stop %s", shell_cmd(newcmd)) forkpid = os.fork() if not forkpid: self.execve_from(conf, newcmd, env) # pragma: nocover run = subprocess_waitpid(forkpid) if run.returncode and check: returncode = run.returncode service_result = "failed" break pid = env.get("MAINPID",0) if pid: if self.wait_vanished_pid(pid, timeout): self.clean_pid_file_from(conf) else: logg.info("%s sleep as no PID was found on Stop", runs) time.sleep(MinimumTimeoutStopSec) pid = self.read_mainpid_from(conf, "") if not pid or not pid_exists(pid) or pid_zombie(pid): self.clean_pid_file_from(conf) if returncode: if os.path.isfile(status_file): self.set_status_from(conf, "ExecStopCode", returncode) self.write_status_from(conf, AS="failed") else: self.clean_status_from(conf) # "inactive" else: logg.error("unsupported run type '%s'", runs) return False # POST sequence active = self.is_active_from(conf) if not active: env["SERVICE_RESULT"] = service_result for cmd in conf.getlist("Service", "ExecStopPost", []): check, cmd = checkstatus(cmd) newcmd = self.exec_cmd(cmd, env, conf) logg.info("post-stop %s", shell_cmd(newcmd)) forkpid = os.fork() if not forkpid: self.execve_from(conf, newcmd, env) # pragma: nocover run = subprocess_waitpid(forkpid) logg.debug("post-stop done (%s) <-%s>", run.returncode or "OK", run.signal or "") return service_result == "success" def wait_vanished_pid(self, pid, timeout): if not pid: return True logg.info("wait for PID %s to vanish (%ss)", pid, timeout) for x in xrange(int(timeout)): if not self.is_active_pid(pid): logg.info("wait for PID %s is done (%s.)", pid, x) return True time.sleep(1) # until TimeoutStopSec logg.info("wait for PID %s failed (%s.)", pid, x) return False def reload_modules(self, *modules): """ [UNIT]... -- reload these units """ self.wait_system() found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] return self.reload_units(units) and found_all def reload_units(self, units): """ fails if any unit fails to reload """ self.wait_system() done = True for unit in self.sortedAfter(units): if not self.reload_unit(unit): done = False return done def reload_unit(self, unit): conf = self.load_unit_conf(unit) if conf is None: logg.error("Unit %s could not be found.", unit) return False if self.not_user_conf(conf): logg.error("Unit %s not for --user mode", unit) return False return self.reload_unit_from(conf) def reload_unit_from(self, conf): if not conf: return False if self.syntax_check(conf) > 100: return False with waitlock(conf): logg.info(" reload unit %s => %s", conf.name(), conf.filename()) return self.do_reload_unit_from(conf) def do_reload_unit_from(self, conf): runs = conf.get("Service", "Type", "simple").lower() env = self.get_env(conf) self.exec_check_service(conf, env, "ExecReload") if runs in [ "sysv" ]: status_file = self.status_file_from(conf) if True: exe = conf.filename() cmd = "'%s' reload" % exe env["SYSTEMCTL_SKIP_REDIRECT"] = "yes" newcmd = self.exec_cmd(cmd, env, conf) logg.info("%s reload %s", runs, shell_cmd(newcmd)) forkpid = os.fork() if not forkpid: self.execve_from(conf, newcmd, env) # pragma: nocover run = subprocess_waitpid(forkpid) self.set_status_from(conf, "ExecReloadCode", run.returncode) if run.returncode: self.write_status_from(conf, AS="failed") return False else: self.write_status_from(conf, AS="active") return True elif runs in [ "simple", "notify", "forking" ]: if not self.is_active_from(conf): logg.info("no reload on inactive service %s", conf.name()) return True for cmd in conf.getlist("Service", "ExecReload", []): env["MAINPID"] = str(self.read_mainpid_from(conf, "")) check, cmd = checkstatus(cmd) newcmd = self.exec_cmd(cmd, env, conf) logg.info("%s reload %s", runs, shell_cmd(newcmd)) forkpid = os.fork() if not forkpid: self.execve_from(conf, newcmd, env) # pragma: nocover run = subprocess_waitpid(forkpid) if check and run.returncode: logg.error("Job for %s failed because the control process exited with error code. (%s)", conf.name(), run.returncode) return False time.sleep(MinimumYield) return True elif runs in [ "oneshot" ]: logg.debug("ignored run type '%s' for reload", runs) return True else: logg.error("unsupported run type '%s'", runs) return False def restart_modules(self, *modules): """ [UNIT]... -- restart these units """ found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] return self.restart_units(units) and found_all def restart_units(self, units): """ fails if any unit fails to restart """ self.wait_system() done = True for unit in self.sortedAfter(units): if not self.restart_unit(unit): done = False return done def restart_unit(self, unit): conf = self.load_unit_conf(unit) if conf is None: logg.error("Unit %s could not be found.", unit) return False if self.not_user_conf(conf): logg.error("Unit %s not for --user mode", unit) return False return self.restart_unit_from(conf) def restart_unit_from(self, conf): if not conf: return False if self.syntax_check(conf) > 100: return False with waitlock(conf): logg.info(" restart unit %s => %s", conf.name(), conf.filename()) if not self.is_active_from(conf): return self.do_start_unit_from(conf) else: return self.do_restart_unit_from(conf) def do_restart_unit_from(self, conf): logg.info("(restart) => stop/start") self.do_stop_unit_from(conf) return self.do_start_unit_from(conf) def try_restart_modules(self, *modules): """ [UNIT]... -- try-restart these units """ found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] return self.try_restart_units(units) and found_all def try_restart_units(self, units): """ fails if any module fails to try-restart """ self.wait_system() done = True for unit in self.sortedAfter(units): if not self.try_restart_unit(unit): done = False return done def try_restart_unit(self, unit): """ only do 'restart' if 'active' """ conf = self.load_unit_conf(unit) if conf is None: logg.error("Unit %s could not be found.", unit) return False if self.not_user_conf(conf): logg.error("Unit %s not for --user mode", unit) return False with waitlock(conf): logg.info(" try-restart unit %s => %s", conf.name(), conf.filename()) if self.is_active_from(conf): return self.do_restart_unit_from(conf) return True def reload_or_restart_modules(self, *modules): """ [UNIT]... -- reload-or-restart these units """ found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] return self.reload_or_restart_units(units) and found_all def reload_or_restart_units(self, units): """ fails if any unit does not reload-or-restart """ self.wait_system() done = True for unit in self.sortedAfter(units): if not self.reload_or_restart_unit(unit): done = False return done def reload_or_restart_unit(self, unit): """ do 'reload' if specified, otherwise do 'restart' """ conf = self.load_unit_conf(unit) if conf is None: logg.error("Unit %s could not be found.", unit) return False if self.not_user_conf(conf): logg.error("Unit %s not for --user mode", unit) return False return self.reload_or_restart_unit_from(conf) def reload_or_restart_unit_from(self, conf): """ do 'reload' if specified, otherwise do 'restart' """ if not conf: return False with waitlock(conf): logg.info(" reload-or-restart unit %s => %s", conf.name(), conf.filename()) return self.do_reload_or_restart_unit_from(conf) def do_reload_or_restart_unit_from(self, conf): if not self.is_active_from(conf): # try: self.stop_unit_from(conf) # except Exception as e: pass return self.do_start_unit_from(conf) elif conf.getlist("Service", "ExecReload", []): logg.info("found service to have ExecReload -> 'reload'") return self.do_reload_unit_from(conf) else: logg.info("found service without ExecReload -> 'restart'") return self.do_restart_unit_from(conf) def reload_or_try_restart_modules(self, *modules): """ [UNIT]... -- reload-or-try-restart these units """ found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] return self.reload_or_try_restart_units(units) and found_all def reload_or_try_restart_units(self, units): """ fails if any unit fails to reload-or-try-restart """ self.wait_system() done = True for unit in self.sortedAfter(units): if not self.reload_or_try_restart_unit(unit): done = False return done def reload_or_try_restart_unit(self, unit): conf = self.load_unit_conf(unit) if conf is None: logg.error("Unit %s could not be found.", unit) return False if self.not_user_conf(conf): logg.error("Unit %s not for --user mode", unit) return False return self.reload_or_try_restart_unit_from(conf) def reload_or_try_restart_unit_from(self, conf): with waitlock(conf): logg.info(" reload-or-try-restart unit %s => %s", conf.name(), conf.filename()) return self.do_reload_or_try_restart_unit_from(conf) def do_reload_or_try_restart_unit_from(self, conf): if conf.getlist("Service", "ExecReload", []): return self.do_reload_unit_from(conf) elif not self.is_active_from(conf): return True else: return self.do_restart_unit_from(conf) def kill_modules(self, *modules): """ [UNIT]... -- kill these units """ found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] return self.kill_units(units) and found_all def kill_units(self, units): """ fails if any unit could not be killed """ self.wait_system() done = True for unit in self.sortedBefore(units): if not self.kill_unit(unit): done = False return done def kill_unit(self, unit): conf = self.load_unit_conf(unit) if conf is None: logg.error("Unit %s could not be found.", unit) return False if self.not_user_conf(conf): logg.error("Unit %s not for --user mode", unit) return False return self.kill_unit_from(conf) def kill_unit_from(self, conf): if not conf: return False with waitlock(conf): logg.info(" kill unit %s => %s", conf.name(), conf.filename()) return self.do_kill_unit_from(conf) def do_kill_unit_from(self, conf): started = time.time() doSendSIGKILL = conf.getbool("Service", "SendSIGKILL", "yes") doSendSIGHUP = conf.getbool("Service", "SendSIGHUP", "no") useKillMode = conf.get("Service", "KillMode", "control-group") useKillSignal = conf.get("Service", "KillSignal", "SIGTERM") kill_signal = getattr(signal, useKillSignal) timeout = self.get_TimeoutStopSec(conf) status_file = self.status_file_from(conf) size = os.path.exists(status_file) and os.path.getsize(status_file) logg.info("STATUS %s %s", status_file, size) mainpid = to_int(self.read_mainpid_from(conf, "")) self.clean_status_from(conf) # clear RemainAfterExit and TimeoutStartSec if not mainpid: if useKillMode in ["control-group"]: logg.warning("no main PID [%s]", conf.filename()) logg.warning("and there is no control-group here") else: logg.info("no main PID [%s]", conf.filename()) return False if not pid_exists(mainpid) or pid_zombie(mainpid): logg.debug("ignoring children when mainpid is already dead") # because we list child processes, not processes in control-group return True pidlist = self.pidlist_of(mainpid) # here if pid_exists(mainpid): logg.info("stop kill PID %s", mainpid) self._kill_pid(mainpid, kill_signal) if useKillMode in ["control-group"]: if len(pidlist) > 1: logg.info("stop control-group PIDs %s", pidlist) for pid in pidlist: if pid != mainpid: self._kill_pid(pid, kill_signal) if doSendSIGHUP: logg.info("stop SendSIGHUP to PIDs %s", pidlist) for pid in pidlist: self._kill_pid(pid, signal.SIGHUP) # wait for the processes to have exited while True: dead = True for pid in pidlist: if pid_exists(pid) and not pid_zombie(pid): dead = False break if dead: break if time.time() > started + timeout: logg.info("service PIDs not stopped after %s", timeout) break time.sleep(1) # until TimeoutStopSec if dead or not doSendSIGKILL: logg.info("done kill PID %s %s", mainpid, dead and "OK") return dead if useKillMode in [ "control-group", "mixed" ]: logg.info("hard kill PIDs %s", pidlist) for pid in pidlist: if pid != mainpid: self._kill_pid(pid, signal.SIGKILL) time.sleep(MinimumYield) # useKillMode in [ "control-group", "mixed", "process" ] if pid_exists(mainpid): logg.info("hard kill PID %s", mainpid) self._kill_pid(mainpid, signal.SIGKILL) time.sleep(MinimumYield) dead = not pid_exists(mainpid) or pid_zombie(mainpid) logg.info("done hard kill PID %s %s", mainpid, dead and "OK") return dead def _kill_pid(self, pid, kill_signal = None): try: sig = kill_signal or signal.SIGTERM os.kill(pid, sig) except OSError as e: if e.errno == errno.ESRCH or e.errno == errno.ENOENT: logg.debug("kill PID %s => No such process", pid) return True else: logg.error("kill PID %s => %s", pid, str(e)) return False return not pid_exists(pid) or pid_zombie(pid) def is_active_modules(self, *modules): """ [UNIT].. -- check if these units are in active state implements True if all is-active = True """ # systemctl returns multiple lines, one for each argument # "active" when is_active # "inactive" when not is_active # "unknown" when not enabled # The return code is set to # 0 when "active" # 1 when unit is not found # 3 when any "inactive" or "unknown" # However: # TODO!!!!! BUG in original systemctl!! # documentation says " exit code 0 if at least one is active" # and "Unless --quiet is specified, print the unit state" units = [] results = [] for module in modules: units = self.match_units([ module ]) if not units: logg.error("Unit %s could not be found.", unit_of(module)) results += [ "unknown" ] continue for unit in units: active = self.get_active_unit(unit) enabled = self.enabled_unit(unit) if enabled != "enabled": active = "unknown" results += [ active ] break ## how it should work: status = "active" in results ## how 'systemctl' works: non_active = [ result for result in results if result != "active" ] status = not non_active if not status: status = 3 if not _quiet: return status, results else: return status def is_active_from(self, conf): """ used in try-restart/other commands to check if needed. """ if not conf: return False return self.get_active_from(conf) == "active" def active_pid_from(self, conf): if not conf: return False pid = self.read_mainpid_from(conf, "") return self.is_active_pid(pid) def is_active_pid(self, pid): """ returns pid if the pid is still an active process """ if pid and pid_exists(pid) and not pid_zombie(pid): return pid # usually a string (not null) return None def get_active_unit(self, unit): """ returns 'active' 'inactive' 'failed' 'unknown' """ conf = self.get_unit_conf(unit) if not conf.loaded(): logg.warning("Unit %s could not be found.", unit) return "unknown" return self.get_active_from(conf) def get_active_from(self, conf): """ returns 'active' 'inactive' 'failed' 'unknown' """ # used in try-restart/other commands to check if needed. if not conf: return "unknown" pid_file = self.pid_file_from(conf) if pid_file: # application PIDFile if not os.path.exists(pid_file): return "inactive" status_file = self.status_file_from(conf) if self.getsize(status_file): state = self.get_status_from(conf, "ActiveState", "") if state: logg.info("get_status_from %s => %s", conf.name(), state) return state pid = self.read_mainpid_from(conf, "") logg.debug("pid_file '%s' => PID %s", pid_file or status_file, pid) if pid: if not pid_exists(pid) or pid_zombie(pid): return "failed" return "active" else: return "inactive" def get_substate_from(self, conf): """ returns 'running' 'exited' 'dead' 'failed' 'plugged' 'mounted' """ if not conf: return False pid_file = self.pid_file_from(conf) if pid_file: if not os.path.exists(pid_file): return "dead" status_file = self.status_file_from(conf) if self.getsize(status_file): state = self.get_status_from(conf, "ActiveState", "") if state: if state in [ "active" ]: return self.get_status_from(conf, "SubState", "running") else: return self.get_status_from(conf, "SubState", "dead") pid = self.read_mainpid_from(conf, "") logg.debug("pid_file '%s' => PID %s", pid_file or status_file, pid) if pid: if not pid_exists(pid) or pid_zombie(pid): return "failed" return "running" else: return "dead" def is_failed_modules(self, *modules): """ [UNIT]... -- check if these units are in failes state implements True if any is-active = True """ units = [] results = [] for module in modules: units = self.match_units([ module ]) if not units: logg.error("Unit %s could not be found.", unit_of(module)) results += [ "unknown" ] continue for unit in units: active = self.get_active_unit(unit) enabled = self.enabled_unit(unit) if enabled != "enabled": active = "unknown" results += [ active ] break status = "failed" in results if not _quiet: return status, results else: return status def is_failed_from(self, conf): if conf is None: return True return self.get_active_from(conf) == "failed" def reset_failed_modules(self, *modules): """ [UNIT]... -- Reset failed state for all, one, or more units """ units = [] status = True for module in modules: units = self.match_units([ module ]) if not units: logg.error("Unit %s could not be found.", unit_of(module)) return 1 for unit in units: if not self.reset_failed_unit(unit): logg.error("Unit %s could not be reset.", unit_of(module)) status = False break return status def reset_failed_unit(self, unit): conf = self.get_unit_conf(unit) if not conf.loaded(): logg.warning("Unit %s could not be found.", unit) return False if self.not_user_conf(conf): logg.error("Unit %s not for --user mode", unit) return False return self.reset_failed_from(conf) def reset_failed_from(self, conf): if conf is None: return True if not self.is_failed_from(conf): return False done = False status_file = self.status_file_from(conf) if status_file and os.path.exists(status_file): try: os.remove(status_file) done = True logg.debug("done rm %s", status_file) except Exception as e: logg.error("while rm %s: %s", status_file, e) pid_file = self.pid_file_from(conf) if pid_file and os.path.exists(pid_file): try: os.remove(pid_file) done = True logg.debug("done rm %s", pid_file) except Exception as e: logg.error("while rm %s: %s", pid_file, e) return done def status_modules(self, *modules): """ [UNIT]... check the status of these units. """ found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] status, result = self.status_units(units) if not found_all: status = 3 # same as (dead) # original behaviour return (status, result) def status_units(self, units): """ concatenates the status output of all units and the last non-successful statuscode """ status, result = 0, "" for unit in units: status1, result1 = self.status_unit(unit) if status1: status = status1 if result: result += "\n\n" result += result1 return status, result def status_unit(self, unit): conf = self.get_unit_conf(unit) result = "%s - %s" % (unit, self.get_description_from(conf)) loaded = conf.loaded() if loaded: filename = conf.filename() enabled = self.enabled_from(conf) result += "\n Loaded: {loaded} ({filename}, {enabled})".format(**locals()) for path in conf.overrides(): result += "\n Drop-In: {path}".format(**locals()) else: result += "\n Loaded: failed" return 3, result active = self.get_active_from(conf) substate = self.get_substate_from(conf) result += "\n Active: {} ({})".format(active, substate) if active == "active": return 0, result else: return 3, result def cat_modules(self, *modules): """ [UNIT]... show the *.system file for these" """ found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] done, result = self.cat_units(units) return (done and found_all, result) def cat_units(self, units): done = True result = "" for unit in units: text = self.cat_unit(unit) if not text: done = False else: if result: result += "\n\n" result += text return done, result def cat_unit(self, unit): try: unit_file = self.unit_file(unit) if unit_file: return open(unit_file).read() logg.error("no file for unit '%s'", unit) except Exception as e: print("Unit {} is not-loaded: {}".format(unit, e)) return False ## ## def load_preset_files(self, module = None): # -> [ preset-file-names,... ] """ reads all preset files, returns the scanned files """ if self._preset_file_list is None: self._preset_file_list = {} for folder in self.preset_folders(): if not folder: continue if self._root: folder = os_path(self._root, folder) if not os.path.isdir(folder): continue for name in os.listdir(folder): if not name.endswith(".preset"): continue if name not in self._preset_file_list: path = os.path.join(folder, name) if os.path.isdir(path): continue preset = PresetFile().read(path) self._preset_file_list[name] = preset logg.debug("found %s preset files", len(self._preset_file_list)) return sorted(self._preset_file_list.keys()) def get_preset_of_unit(self, unit): """ [UNIT] check the *.preset of this unit """ self.load_preset_files() for filename in sorted(self._preset_file_list.keys()): preset = self._preset_file_list[filename] status = preset.get_preset(unit) if status: return status return None def preset_modules(self, *modules): """ [UNIT]... -- set 'enabled' when in *.preset """ if self.user_mode(): logg.warning("preset makes no sense in --user mode") return True found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] return self.preset_units(units) and found_all def preset_units(self, units): """ fails if any unit could not be changed """ self.wait_system() fails = 0 found = 0 for unit in units: status = self.get_preset_of_unit(unit) if not status: continue found += 1 if status.startswith("enable"): if self._preset_mode == "disable": continue logg.info("preset enable %s", unit) if not self.enable_unit(unit): logg.warning("failed to enable %s", unit) fails += 1 if status.startswith("disable"): if self._preset_mode == "enable": continue logg.info("preset disable %s", unit) if not self.disable_unit(unit): logg.warning("failed to disable %s", unit) fails += 1 return not fails and not not found def system_preset_all(self, *modules): """ 'preset' all services enable or disable services according to *.preset files """ if self.user_mode(): logg.warning("preset-all makes no sense in --user mode") return True found_all = True units = self.match_units() # TODO: how to handle module arguments return self.preset_units(units) and found_all def wanted_from(self, conf, default = None): if not conf: return default return conf.get("Install", "WantedBy", default, True) def enablefolders(self, wanted): if self.user_mode(): for folder in self.user_folders(): yield self.default_enablefolder(wanted, folder) if True: for folder in self.system_folders(): yield self.default_enablefolder(wanted, folder) def enablefolder(self, wanted = None): if self.user_mode(): user_folder = self.user_folder() return self.default_enablefolder(wanted, user_folder) else: return self.default_enablefolder(wanted) def default_enablefolder(self, wanted = None, basefolder = None): basefolder = basefolder or self.system_folder() if not wanted: return wanted if not wanted.endswith(".wants"): wanted = wanted + ".wants" return os.path.join(basefolder, wanted) def enable_modules(self, *modules): """ [UNIT]... -- enable these units """ found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: logg.info("matched %s", unit) #++ if unit not in units: units += [ unit ] return self.enable_units(units) and found_all def enable_units(self, units): self.wait_system() done = True for unit in units: if not self.enable_unit(unit): done = False elif self._now: self.start_unit(unit) return done def enable_unit(self, unit): unit_file = self.unit_file(unit) if not unit_file: logg.error("Unit %s could not be found.", unit) return False if self.is_sysv_file(unit_file): if self.user_mode(): logg.error("Initscript %s not for --user mode", unit) return False return self.enable_unit_sysv(unit_file) conf = self.get_unit_conf(unit) if self.not_user_conf(conf): logg.error("Unit %s not for --user mode", unit) return False wanted = self.wanted_from(self.get_unit_conf(unit)) if not wanted: return False # "static" is-enabled folder = self.enablefolder(wanted) if self._root: folder = os_path(self._root, folder) if not os.path.isdir(folder): os.makedirs(folder) target = os.path.join(folder, os.path.basename(unit_file)) if True: _f = self._force and "-f" or "" logg.info("ln -s {_f} '{unit_file}' '{target}'".format(**locals())) if self._force and os.path.islink(target): os.remove(target) if not os.path.islink(target): os.symlink(unit_file, target) return True def rc3_root_folder(self): old_folder = "/etc/rc3.d" new_folder = "/etc/init.d/rc3.d" if self._root: old_folder = os_path(self._root, old_folder) new_folder = os_path(self._root, new_folder) if os.path.isdir(old_folder): return old_folder return new_folder def rc5_root_folder(self): old_folder = "/etc/rc5.d" new_folder = "/etc/init.d/rc5.d" if self._root: old_folder = os_path(self._root, old_folder) new_folder = os_path(self._root, new_folder) if os.path.isdir(old_folder): return old_folder return new_folder def enable_unit_sysv(self, unit_file): # a "multi-user.target"/rc3 is also started in /rc5 rc3 = self._enable_unit_sysv(unit_file, self.rc3_root_folder()) rc5 = self._enable_unit_sysv(unit_file, self.rc5_root_folder()) return rc3 and rc5 def _enable_unit_sysv(self, unit_file, rc_folder): name = os.path.basename(unit_file) nameS = "S50"+name nameK = "K50"+name if not os.path.isdir(rc_folder): os.makedirs(rc_folder) # do not double existing entries for found in os.listdir(rc_folder): m = re.match(r"S\d\d(.*)", found) if m and m.group(1) == name: nameS = found m = re.match(r"K\d\d(.*)", found) if m and m.group(1) == name: nameK = found target = os.path.join(rc_folder, nameS) if not os.path.exists(target): os.symlink(unit_file, target) target = os.path.join(rc_folder, nameK) if not os.path.exists(target): os.symlink(unit_file, target) return True def disable_modules(self, *modules): """ [UNIT]... -- disable these units """ found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] return self.disable_units(units) and found_all def disable_units(self, units): self.wait_system() done = True for unit in units: if not self.disable_unit(unit): done = False return done def disable_unit(self, unit): unit_file = self.unit_file(unit) if not unit_file: logg.error("Unit %s could not be found.", unit) return False if self.is_sysv_file(unit_file): if self.user_mode(): logg.error("Initscript %s not for --user mode", unit) return False return self.disable_unit_sysv(unit_file) conf = self.get_unit_conf(unit) if self.not_user_conf(conf): logg.error("Unit %s not for --user mode", unit) return False wanted = self.wanted_from(self.get_unit_conf(unit)) if not wanted: return False # "static" is-enabled for folder in self.enablefolders(wanted): if self._root: folder = os_path(self._root, folder) target = os.path.join(folder, os.path.basename(unit_file)) if os.path.isfile(target): try: _f = self._force and "-f" or "" logg.info("rm {_f} '{target}'".format(**locals())) os.remove(target) except IOError as e: logg.error("disable %s: %s", target, e) except OSError as e: logg.error("disable %s: %s", target, e) return True def disable_unit_sysv(self, unit_file): rc3 = self._disable_unit_sysv(unit_file, self.rc3_root_folder()) rc5 = self._disable_unit_sysv(unit_file, self.rc5_root_folder()) return rc3 and rc5 def _disable_unit_sysv(self, unit_file, rc_folder): # a "multi-user.target"/rc3 is also started in /rc5 name = os.path.basename(unit_file) nameS = "S50"+name nameK = "K50"+name # do not forget the existing entries for found in os.listdir(rc_folder): m = re.match(r"S\d\d(.*)", found) if m and m.group(1) == name: nameS = found m = re.match(r"K\d\d(.*)", found) if m and m.group(1) == name: nameK = found target = os.path.join(rc_folder, nameS) if os.path.exists(target): os.unlink(target) target = os.path.join(rc_folder, nameK) if os.path.exists(target): os.unlink(target) return True def is_enabled_sysv(self, unit_file): name = os.path.basename(unit_file) target = os.path.join(self.rc3_root_folder(), "S50%s" % name) if os.path.exists(target): return True return False def is_enabled_modules(self, *modules): """ [UNIT]... -- check if these units are enabled returns True if any of them is enabled.""" found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] return self.is_enabled_units(units) # and found_all def is_enabled_units(self, units): """ true if any is enabled, and a list of infos """ result = False infos = [] for unit in units: infos += [ self.enabled_unit(unit) ] if self.is_enabled(unit): result = True return result, infos def is_enabled(self, unit): unit_file = self.unit_file(unit) if not unit_file: logg.error("Unit %s could not be found.", unit) return False if self.is_sysv_file(unit_file): return self.is_enabled_sysv(unit_file) wanted = self.wanted_from(self.get_unit_conf(unit)) if not wanted: return True # "static" for folder in self.enablefolders(wanted): if self._root: folder = os_path(self._root, folder) target = os.path.join(folder, os.path.basename(unit_file)) if os.path.isfile(target): return True return False def enabled_unit(self, unit): conf = self.get_unit_conf(unit) return self.enabled_from(conf) def enabled_from(self, conf): unit_file = conf.filename() if self.is_sysv_file(unit_file): state = self.is_enabled_sysv(unit_file) if state: return "enabled" return "disabled" if conf.masked: return "masked" wanted = self.wanted_from(conf) if not wanted: return "static" for folder in self.enablefolders(wanted): if self._root: folder = os_path(self._root, folder) target = os.path.join(folder, os.path.basename(unit_file)) if os.path.isfile(target): return "enabled" return "disabled" def mask_modules(self, *modules): """ [UNIT]... -- mask non-startable units """ found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] return self.mask_units(units) and found_all def mask_units(self, units): self.wait_system() done = True for unit in units: if not self.mask_unit(unit): done = False return done def mask_unit(self, unit): unit_file = self.unit_file(unit) if not unit_file: logg.error("Unit %s could not be found.", unit) return False if self.is_sysv_file(unit_file): logg.error("Initscript %s can not be masked", unit) return False conf = self.get_unit_conf(unit) if self.not_user_conf(conf): logg.error("Unit %s not for --user mode", unit) return False folder = self.mask_folder() if self._root: folder = os_path(self._root, folder) if not os.path.isdir(folder): os.makedirs(folder) target = os.path.join(folder, os.path.basename(unit_file)) if True: _f = self._force and "-f" or "" logg.debug("ln -s {_f} /dev/null '{target}'".format(**locals())) if self._force and os.path.islink(target): os.remove(target) if not os.path.exists(target): os.symlink("/dev/null", target) logg.info("Created symlink {target} -> /dev/null".format(**locals())) return True elif os.path.islink(target): logg.debug("mask symlink does already exist: %s", target) return True else: logg.error("mask target does already exist: %s", target) return False def mask_folder(self): for folder in self.mask_folders(): if folder: return folder raise Exception("did not find any systemd/system folder") def mask_folders(self): if self.user_mode(): for folder in self.user_folders(): yield folder if True: for folder in self.system_folders(): yield folder def unmask_modules(self, *modules): """ [UNIT]... -- unmask non-startable units """ found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] return self.unmask_units(units) and found_all def unmask_units(self, units): self.wait_system() done = True for unit in units: if not self.unmask_unit(unit): done = False return done def unmask_unit(self, unit): unit_file = self.unit_file(unit) if not unit_file: logg.error("Unit %s could not be found.", unit) return False if self.is_sysv_file(unit_file): logg.error("Initscript %s can not be un/masked", unit) return False conf = self.get_unit_conf(unit) if self.not_user_conf(conf): logg.error("Unit %s not for --user mode", unit) return False folder = self.mask_folder() if self._root: folder = os_path(self._root, folder) target = os.path.join(folder, os.path.basename(unit_file)) if True: _f = self._force and "-f" or "" logg.info("rm {_f} '{target}'".format(**locals())) if os.path.islink(target): os.remove(target) return True elif not os.path.exists(target): logg.debug("Symlink did exist anymore: %s", target) return True else: logg.warning("target is not a symlink: %s", target) return True def list_dependencies_modules(self, *modules): """ [UNIT]... show the dependency tree" """ found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] return self.list_dependencies_units(units) # and found_all def list_dependencies_units(self, units): if self._now: return self.list_start_dependencies_units(units) result = [] for unit in units: if result: result += [ "", "" ] result += self.list_dependencies_unit(unit) return result def list_dependencies_unit(self, unit): result = [] for line in self.list_dependencies(unit, ""): result += [ line ] return result def list_dependencies(self, unit, indent = None, mark = None, loop = []): mapping = {} mapping["Requires"] = "required to start" mapping["Wants"] = "wanted to start" mapping["Requisite"] = "required started" mapping["Bindsto"] = "binds to start" mapping["PartOf"] = "part of started" mapping[".requires"] = ".required to start" mapping[".wants"] = ".wanted to start" mapping["PropagateReloadTo"] = "(to be reloaded as well)" mapping["Conflicts"] = "(to be stopped on conflict)" restrict = ["Requires", "Requisite", "ConsistsOf", "Wants", "BindsTo", ".requires", ".wants"] indent = indent or "" mark = mark or "" deps = self.get_dependencies_unit(unit) conf = self.get_unit_conf(unit) if not conf.loaded(): if not self._show_all: return yield "%s(%s): %s" % (indent, unit, mark) else: yield "%s%s: %s" % (indent, unit, mark) for stop_recursion in [ "Conflict", "conflict", "reloaded", "Propagate" ]: if stop_recursion in mark: return for dep in deps: if dep in loop: logg.debug("detected loop at %s", dep) continue new_loop = loop + list(deps.keys()) new_indent = indent + "| " new_mark = deps[dep] if not self._show_all: if new_mark not in restrict: continue if new_mark in mapping: new_mark = mapping[new_mark] restrict = ["Requires", "Requisite", "ConsistsOf", "Wants", "BindsTo", ".requires", ".wants"] for line in self.list_dependencies(dep, new_indent, new_mark, new_loop): yield line def get_dependencies_unit(self, unit): conf = self.get_unit_conf(unit) deps = {} for style in [ "Requires", "Wants", "Requisite", "BindsTo", "PartOf", ".requires", ".wants", "PropagateReloadTo", "Conflicts", ]: if style.startswith("."): for folder in self.sysd_folders(): if not folder: continue require_path = os.path.join(folder, unit + style) if self._root: require_path = os_path(self._root, require_path) if os.path.isdir(require_path): for required in os.listdir(require_path): if required not in deps: deps[required] = style else: for requirelist in conf.getlist("Unit", style, []): for required in requirelist.strip().split(" "): deps[required.strip()] = style return deps def get_start_dependencies(self, unit): # pragma: no cover """ the list of services to be started as well / TODO: unused """ deps = {} unit_deps = self.get_dependencies_unit(unit) for dep_unit, dep_style in unit_deps.items(): restrict = ["Requires", "Requisite", "ConsistsOf", "Wants", "BindsTo", ".requires", ".wants"] if dep_style in restrict: if dep_unit in deps: if dep_style not in deps[dep_unit]: deps[dep_unit].append( dep_style) else: deps[dep_unit] = [ dep_style ] next_deps = self.get_start_dependencies(dep_unit) for dep, styles in next_deps.items(): for style in styles: if dep in deps: if style not in deps[dep]: deps[dep].append(style) else: deps[dep] = [ style ] return deps def list_start_dependencies_units(self, units): unit_order = [] deps = {} for unit in units: unit_order.append(unit) # unit_deps = self.get_start_dependencies(unit) # TODO unit_deps = self.get_dependencies_unit(unit) for dep_unit, styles in unit_deps.items(): styles = to_list(styles) for dep_style in styles: if dep_unit in deps: if dep_style not in deps[dep_unit]: deps[dep_unit].append( dep_style) else: deps[dep_unit] = [ dep_style ] deps_conf = [] for dep in deps: if dep in unit_order: continue conf = self.get_unit_conf(dep) if conf.loaded(): deps_conf.append(conf) for unit in unit_order: deps[unit] = [ "Requested" ] conf = self.get_unit_conf(unit) if conf.loaded(): deps_conf.append(conf) result = [] for dep in sortedAfter(deps_conf, cmp=compareAfter): line = (dep.name(), "(%s)" % (" ".join(deps[dep.name()]))) result.append(line) return result def sortedAfter(self, unitlist): """ get correct start order for the unit list (ignoring masked units) """ conflist = [ self.get_unit_conf(unit) for unit in unitlist ] if True: conflist = [] for unit in unitlist: conf = self.get_unit_conf(unit) if conf.masked: logg.debug("ignoring masked unit %s", unit) continue conflist.append(conf) sortlist = sortedAfter(conflist) return [ item.name() for item in sortlist ] def sortedBefore(self, unitlist): """ get correct start order for the unit list (ignoring masked units) """ conflist = [ self.get_unit_conf(unit) for unit in unitlist ] if True: conflist = [] for unit in unitlist: conf = self.get_unit_conf(unit) if conf.masked: logg.debug("ignoring masked unit %s", unit) continue conflist.append(conf) sortlist = sortedAfter(reversed(conflist)) return [ item.name() for item in reversed(sortlist) ] def system_daemon_reload(self): """ reload does will only check the service files here. The returncode will tell the number of warnings, and it is over 100 if it can not continue even for the relaxed systemctl.py style of execution. """ errors = 0 for unit in self.match_units(): try: conf = self.get_unit_conf(unit) except Exception as e: logg.error("%s: can not read unit file %s\n\t%s", unit, conf.filename(), e) continue errors += self.syntax_check(conf) if errors: logg.warning(" (%s) found %s problems", errors, errors % 100) return True # errors def syntax_check(self, conf): if conf.filename() and conf.filename().endswith(".service"): return self.syntax_check_service(conf) return 0 def syntax_check_service(self, conf): unit = conf.name() if not conf.data.has_section("Service"): logg.error(" %s: a .service file without [Service] section", unit) return 101 errors = 0 haveType = conf.get("Service", "Type", "simple") haveExecStart = conf.getlist("Service", "ExecStart", []) haveExecStop = conf.getlist("Service", "ExecStop", []) haveExecReload = conf.getlist("Service", "ExecReload", []) usedExecStart = [] usedExecStop = [] usedExecReload = [] if haveType not in [ "simple", "forking", "notify", "oneshot", "dbus", "idle", "sysv"]: logg.error(" %s: Failed to parse service type, ignoring: %s", unit, haveType) errors += 100 for line in haveExecStart: if not line.startswith("/") and not line.startswith("-/"): logg.error(" %s: Executable path is not absolute, ignoring: %s", unit, line.strip()) errors += 1 usedExecStart.append(line) for line in haveExecStop: if not line.startswith("/") and not line.startswith("-/"): logg.error(" %s: Executable path is not absolute, ignoring: %s", unit, line.strip()) errors += 1 usedExecStop.append(line) for line in haveExecReload: if not line.startswith("/") and not line.startswith("-/"): logg.error(" %s: Executable path is not absolute, ignoring: %s", unit, line.strip()) errors += 1 usedExecReload.append(line) if haveType in ["simple", "notify", "forking"]: if not usedExecStart and not usedExecStop: logg.error(" %s: Service lacks both ExecStart and ExecStop= setting. Refusing.", unit) errors += 101 elif not usedExecStart and haveType != "oneshot": logg.error(" %s: Service has no ExecStart= setting, which is only allowed for Type=oneshot services. Refusing.", unit) errors += 101 if len(usedExecStart) > 1 and haveType != "oneshot": logg.error(" %s: there may be only one ExecStart statement (unless for 'oneshot' services)." + "\n\t\t\tYou can use ExecStartPre / ExecStartPost to add additional commands.", unit) errors += 1 if len(usedExecStop) > 1 and haveType != "oneshot": logg.info(" %s: there should be only one ExecStop statement (unless for 'oneshot' services)." + "\n\t\t\tYou can use ExecStopPost to add additional commands (also executed on failed Start)", unit) if len(usedExecReload) > 1: logg.info(" %s: there should be only one ExecReload statement." + "\n\t\t\tUse ' ; ' for multiple commands (ExecReloadPost or ExedReloadPre do not exist)", unit) if len(usedExecReload) > 0 and "/bin/kill " in usedExecReload[0]: logg.warning(" %s: the use of /bin/kill is not recommended for ExecReload as it is asychronous." + "\n\t\t\tThat means all the dependencies will perform the reload simultanously / out of order.", unit) if conf.getlist("Service", "ExecRestart", []): #pragma: no cover logg.error(" %s: there no such thing as an ExecRestart (ignored)", unit) if conf.getlist("Service", "ExecRestartPre", []): #pragma: no cover logg.error(" %s: there no such thing as an ExecRestartPre (ignored)", unit) if conf.getlist("Service", "ExecRestartPost", []): #pragma: no cover logg.error(" %s: there no such thing as an ExecRestartPost (ignored)", unit) if conf.getlist("Service", "ExecReloadPre", []): #pragma: no cover logg.error(" %s: there no such thing as an ExecReloadPre (ignored)", unit) if conf.getlist("Service", "ExecReloadPost", []): #pragma: no cover logg.error(" %s: there no such thing as an ExecReloadPost (ignored)", unit) if conf.getlist("Service", "ExecStopPre", []): #pragma: no cover logg.error(" %s: there no such thing as an ExecStopPre (ignored)", unit) for env_file in conf.getlist("Service", "EnvironmentFile", []): if env_file.startswith("-"): continue if not os.path.isfile(os_path(self._root, env_file)): logg.error(" %s: Failed to load environment files: %s", unit, env_file) errors += 101 return errors def exec_check_service(self, conf, env, exectype = ""): if not conf: return True if not conf.data.has_section("Service"): return True #pragma: no cover haveType = conf.get("Service", "Type", "simple") if haveType in [ "sysv" ]: return True # we don't care about that abspath = 0 notexists = 0 for execs in [ "ExecStartPre", "ExecStart", "ExecStartPost", "ExecStop", "ExecStopPost", "ExecReload" ]: if not execs.startswith(exectype): continue for cmd in conf.getlist("Service", execs, []): check, cmd = checkstatus(cmd) newcmd = self.exec_cmd(cmd, env, conf) if not newcmd: continue exe = newcmd[0] if not exe: continue if exe[0] != "/": logg.error(" Exec is not an absolute path: %s=%s", execs, cmd) abspath += 1 if not os.path.isfile(exe): logg.error(" Exec command does not exist: (%s) %s", execs, exe) notexists += 1 newexe1 = os.path.join("/usr/bin", exe) newexe2 = os.path.join("/bin", exe) if os.path.exists(newexe1): logg.error(" but this does exist: %s %s", " " * len(execs), newexe1) elif os.path.exists(newexe2): logg.error(" but this does exist: %s %s", " " * len(execs), newexe2) if not abspath and not notexists: return True if True: filename = conf.filename() if len(filename) > 45: filename = "..." + filename[-42:] logg.error(" !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") logg.error(" Found %s problems in %s", abspath + notexists, filename) time.sleep(1) if abspath: logg.error(" The SystemD commands must always be absolute paths by definition.") time.sleep(1) logg.error(" Earlier versions of systemctl.py did use a subshell thus using $PATH") time.sleep(1) logg.error(" however newer versions use execve just like the real SystemD daemon") time.sleep(1) logg.error(" so that your docker-only service scripts may start to fail suddenly.") time.sleep(1) if notexists: logg.error(" Now %s executable paths were not found in the current environment.", notexists) time.sleep(1) logg.error(" !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") return False def show_modules(self, *modules): """ [PATTERN]... -- Show properties of one or more units Show properties of one or more units (or the manager itself). If no argument is specified, properties of the manager will be shown. If a unit name is specified, properties of the unit is shown. By default, empty properties are suppressed. Use --all to show those too. To select specific properties to show, use --property=. This command is intended to be used whenever computer-parsable output is required. Use status if you are looking for formatted human-readable output. NOTE: only a subset of properties is implemented """ notfound = [] found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) units += [ module ] found_all = False continue for unit in matched: if unit not in units: units += [ unit ] return self.show_units(units) + notfound # and found_all def show_units(self, units): logg.debug("show --property=%s", self._unit_property) result = [] for unit in units: if result: result += [ "" ] for var, value in self.show_unit_items(unit): if self._unit_property: if self._unit_property != var: continue else: if not value and not self._show_all: continue result += [ "%s=%s" % (var, value) ] return result def show_unit_items(self, unit): """ [UNIT]... -- show properties of a unit. """ logg.info("try read unit %s", unit) conf = self.get_unit_conf(unit) for entry in self.each_unit_items(unit, conf): yield entry def each_unit_items(self, unit, conf): loaded = conf.loaded() if not loaded: loaded = "not-loaded" if "NOT-FOUND" in self.get_description_from(conf): loaded = "not-found" yield "Id", unit yield "Names", unit yield "Description", self.get_description_from(conf) # conf.get("Unit", "Description") yield "PIDFile", self.pid_file_from(conf) # not self.pid_file_from w/o default location yield "MainPID", self.active_pid_from(conf) or "0" # status["MainPID"] or PIDFile-read yield "SubState", self.get_substate_from(conf) # status["SubState"] or notify-result yield "ActiveState", self.get_active_from(conf) # status["ActiveState"] yield "LoadState", loaded yield "UnitFileState", self.enabled_from(conf) yield "TimeoutStartUSec", seconds_to_time(self.get_TimeoutStartSec(conf)) yield "TimeoutStopUSec", seconds_to_time(self.get_TimeoutStopSec(conf)) env_parts = [] for env_part in conf.getlist("Service", "Environment", []): env_parts.append(self.expand_special(env_part, conf)) if env_parts: yield "Environment", " ".join(env_parts) env_files = [] for env_file in conf.getlist("Service", "EnvironmentFile", []): env_files.append(self.expand_special(env_file, conf)) if env_files: yield "EnvironmentFile", " ".join(env_files) # igno_centos = [ "netconsole", "network" ] igno_opensuse = [ "raw", "pppoe", "*.local", "boot.*", "rpmconf*", "purge-kernels.service", "after-local.service", "postfix*" ] igno_ubuntu = [ "mount*", "umount*", "ondemand", "*.local" ] igno_always = [ "network*", "dbus", "systemd-*" ] def _ignored_unit(self, unit, ignore_list): for ignore in ignore_list: if fnmatch.fnmatchcase(unit, ignore): return True # ignore if fnmatch.fnmatchcase(unit, ignore+".service"): return True # ignore return False def system_default_services(self, sysv = "S", default_target = None): """ show the default services This is used internally to know the list of service to be started in 'default' runlevel when the container is started through default initialisation. It will ignore a number of services - use '--all' to show a longer list of services and use '--all --force' if not even a minimal filter shall be used. """ igno = self.igno_centos + self.igno_opensuse + self.igno_ubuntu + self.igno_always if self._show_all: igno = self.igno_always if self._force: igno = [] logg.debug("ignored services filter for default.target:\n\t%s", igno) return self.enabled_default_services(sysv, default_target, igno) def enabled_default_services(self, sysv = "S", default_target = None, igno = []): if self.user_mode(): return self.enabled_default_user_services(sysv, default_target, igno) else: return self.enabled_default_system_services(sysv, default_target, igno) def enabled_default_user_services(self, sysv = "S", default_target = None, igno = []): logg.debug("check for default user services") default_target = default_target or self._default_target default_services = [] for basefolder in self.user_folders(): if not basefolder: continue folder = self.default_enablefolder(default_target, basefolder) if self._root: folder = os_path(self._root, folder) if os.path.isdir(folder): for unit in sorted(os.listdir(folder)): path = os.path.join(folder, unit) if os.path.isdir(path): continue if self._ignored_unit(unit, igno): continue # ignore if unit.endswith(".service"): default_services.append(unit) for basefolder in self.system_folders(): if not basefolder: continue folder = self.default_enablefolder(default_target, basefolder) if self._root: folder = os_path(self._root, folder) if os.path.isdir(folder): for unit in sorted(os.listdir(folder)): path = os.path.join(folder, unit) if os.path.isdir(path): continue if self._ignored_unit(unit, igno): continue # ignore if unit.endswith(".service"): conf = self.load_unit_conf(unit) if self.not_user_conf(conf): pass else: default_services.append(unit) return default_services def enabled_default_system_services(self, sysv = "S", default_target = None, igno = []): logg.debug("check for default system services") default_target = default_target or self._default_target default_services = [] for basefolder in self.system_folders(): if not basefolder: continue folder = self.default_enablefolder(default_target, basefolder) if self._root: folder = os_path(self._root, folder) if os.path.isdir(folder): for unit in sorted(os.listdir(folder)): path = os.path.join(folder, unit) if os.path.isdir(path): continue if self._ignored_unit(unit, igno): continue # ignore if unit.endswith(".service"): default_services.append(unit) for folder in [ self.rc3_root_folder() ]: if not os.path.isdir(folder): logg.warning("non-existant %s", folder) continue for unit in sorted(os.listdir(folder)): path = os.path.join(folder, unit) if os.path.isdir(path): continue m = re.match(sysv+r"\d\d(.*)", unit) if m: service = m.group(1) unit = service + ".service" if self._ignored_unit(unit, igno): continue # ignore default_services.append(unit) return default_services def system_default(self, arg = True): """ start units for default system level This will go through the enabled services in the default 'multi-user.target'. However some services are ignored as being known to be installation garbage from unintended services. Use '--all' so start all of the installed services and with '--all --force' even those services that are otherwise wrong. /// SPECIAL: with --now or --init the init-loop is run and afterwards a system_halt is performed with the enabled services to be stopped.""" self.sysinit_status(SubState = "initializing") logg.info("system default requested - %s", arg) init = self._now or self._init self.start_system_default(init = init) def start_system_default(self, init = False): """ detect the default.target services and start them. When --init is given then the init-loop is run and the services are stopped again by 'systemctl halt'.""" default_target = self._default_target default_services = self.system_default_services("S", default_target) self.sysinit_status(SubState = "starting") self.start_units(default_services) logg.info(" -- system is up") if init: logg.info("init-loop start") sig = self.init_loop_until_stop(default_services) logg.info("init-loop %s", sig) self.stop_system_default() def stop_system_default(self): """ detect the default.target services and stop them. This is commonly run through 'systemctl halt' or at the end of a 'systemctl --init default' loop.""" default_target = self._default_target default_services = self.system_default_services("K", default_target) self.sysinit_status(SubState = "stopping") self.stop_units(default_services) logg.info(" -- system is down") def system_halt(self, arg = True): """ stop units from default system level """ logg.info("system halt requested - %s", arg) self.stop_system_default() try: os.kill(1, signal.SIGQUIT) # exit init-loop on no_more_procs except Exception as e: logg.warning("SIGQUIT to init-loop on PID-1: %s", e) def system_get_default(self): """ get current default run-level""" current = self._default_target folder = os_path(self._root, self.mask_folder()) target = os.path.join(folder, "default.target") if os.path.islink(target): current = os.path.basename(os.readlink(target)) return current def set_default_modules(self, *modules): """ set current default run-level""" if not modules: logg.debug(".. no runlevel given") return (1, "Too few arguments") current = self._default_target folder = os_path(self._root, self.mask_folder()) target = os.path.join(folder, "default.target") if os.path.islink(target): current = os.path.basename(os.readlink(target)) err, msg = 0, "" for module in modules: if module == current: continue targetfile = None for targetname, targetpath in self.each_target_file(): if targetname == module: targetfile = targetpath if not targetfile: err, msg = 3, "No such runlevel %s" % (module) continue # if os.path.islink(target): os.unlink(target) if not os.path.isdir(os.path.dirname(target)): os.makedirs(os.path.dirname(target)) os.symlink(targetfile, target) msg = "Created symlink from %s -> %s" % (target, targetfile) logg.debug("%s", msg) return (err, msg) def init_modules(self, *modules): """ [UNIT*] -- init loop: '--init default' or '--init start UNIT*' The systemctl init service will start the enabled 'default' services, and then wait for any zombies to be reaped. When a SIGINT is received then a clean shutdown of the enabled services is ensured. A Control-C in in interactive mode will also run 'stop' on all the enabled services. // When a UNIT name is given then only that one is started instead of the services in the 'default.target'. Using 'init UNIT' is better than '--init start UNIT' because the UNIT is also stopped cleanly even when it was never enabled in the system. /// SPECIAL: when using --now then only the init-loop is started, with the reap-zombies function and waiting for an interrupt. (and no unit is started/stoppped wether given or not). """ if self._now: return self.init_loop_until_stop([]) if not modules: # like 'systemctl --init default' if self._now or self._show_all: logg.debug("init default --now --all => no_more_procs") self.exit_when_no_more_procs = True return self.start_system_default(init = True) # # otherwise quit when all the init-services have died self.exit_when_no_more_services = True if self._now or self._show_all: logg.debug("init services --now --all => no_more_procs") self.exit_when_no_more_procs = True found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] logg.info("init %s -> start %s", ",".join(modules), ",".join(units)) done = self.start_units(units, init = True) logg.info("-- init is done") return done # and found_all def start_log_files(self, units): self._log_file = {} self._log_hold = {} for unit in units: conf = self.load_unit_conf(unit) if not conf: continue log_path = self.path_journal_log(conf) try: opened = os.open(log_path, os.O_RDONLY | os.O_NONBLOCK) self._log_file[unit] = opened self._log_hold[unit] = b"" except Exception as e: logg.error("can not open %s log: %s\n\t%s", unit, log_path, e) def read_log_files(self, units): BUFSIZE=8192 for unit in units: if unit in self._log_file: new_text = b"" while True: buf = os.read(self._log_file[unit], BUFSIZE) if not buf: break new_text += buf continue text = self._log_hold[unit] + new_text if not text: continue lines = text.split(b"\n") if not text.endswith(b"\n"): self._log_hold[unit] = lines[-1] lines = lines[:-1] for line in lines: prefix = unit.encode("utf-8") content = prefix+b": "+line+b"\n" os.write(1, content) try: os.fsync(1) except: pass def stop_log_files(self, units): for unit in units: try: if unit in self._log_file: if self._log_file[unit]: os.close(self._log_file[unit]) except Exception as e: logg.error("can not close log: %s\n\t%s", unit, e) self._log_file = {} self._log_hold = {} def init_loop_until_stop(self, units): """ this is the init-loop - it checks for any zombies to be reaped and waits for an interrupt. When a SIGTERM /SIGINT /Control-C signal is received then the signal name is returned. Any other signal will just raise an Exception like one would normally expect. As a special the 'systemctl halt' emits SIGQUIT which puts it into no_more_procs mode.""" signal.signal(signal.SIGQUIT, lambda signum, frame: ignore_signals_and_raise_keyboard_interrupt("SIGQUIT")) signal.signal(signal.SIGINT, lambda signum, frame: ignore_signals_and_raise_keyboard_interrupt("SIGINT")) signal.signal(signal.SIGTERM, lambda signum, frame: ignore_signals_and_raise_keyboard_interrupt("SIGTERM")) self.start_log_files(units) self.sysinit_status(ActiveState = "active", SubState = "running") result = None while True: try: time.sleep(InitLoopSleep) self.read_log_files(units) ##### the reaper goes round running = self.system_reap_zombies() # logg.debug("reap zombies - init-loop found %s running procs", running) if self.exit_when_no_more_services: active = False for unit in units: conf = self.load_unit_conf(unit) if not conf: continue if self.is_active_from(conf): active = True if not active: logg.info("no more services - exit init-loop") break if self.exit_when_no_more_procs: if not running: logg.info("no more procs - exit init-loop") break except KeyboardInterrupt as e: if e.args and e.args[0] == "SIGQUIT": # the original systemd puts a coredump on that signal. logg.info("SIGQUIT - switch to no more procs check") self.exit_when_no_more_procs = True continue signal.signal(signal.SIGTERM, signal.SIG_DFL) signal.signal(signal.SIGINT, signal.SIG_DFL) logg.info("interrupted - exit init-loop") result = e.message or "STOPPED" break except Exception as e: logg.info("interrupted - exception %s", e) raise self.sysinit_status(ActiveState = None, SubState = "degraded") self.read_log_files(units) self.read_log_files(units) self.stop_log_files(units) logg.debug("done - init loop") return result def system_reap_zombies(self): """ check to reap children """ selfpid = os.getpid() running = 0 for pid in os.listdir("/proc"): try: pid = int(pid) except: continue if pid == selfpid: continue proc_status = "/proc/%s/status" % pid if os.path.isfile(proc_status): zombie = False ppid = -1 try: for line in open(proc_status): m = re.match(r"State:\s*Z.*", line) if m: zombie = True m = re.match(r"PPid:\s*(\d+)", line) if m: ppid = int(m.group(1)) except IOError as e: logg.warning("%s : %s", proc_status, e) continue if zombie and ppid == os.getpid(): logg.info("reap zombie %s", pid) try: os.waitpid(pid, os.WNOHANG) except OSError as e: logg.warning("reap zombie %s: %s", e.strerror) if os.path.isfile(proc_status): if pid > 1: running += 1 return running # except PID 0 and PID 1 def sysinit_status(self, **status): conf = self.sysinit_target() self.write_status_from(conf, **status) def sysinit_target(self): if not self._sysinit_target: self._sysinit_target = self.default_unit_conf("sysinit.target", "System Initialization") return self._sysinit_target def is_system_running(self): conf = self.sysinit_target() status_file = self.status_file_from(conf) if not os.path.isfile(status_file): time.sleep(EpsilonTime) if not os.path.isfile(status_file): return "offline" status = self.read_status_from(conf) return status.get("SubState", "unknown") def system_is_system_running(self): state = self.is_system_running() if self._quiet: return state in [ "running" ] else: if state in [ "running" ]: return True, state else: return False, state def wait_system(self, target = None): target = target or SysInitTarget for attempt in xrange(int(SysInitWait)): state = self.is_system_running() if "init" in state: if target in [ "sysinit.target", "basic.target" ]: logg.info("system not initialized - wait %s", target) time.sleep(1) continue if "start" in state or "stop" in state: if target in [ "basic.target" ]: logg.info("system not running - wait %s", target) time.sleep(1) continue if "running" not in state: logg.info("system is %s", state) break def pidlist_of(self, pid): try: pid = int(pid) except: return [] pidlist = [ pid ] pids = [ pid ] for depth in xrange(ProcMaxDepth): for pid in os.listdir("/proc"): try: pid = int(pid) except: continue proc_status = "/proc/%s/status" % pid if os.path.isfile(proc_status): try: for line in open(proc_status): if line.startswith("PPid:"): ppid = line[len("PPid:"):].strip() try: ppid = int(ppid) except: continue if ppid in pidlist and pid not in pids: pids += [ pid ] except IOError as e: logg.warning("%s : %s", proc_status, e) continue if len(pids) != len(pidlist): pidlist = pids[:] continue return pids def etc_hosts(self): path = "/etc/hosts" if self._root: return os_path(self._root, path) return path def force_ipv4(self, *args): """ only ipv4 localhost in /etc/hosts """ logg.debug("checking /etc/hosts for '::1 localhost'") lines = [] for line in open(self.etc_hosts()): if "::1" in line: newline = re.sub("\\slocalhost\\s", " ", line) if line != newline: logg.info("/etc/hosts: '%s' => '%s'", line.rstrip(), newline.rstrip()) line = newline lines.append(line) f = open(self.etc_hosts(), "w") for line in lines: f.write(line) f.close() def force_ipv6(self, *args): """ only ipv4 localhost in /etc/hosts """ logg.debug("checking /etc/hosts for '127.0.0.1 localhost'") lines = [] for line in open(self.etc_hosts()): if "127.0.0.1" in line: newline = re.sub("\\slocalhost\\s", " ", line) if line != newline: logg.info("/etc/hosts: '%s' => '%s'", line.rstrip(), newline.rstrip()) line = newline lines.append(line) f = open(self.etc_hosts(), "w") for line in lines: f.write(line) f.close() def show_help(self, *args): """[command] -- show this help """ lines = [] okay = True prog = os.path.basename(sys.argv[0]) if not args: argz = {} for name in dir(self): arg = None if name.startswith("system_"): arg = name[len("system_"):].replace("_","-") if name.startswith("show_"): arg = name[len("show_"):].replace("_","-") if name.endswith("_of_unit"): arg = name[:-len("_of_unit")].replace("_","-") if name.endswith("_modules"): arg = name[:-len("_modules")].replace("_","-") if arg: argz[arg] = name lines.append("%s command [options]..." % prog) lines.append("") lines.append("Commands:") for arg in sorted(argz): name = argz[arg] method = getattr(self, name) doc = "..." doctext = getattr(method, "__doc__") if doctext: doc = doctext elif not self._show_all: continue # pragma: nocover firstline = doc.split("\n")[0] doc_text = firstline.strip() if "--" not in firstline: doc_text = "-- " + doc_text lines.append(" %s %s" % (arg, firstline.strip())) return lines for arg in args: arg = arg.replace("-","_") func1 = getattr(self.__class__, arg+"_modules", None) func2 = getattr(self.__class__, arg+"_of_unit", None) func3 = getattr(self.__class__, "show_"+arg, None) func4 = getattr(self.__class__, "system_"+arg, None) func = func1 or func2 or func3 or func4 if func is None: print("error: no such command '%s'" % arg) okay = False else: doc_text = "..." doc = getattr(func, "__doc__", None) if doc: doc_text = doc.replace("\n","\n\n", 1).strip() if "--" not in doc_text: doc_text = "-- " + doc_text else: logg.debug("__doc__ of %s is none", func_name) if not self._show_all: continue lines.append("%s %s %s" % (prog, arg, doc_text)) if not okay: self.show_help() return False return lines def systemd_version(self): """ the version line for systemd compatibility """ return "systemd %s\n - via systemctl.py %s" % (self._systemd_version, __version__) def systemd_features(self): """ the info line for systemd features """ features1 = "-PAM -AUDIT -SELINUX -IMA -APPARMOR -SMACK" features2 = " +SYSVINIT -UTMP -LIBCRYPTSETUP -GCRYPT -GNUTLS" features3 = " -ACL -XZ -LZ4 -SECCOMP -BLKID -ELFUTILS -KMOD -IDN" return features1+features2+features3 def systems_version(self): return [ self.systemd_version(), self.systemd_features() ] def print_result(result): # logg_info = logg.info # logg_debug = logg.debug def logg_info(*msg): pass def logg_debug(*msg): pass exitcode = 0 if result is None: logg_info("EXEC END None") elif result is True: logg_info("EXEC END True") result = None exitcode = 0 elif result is False: logg_info("EXEC END False") result = None exitcode = 1 elif isinstance(result, tuple) and len(result) == 2: exitcode, status = result logg_info("EXEC END %s '%s'", exitcode, status) if exitcode is True: exitcode = 0 if exitcode is False: exitcode = 1 result = status elif isinstance(result, int): logg_info("EXEC END %s", result) exitcode = result result = None # if result is None: pass elif isinstance(result, string_types): print(result) result1 = result.split("\n")[0][:-20] if result == result1: logg_info("EXEC END '%s'", result) else: logg_info("EXEC END '%s...'", result1) logg_debug(" END '%s'", result) elif isinstance(result, list) or hasattr(result, "next") or hasattr(result, "__next__"): shown = 0 for element in result: if isinstance(element, tuple): print("\t".join([ str(elem) for elem in element] )) else: print(element) shown += 1 logg_info("EXEC END %s items", shown) logg_debug(" END %s", result) elif hasattr(result, "keys"): shown = 0 for key in sorted(result.keys()): element = result[key] if isinstance(element, tuple): print(key,"=","\t".join([ str(elem) for elem in element])) else: print("%s=%s" % (key,element)) shown += 1 logg_info("EXEC END %s items", shown) logg_debug(" END %s", result) else: logg.warning("EXEC END Unknown result type %s", str(type(result))) return exitcode if __name__ == "__main__": import optparse _o = optparse.OptionParser("%prog [options] command [name...]", epilog="use 'help' command for more information") _o.add_option("--version", action="store_true", help="Show package version") _o.add_option("--system", action="store_true", default=False, help="Connect to system manager (default)") # overrides --user _o.add_option("--user", action="store_true", default=_user_mode, help="Connect to user service manager") # _o.add_option("-H", "--host", metavar="[USER@]HOST", # help="Operate on remote host*") # _o.add_option("-M", "--machine", metavar="CONTAINER", # help="Operate on local container*") _o.add_option("-t","--type", metavar="TYPE", dest="unit_type", default=_unit_type, help="List units of a particual type") _o.add_option("--state", metavar="STATE", default=_unit_state, help="List units with particular LOAD or SUB or ACTIVE state") _o.add_option("-p", "--property", metavar="NAME", dest="unit_property", default=_unit_property, help="Show only properties by this name") _o.add_option("-a", "--all", action="store_true", dest="show_all", default=_show_all, help="Show all loaded units/properties, including dead empty ones. To list all units installed on the system, use the 'list-unit-files' command instead") _o.add_option("-l","--full", action="store_true", default=_full, help="Don't ellipsize unit names on output (never ellipsized)") _o.add_option("--reverse", action="store_true", help="Show reverse dependencies with 'list-dependencies' (ignored)") _o.add_option("--job-mode", metavar="MODE", help="Specifiy how to deal with already queued jobs, when queuing a new job (ignored)") _o.add_option("--show-types", action="store_true", help="When showing sockets, explicitly show their type (ignored)") _o.add_option("-i","--ignore-inhibitors", action="store_true", help="When shutting down or sleeping, ignore inhibitors (ignored)") _o.add_option("--kill-who", metavar="WHO", help="Who to send signal to (ignored)") _o.add_option("-s", "--signal", metavar="SIG", help="Which signal to send (ignored)") _o.add_option("--now", action="store_true", default=_now, help="Start or stop unit in addition to enabling or disabling it") _o.add_option("-q","--quiet", action="store_true", default=_quiet, help="Suppress output") _o.add_option("--no-block", action="store_true", default=False, help="Do not wait until operation finished (ignored)") _o.add_option("--no-legend", action="store_true", default=_no_legend, help="Do not print a legend (column headers and hints)") _o.add_option("--no-wall", action="store_true", default=False, help="Don't send wall message before halt/power-off/reboot (ignored)") _o.add_option("--no-reload", action="store_true", help="Don't reload daemon after en-/dis-abling unit files (ignored)") _o.add_option("--no-ask-password", action="store_true", default=_no_ask_password, help="Do not ask for system passwords") # _o.add_option("--global", action="store_true", dest="globally", default=_globally, # help="Enable/disable unit files globally") # for all user logins # _o.add_option("--runtime", action="store_true", # help="Enable unit files only temporarily until next reboot") _o.add_option("--force", action="store_true", default=_force, help="When enabling unit files, override existing symblinks / When shutting down, execute action immediately") _o.add_option("--preset-mode", metavar="TYPE", default=_preset_mode, help="Apply only enable, only disable, or all presets [%default]") _o.add_option("--root", metavar="PATH", default=_root, help="Enable unit files in the specified root directory (used for alternative root prefix)") _o.add_option("-n","--lines", metavar="NUM", help="Number of journal entries to show (ignored)") _o.add_option("-o","--output", metavar="CAT", help="change journal output mode [short, ..., cat] (ignored)") _o.add_option("--plain", action="store_true", help="Print unit dependencies as a list instead of a tree (ignored)") _o.add_option("--no-pager", action="store_true", help="Do not pipe output into pager (ignored)") # _o.add_option("--coverage", metavar="OPTIONLIST", default=COVERAGE, help="..support for coverage (e.g. spawn,oldest,sleep) [%default]") _o.add_option("-e","--extra-vars", "--environment", metavar="NAME=VAL", action="append", default=[], help="..override settings in the syntax of 'Environment='") _o.add_option("-v","--verbose", action="count", default=0, help="..increase debugging information level") _o.add_option("-4","--ipv4", action="store_true", default=False, help="..only keep ipv4 localhost in /etc/hosts") _o.add_option("-6","--ipv6", action="store_true", default=False, help="..only keep ipv6 localhost in /etc/hosts") _o.add_option("-1","--init", action="store_true", default=False, help="..keep running as init-process (default if PID 1)") opt, args = _o.parse_args() logging.basicConfig(level = max(0, logging.FATAL - 10 * opt.verbose)) logg.setLevel(max(0, logging.ERROR - 10 * opt.verbose)) # COVERAGE = opt.coverage if "sleep" in COVERAGE: MinimumTimeoutStartSec = 7 MinimumTimeoutStopSec = 7 if "quick" in COVERAGE: MinimumTimeoutStartSec = 4 MinimumTimeoutStopSec = 4 DefaultTimeoutStartSec = 9 DefaultTimeoutStopSec = 9 _extra_vars = opt.extra_vars _force = opt.force _full = opt.full _no_legend = opt.no_legend _no_ask_password = opt.no_ask_password _now = opt.now _preset_mode = opt.preset_mode _quiet = opt.quiet _root = opt.root _show_all = opt.show_all _unit_state = opt.state _unit_type = opt.unit_type _unit_property = opt.unit_property # being PID 1 (or 0) in a container will imply --init _pid = os.getpid() _init = opt.init or _pid in [ 1, 0 ] _user_mode = opt.user if os.geteuid() and _pid in [ 1, 0 ]: _user_mode = True if opt.system: _user_mode = False # override --user # if _user_mode: systemctl_debug_log = os_path(_root, _var_path(_systemctl_debug_log)) systemctl_extra_log = os_path(_root, _var_path(_systemctl_extra_log)) else: systemctl_debug_log = os_path(_root, _systemctl_debug_log) systemctl_extra_log = os_path(_root, _systemctl_extra_log) if os.access(systemctl_extra_log, os.W_OK): loggfile = logging.FileHandler(systemctl_extra_log) loggfile.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s")) logg.addHandler(loggfile) logg.setLevel(max(0, logging.INFO - 10 * opt.verbose)) if os.access(systemctl_debug_log, os.W_OK): loggfile = logging.FileHandler(systemctl_debug_log) loggfile.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s")) logg.addHandler(loggfile) logg.setLevel(logging.DEBUG) logg.info("EXEC BEGIN %s %s%s%s", os.path.realpath(sys.argv[0]), " ".join(args), _user_mode and " --user" or " --system", _init and " --init" or "", ) # # systemctl = Systemctl() if opt.version: args = [ "version" ] if not args: if _init: args = [ "default" ] else: args = [ "list-units" ] logg.debug("======= systemctl.py " + " ".join(args)) command = args[0] modules = args[1:] if opt.ipv4: systemctl.force_ipv4() elif opt.ipv6: systemctl.force_ipv6() found = False # command NAME if command.startswith("__"): command_name = command[2:] command_func = getattr(systemctl, command_name, None) if callable(command_func) and not found: found = True result = command_func(*modules) command_name = command.replace("-","_").replace(".","_")+"_modules" command_func = getattr(systemctl, command_name, None) if callable(command_func) and not found: systemctl.wait_boot(command_name) found = True result = command_func(*modules) command_name = "show_"+command.replace("-","_").replace(".","_") command_func = getattr(systemctl, command_name, None) if callable(command_func) and not found: systemctl.wait_boot(command_name) found = True result = command_func(*modules) command_name = "system_"+command.replace("-","_").replace(".","_") command_func = getattr(systemctl, command_name, None) if callable(command_func) and not found: systemctl.wait_boot(command_name) found = True result = command_func() command_name = "systems_"+command.replace("-","_").replace(".","_") command_func = getattr(systemctl, command_name, None) if callable(command_func) and not found: systemctl.wait_boot(command_name) found = True result = command_func() if not found: logg.error("Unknown operation %s.", command) sys.exit(1) # sys.exit(print_result(result)) docker-systemctl-replacement-1.4.3424/files/docker/systemctl3.py000077500000000000000000006065171355427602500246320ustar00rootroot00000000000000#! /usr/bin/python3 from __future__ import print_function __copyright__ = "(C) 2016-2019 Guido U. Draheim, licensed under the EUPL" __version__ = "1.4.3424" import logging logg = logging.getLogger("systemctl") import re import fnmatch import shlex import collections import errno import os import sys import signal import time import socket import datetime import fcntl if sys.version[0] == '2': string_types = basestring BlockingIOError = IOError else: string_types = str xrange = range COVERAGE = os.environ.get("SYSTEMCTL_COVERAGE", "") DEBUG_AFTER = os.environ.get("SYSTEMCTL_DEBUG_AFTER", "") or False EXIT_WHEN_NO_MORE_PROCS = os.environ.get("SYSTEMCTL_EXIT_WHEN_NO_MORE_PROCS", "") or False EXIT_WHEN_NO_MORE_SERVICES = os.environ.get("SYSTEMCTL_EXIT_WHEN_NO_MORE_SERVICES", "") or False FOUND_OK = 0 FOUND_INACTIVE = 2 FOUND_UNKNOWN = 4 # defaults for options _extra_vars = [] _force = False _full = False _now = False _no_legend = False _no_ask_password = False _preset_mode = "all" _quiet = False _root = "" _unit_type = None _unit_state = None _unit_property = None _show_all = False _user_mode = False # common default paths _default_target = "multi-user.target" _system_folder1 = "/etc/systemd/system" _system_folder2 = "/var/run/systemd/system" _system_folder3 = "/usr/lib/systemd/system" _system_folder4 = "/lib/systemd/system" _system_folder9 = None _user_folder1 = "~/.config/systemd/user" _user_folder2 = "/etc/systemd/user" _user_folder3 = "~.local/share/systemd/user" _user_folder4 = "/usr/lib/systemd/user" _user_folder9 = None _init_folder1 = "/etc/init.d" _init_folder2 = "/var/run/init.d" _init_folder9 = None _preset_folder1 = "/etc/systemd/system-preset" _preset_folder2 = "/var/run/systemd/system-preset" _preset_folder3 = "/usr/lib/systemd/system-preset" _preset_folder4 = "/lib/systemd/system-preset" _preset_folder9 = None SystemCompatibilityVersion = 219 SysInitTarget = "sysinit.target" SysInitWait = 5 # max for target EpsilonTime = 0.1 MinimumYield = 0.5 MinimumTimeoutStartSec = 4 MinimumTimeoutStopSec = 4 DefaultTimeoutStartSec = int(os.environ.get("SYSTEMCTL_TIMEOUT_START_SEC", 90)) # official value DefaultTimeoutStopSec = int(os.environ.get("SYSTEMCTL_TIMEOUT_STOP_SEC", 90)) # official value DefaultMaximumTimeout = int(os.environ.get("SYSTEMCTL_MAXIMUM_TIMEOUT", 200)) # overrides all other InitLoopSleep = int(os.environ.get("SYSTEMCTL_INITLOOP", 5)) ProcMaxDepth = 100 MaxLockWait = None # equals DefaultMaximumTimeout DefaultPath = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ResetLocale = ["LANG", "LANGUAGE", "LC_CTYPE", "LC_NUMERIC", "LC_TIME", "LC_COLLATE", "LC_MONETARY", "LC_MESSAGES", "LC_PAPER", "LC_NAME", "LC_ADDRESS", "LC_TELEPHONE", "LC_MEASUREMENT", "LC_IDENTIFICATION", "LC_ALL"] # The systemd default is NOTIFY_SOCKET="/var/run/systemd/notify" _notify_socket_folder = "/var/run/systemd" # alias /run/systemd _pid_file_folder = "/var/run" _journal_log_folder = "/var/log/journal" _systemctl_debug_log = "/var/log/systemctl.debug.log" _systemctl_extra_log = "/var/log/systemctl.log" _default_targets = [ "poweroff.target", "rescue.target", "sysinit.target", "basic.target", "multi-user.target", "graphical.target", "reboot.target" ] _feature_targets = [ "network.target", "remote-fs.target", "local-fs.target", "timers.target", "nfs-client.target" ] _all_common_targets = [ "default.target" ] + _default_targets + _feature_targets # inside a docker we pretend the following _all_common_enabled = [ "default.target", "multi-user.target", "remote-fs.target" ] _all_common_disabled = [ "graphical.target", "resue.target", "nfs-client.target" ] _runlevel_mappings = {} # the official list _runlevel_mappings["0"] = "poweroff.target" _runlevel_mappings["1"] = "rescue.target" _runlevel_mappings["2"] = "multi-user.target" _runlevel_mappings["3"] = "multi-user.target" _runlevel_mappings["4"] = "multi-user.target" _runlevel_mappings["5"] = "graphical.target" _runlevel_mappings["6"] = "reboot.target" _sysv_mappings = {} # by rule of thumb _sysv_mappings["$local_fs"] = "local-fs.target" _sysv_mappings["$network"] = "network.target" _sysv_mappings["$remote_fs"] = "remote-fs.target" _sysv_mappings["$timer"] = "timers.target" def shell_cmd(cmd): return " ".join(["'%s'" % part for part in cmd]) def to_int(value, default = 0): try: return int(value) except: return default def to_list(value): if isinstance(value, string_types): return [ value ] return value def unit_of(module): if "." not in module: return module + ".service" return module def os_path(root, path): if not root: return path if not path: return path while path.startswith(os.path.sep): path = path[1:] return os.path.join(root, path) def os_getlogin(): """ NOT using os.getlogin() """ import pwd return pwd.getpwuid(os.geteuid()).pw_name def get_runtime_dir(): explicit = os.environ.get("XDG_RUNTIME_DIR", "") if explicit: return explicit user = os_getlogin() return "/tmp/run-"+user def get_home(): explicit = os.environ.get("HOME", "") if explicit: return explicit return os.path.expanduser("~") def _var_path(path): """ assumes that the path starts with /var - when in user mode it shall be moved to /run/user/1001/run/ or as a fallback path to /tmp/run-{user}/ so that you may find /var/log in /tmp/run-{user}/log ..""" if path.startswith("/var"): runtime = get_runtime_dir() # $XDG_RUNTIME_DIR if not os.path.isdir(runtime): os.makedirs(runtime) os.chmod(runtime, 0o700) return re.sub("^(/var)?", get_runtime_dir(), path) return path def shutil_setuid(user = None, group = None): """ set fork-child uid/gid (returns pw-info env-settings)""" if group: import grp gid = grp.getgrnam(group).gr_gid os.setgid(gid) logg.debug("setgid %s '%s'", gid, group) if user: import pwd pw = pwd.getpwnam(user) if not group: gid = pw.pw_gid os.setgid(gid) logg.debug("setgid %s", gid) uid = pw.pw_uid os.setuid(uid) logg.debug("setuid %s '%s'", uid, user) home = pw.pw_dir shell = pw.pw_shell logname = pw.pw_name return { "USER": user, "LOGNAME": logname, "HOME": home, "SHELL": shell } return {} def shutil_truncate(filename): """ truncates the file (or creates a new empty file)""" filedir = os.path.dirname(filename) if not os.path.isdir(filedir): os.makedirs(filedir) f = open(filename, "w") f.write("") f.close() # http://stackoverflow.com/questions/568271/how-to-check-if-there-exists-a-process-with-a-given-pid def pid_exists(pid): """Check whether pid exists in the current process table.""" if pid is None: return False return _pid_exists(int(pid)) def _pid_exists(pid): """Check whether pid exists in the current process table. UNIX only. """ if pid < 0: return False if pid == 0: # According to "man 2 kill" PID 0 refers to every process # in the process group of the calling process. # On certain systems 0 is a valid PID but we have no way # to know that in a portable fashion. raise ValueError('invalid PID 0') try: os.kill(pid, 0) except OSError as err: if err.errno == errno.ESRCH: # ESRCH == No such process return False elif err.errno == errno.EPERM: # EPERM clearly means there's a process to deny access to return True else: # According to "man 2 kill" possible error values are # (EINVAL, EPERM, ESRCH) raise else: return True def pid_zombie(pid): """ may be a pid exists but it is only a zombie """ if pid is None: return False return _pid_zombie(int(pid)) def _pid_zombie(pid): """ may be a pid exists but it is only a zombie """ if pid < 0: return False if pid == 0: # According to "man 2 kill" PID 0 refers to every process # in the process group of the calling process. # On certain systems 0 is a valid PID but we have no way # to know that in a portable fashion. raise ValueError('invalid PID 0') check = "/proc/%s/status" % pid try: for line in open(check): if line.startswith("State:"): return "Z" in line except IOError as e: if e.errno != errno.ENOENT: logg.error("%s (%s): %s", check, e.errno, e) return False return False def checkstatus(cmd): if cmd.startswith("-"): return False, cmd[1:] else: return True, cmd # https://github.com/phusion/baseimage-docker/blob/rel-0.9.16/image/bin/my_init def ignore_signals_and_raise_keyboard_interrupt(signame): signal.signal(signal.SIGTERM, signal.SIG_IGN) signal.signal(signal.SIGINT, signal.SIG_IGN) raise KeyboardInterrupt(signame) class SystemctlConfigParser: """ A *.service files has a structure similar to an *.ini file but it is actually not like it. Settings may occur multiple times in each section and they create an implicit list. In reality all the settings are globally uniqute, so that an 'environment' can be printed without adding prefixes. Settings are continued with a backslash at the end of the line. """ def __init__(self, defaults=None, dict_type=None, allow_no_value=False): self._defaults = defaults or {} self._dict_type = dict_type or collections.OrderedDict self._allow_no_value = allow_no_value self._conf = self._dict_type() self._files = [] def defaults(self): return self._defaults def sections(self): return list(self._conf.keys()) def add_section(self, section): if section not in self._conf: self._conf[section] = self._dict_type() def has_section(self, section): return section in self._conf def has_option(self, section, option): if section not in self._conf: return False return option in self._conf[section] def set(self, section, option, value): if section not in self._conf: self._conf[section] = self._dict_type() if option not in self._conf[section]: self._conf[section][option] = [ value ] else: self._conf[section][option].append(value) if value is None: self._conf[section][option] = [] def get(self, section, option, default = None, allow_no_value = False): allow_no_value = allow_no_value or self._allow_no_value if section not in self._conf: if default is not None: return default if allow_no_value: return None logg.warning("section {} does not exist".format(section)) logg.warning(" have {}".format(self.sections())) raise AttributeError("section {} does not exist".format(section)) if option not in self._conf[section]: if default is not None: return default if allow_no_value: return None raise AttributeError("option {} in {} does not exist".format(option, section)) if not self._conf[section][option]: # i.e. an empty list if default is not None: return default if allow_no_value: return None raise AttributeError("option {} in {} is None".format(option, section)) return self._conf[section][option][0] # the first line in the list of configs def getlist(self, section, option, default = None, allow_no_value = False): allow_no_value = allow_no_value or self._allow_no_value if section not in self._conf: if default is not None: return default if allow_no_value: return [] logg.warning("section {} does not exist".format(section)) logg.warning(" have {}".format(self.sections())) raise AttributeError("section {} does not exist".format(section)) if option not in self._conf[section]: if default is not None: return default if allow_no_value: return [] raise AttributeError("option {} in {} does not exist".format(option, section)) return self._conf[section][option] # returns a list, possibly empty def read(self, filename): return self.read_sysd(filename) def read_sysd(self, filename): initscript = False initinfo = False section = None nextline = False name, text = "", "" if os.path.isfile(filename): self._files.append(filename) for orig_line in open(filename): if nextline: text += orig_line if text.rstrip().endswith("\\") or text.rstrip().endswith("\\\n"): text = text.rstrip() + "\n" else: self.set(section, name, text) nextline = False continue line = orig_line.strip() if not line: continue if line.startswith("#"): continue if line.startswith(";"): continue if line.startswith(".include"): logg.error("the '.include' syntax is deprecated. Use x.service.d/ drop-in files!") includefile = re.sub(r'^\.include[ ]*', '', line).rstrip() if not os.path.isfile(includefile): raise Exception("tried to include file that doesn't exist: %s" % includefile) self.read_sysd(includefile) continue if line.startswith("["): x = line.find("]") if x > 0: section = line[1:x] self.add_section(section) continue m = re.match(r"(\w+) *=(.*)", line) if not m: logg.warning("bad ini line: %s", line) raise Exception("bad ini line") name, text = m.group(1), m.group(2).strip() if text.endswith("\\") or text.endswith("\\\n"): nextline = True text = text + "\n" else: # hint: an empty line shall reset the value-list self.set(section, name, text and text or None) def read_sysv(self, filename): """ an LSB header is scanned and converted to (almost) equivalent settings of a SystemD ini-style input """ initscript = False initinfo = False section = None if os.path.isfile(filename): self._files.append(filename) for orig_line in open(filename): line = orig_line.strip() if line.startswith("#"): if " BEGIN INIT INFO" in line: initinfo = True section = "init.d" if " END INIT INFO" in line: initinfo = False if initinfo: m = re.match(r"\S+\s*(\w[\w_-]*):(.*)", line) if m: key, val = m.group(1), m.group(2).strip() self.set(section, key, val) continue description = self.get("init.d", "Description", "") if description: self.set("Unit", "Description", description) check = self.get("init.d", "Required-Start","") if check: for item in check.split(" "): if item.strip() in _sysv_mappings: self.set("Unit", "Requires", _sysv_mappings[item.strip()]) provides = self.get("init.d", "Provides", "") if provides: self.set("Install", "Alias", provides) # if already in multi-user.target then start it there. runlevels = self.get("init.d", "Default-Start","") if runlevels: for item in runlevels.split(" "): if item.strip() in _runlevel_mappings: self.set("Install", "WantedBy", _runlevel_mappings[item.strip()]) self.set("Service", "Type", "sysv") def filenames(self): return self._files # UnitConfParser = ConfigParser.RawConfigParser UnitConfParser = SystemctlConfigParser class SystemctlConf: def __init__(self, data, module = None): self.data = data # UnitConfParser self.env = {} self.status = None self.masked = None self.module = module self.drop_in_files = {} self._root = _root self._user_mode = _user_mode def os_path(self, path): return os_path(self._root, path) def os_path_var(self, path): if self._user_mode: return os_path(self._root, _var_path(path)) return os_path(self._root, path) def loaded(self): files = self.data.filenames() if self.masked: return "masked" if len(files): return "loaded" return "" def filename(self): """ returns the last filename that was parsed """ files = self.data.filenames() if files: return files[0] return None def overrides(self): """ drop-in files are loaded alphabetically by name, not by full path """ return [ self.drop_in_files[name] for name in sorted(self.drop_in_files) ] def name(self): """ the unit id or defaults to the file name """ name = self.module or "" filename = self.filename() if filename: name = os.path.basename(filename) return self.get("Unit", "Id", name) def set(self, section, name, value): return self.data.set(section, name, value) def get(self, section, name, default, allow_no_value = False): return self.data.get(section, name, default, allow_no_value) def getlist(self, section, name, default = None, allow_no_value = False): return self.data.getlist(section, name, default or [], allow_no_value) def getbool(self, section, name, default = None): value = self.data.get(section, name, default or "no") if value: if value[0] in "TtYy123456789": return True return False class PresetFile: def __init__(self): self._files = [] self._lines = [] def filename(self): """ returns the last filename that was parsed """ if self._files: return self._files[-1] return None def read(self, filename): self._files.append(filename) for line in open(filename): self._lines.append(line.strip()) return self def get_preset(self, unit): for line in self._lines: m = re.match(r"(enable|disable)\s+(\S+)", line) if m: status, pattern = m.group(1), m.group(2) if fnmatch.fnmatchcase(unit, pattern): logg.debug("%s %s => %s [%s]", status, pattern, unit, self.filename()) return status return None ## with waitlock(conf): self.start() class waitlock: def __init__(self, conf): self.conf = conf # currently unused self.opened = None self.lockfolder = conf.os_path_var(_notify_socket_folder) try: folder = self.lockfolder if not os.path.isdir(folder): os.makedirs(folder) except Exception as e: logg.warning("oops, %s", e) def lockfile(self): unit = "" if self.conf: unit = self.conf.name() return os.path.join(self.lockfolder, str(unit or "global") + ".lock") def __enter__(self): try: lockfile = self.lockfile() lockname = os.path.basename(lockfile) self.opened = os.open(lockfile, os.O_RDWR | os.O_CREAT, 0o600) for attempt in xrange(int(MaxLockWait or DefaultMaximumTimeout)): try: logg.debug("[%s] %s. trying %s _______ ", os.getpid(), attempt, lockname) fcntl.flock(self.opened, fcntl.LOCK_EX | fcntl.LOCK_NB) st = os.fstat(self.opened) if not st.st_nlink: logg.debug("[%s] %s. %s got deleted, trying again", os.getpid(), attempt, lockname) os.close(self.opened) self.opened = os.open(lockfile, os.O_RDWR | os.O_CREAT, 0o600) continue content = "{ 'systemctl': %s, 'lock': '%s' }\n" % (os.getpid(), lockname) os.write(self.opened, content.encode("utf-8")) logg.debug("[%s] %s. holding lock on %s", os.getpid(), attempt, lockname) return True except BlockingIOError as e: whom = os.read(self.opened, 4096) os.lseek(self.opened, 0, os.SEEK_SET) logg.info("[%s] %s. systemctl locked by %s", os.getpid(), attempt, whom.rstrip()) time.sleep(1) # until MaxLockWait continue logg.error("[%s] not able to get the lock to %s", os.getpid(), lockname) except Exception as e: logg.warning("[%s] oops %s, %s", os.getpid(), str(type(e)), e) #TODO# raise Exception("no lock for %s", self.unit or "global") return False def __exit__(self, type, value, traceback): try: os.lseek(self.opened, 0, os.SEEK_SET) os.ftruncate(self.opened, 0) if "removelockfile" in COVERAGE: # actually an optional implementation lockfile = self.lockfile() lockname = os.path.basename(lockfile) os.unlink(lockfile) # ino is kept allocated because opened by this process logg.debug("[%s] lockfile removed for %s", os.getpid(), lockname) fcntl.flock(self.opened, fcntl.LOCK_UN) os.close(self.opened) # implies an unlock but that has happend like 6 seconds later self.opened = None except Exception as e: logg.warning("oops, %s", e) def must_have_failed(waitpid, cmd): # found to be needed on ubuntu:16.04 to match test result from ubuntu:18.04 and other distros # .... I have tracked it down that python's os.waitpid() returns an exitcode==0 even when the # .... underlying process has actually failed with an exitcode<>0. It is unknown where that # .... bug comes from but it seems a bit serious to trash some very basic unix functionality. # .... Essentially a parent process does not get the correct exitcode from its own children. if cmd and cmd[0] == "/bin/kill": pid = None for arg in cmd[1:]: if not arg.startswith("-"): pid = arg if pid is None: # unknown $MAINPID if not waitpid.returncode: logg.error("waitpid %s did return %s => correcting as 11", cmd, waitpid.returncode) waitpidNEW = collections.namedtuple("waitpidNEW", ["pid", "returncode", "signal" ]) waitpid = waitpidNEW(waitpid.pid, 11, waitpid.signal) return waitpid def subprocess_waitpid(pid): waitpid = collections.namedtuple("waitpid", ["pid", "returncode", "signal" ]) run_pid, run_stat = os.waitpid(pid, 0) return waitpid(run_pid, os.WEXITSTATUS(run_stat), os.WTERMSIG(run_stat)) def subprocess_testpid(pid): testpid = collections.namedtuple("testpid", ["pid", "returncode", "signal" ]) run_pid, run_stat = os.waitpid(pid, os.WNOHANG) if run_pid: return testpid(run_pid, os.WEXITSTATUS(run_stat), os.WTERMSIG(run_stat)) else: return testpid(pid, None, 0) def parse_unit(name): # -> object(prefix, instance, suffix, ...., name, component) unit_name, suffix = name, "" has_suffix = name.rfind(".") if has_suffix > 0: unit_name = name[:has_suffix] suffix = name[has_suffix+1:] prefix, instance = unit_name, "" has_instance = unit_name.find("@") if has_instance > 0: prefix = unit_name[:has_instance] instance = unit_name[has_instance+1:] component = "" has_component = prefix.rfind("-") if has_component > 0: component = prefix[has_component+1:] UnitName = collections.namedtuple("UnitName", ["name", "prefix", "instance", "suffix", "component" ]) return UnitName(name, prefix, instance, suffix, component) def time_to_seconds(text, maximum = None): if maximum is None: maximum = DefaultMaximumTimeout value = 0 for part in str(text).split(" "): item = part.strip() if item == "infinity": return maximum if item.endswith("m"): try: value += 60 * int(item[:-1]) except: pass # pragma: no cover if item.endswith("min"): try: value += 60 * int(item[:-3]) except: pass # pragma: no cover elif item.endswith("ms"): try: value += int(item[:-2]) / 1000. except: pass # pragma: no cover elif item.endswith("s"): try: value += int(item[:-1]) except: pass # pragma: no cover elif item: try: value += int(item) except: pass # pragma: no cover if value > maximum: return maximum if not value: return 1 return value def seconds_to_time(seconds): seconds = float(seconds) mins = int(int(seconds) / 60) secs = int(int(seconds) - (mins * 60)) msecs = int(int(seconds * 1000) - (secs * 1000 + mins * 60000)) if mins and secs and msecs: return "%smin %ss %sms" % (mins, secs, msecs) elif mins and secs: return "%smin %ss" % (mins, secs) elif secs and msecs: return "%ss %sms" % (secs, msecs) elif mins and msecs: return "%smin %sms" % (mins, msecs) elif mins: return "%smin" % (mins) else: return "%ss" % (secs) def getBefore(conf): result = [] beforelist = conf.getlist("Unit", "Before", []) for befores in beforelist: for before in befores.split(" "): name = before.strip() if name and name not in result: result.append(name) return result def getAfter(conf): result = [] afterlist = conf.getlist("Unit", "After", []) for afters in afterlist: for after in afters.split(" "): name = after.strip() if name and name not in result: result.append(name) return result def compareAfter(confA, confB): idA = confA.name() idB = confB.name() for after in getAfter(confA): if after == idB: logg.debug("%s After %s", idA, idB) return -1 for after in getAfter(confB): if after == idA: logg.debug("%s After %s", idB, idA) return 1 for before in getBefore(confA): if before == idB: logg.debug("%s Before %s", idA, idB) return 1 for before in getBefore(confB): if before == idA: logg.debug("%s Before %s", idB, idA) return -1 return 0 def sortedAfter(conflist, cmp = compareAfter): # the normal sorted() does only look at two items # so if "A after C" and a list [A, B, C] then # it will see "A = B" and "B = C" assuming that # "A = C" and the list is already sorted. # # To make a totalsorted we have to create a marker # that informs sorted() that also B has a relation. # It only works when 'after' has a direction, so # anything without 'before' is a 'after'. In that # case we find that "B after C". class SortTuple: def __init__(self, rank, conf): self.rank = rank self.conf = conf sortlist = [ SortTuple(0, conf) for conf in conflist] for check in xrange(len(sortlist)): # maxrank = len(sortlist) changed = 0 for A in xrange(len(sortlist)): for B in xrange(len(sortlist)): if A != B: itemA = sortlist[A] itemB = sortlist[B] before = compareAfter(itemA.conf, itemB.conf) if before > 0 and itemA.rank <= itemB.rank: if DEBUG_AFTER: # pragma: no cover logg.info(" %-30s before %s", itemA.conf.name(), itemB.conf.name()) itemA.rank = itemB.rank + 1 changed += 1 if before < 0 and itemB.rank <= itemA.rank: if DEBUG_AFTER: # pragma: no cover logg.info(" %-30s before %s", itemB.conf.name(), itemA.conf.name()) itemB.rank = itemA.rank + 1 changed += 1 if not changed: if DEBUG_AFTER: # pragma: no cover logg.info("done in check %s of %s", check, len(sortlist)) break # because Requires is almost always the same as the After clauses # we are mostly done in round 1 as the list is in required order for conf in conflist: if DEBUG_AFTER: # pragma: no cover logg.debug(".. %s", conf.name()) for item in sortlist: if DEBUG_AFTER: # pragma: no cover logg.info("(%s) %s", item.rank, item.conf.name()) sortedlist = sorted(sortlist, key = lambda item: -item.rank) for item in sortedlist: if DEBUG_AFTER: # pragma: no cover logg.info("[%s] %s", item.rank, item.conf.name()) return [ item.conf for item in sortedlist ] class Systemctl: def __init__(self): # from command line options or the defaults self._extra_vars = _extra_vars self._force = _force self._full = _full self._init = _init self._no_ask_password = _no_ask_password self._no_legend = _no_legend self._now = _now self._preset_mode = _preset_mode self._quiet = _quiet self._root = _root self._show_all = _show_all self._unit_property = _unit_property self._unit_state = _unit_state self._unit_type = _unit_type # some common constants that may be changed self._systemd_version = SystemCompatibilityVersion self._pid_file_folder = _pid_file_folder self._journal_log_folder = _journal_log_folder # and the actual internal runtime state self._loaded_file_sysv = {} # /etc/init.d/name => config data self._loaded_file_sysd = {} # /etc/systemd/system/name.service => config data self._file_for_unit_sysv = None # name.service => /etc/init.d/name self._file_for_unit_sysd = None # name.service => /etc/systemd/system/name.service self._preset_file_list = None # /etc/systemd/system-preset/* => file content self._default_target = _default_target self._sysinit_target = None self.exit_when_no_more_procs = EXIT_WHEN_NO_MORE_PROCS or False self.exit_when_no_more_services = EXIT_WHEN_NO_MORE_SERVICES or False self._user_mode = _user_mode self._user_getlogin = os_getlogin() self._log_file = {} # init-loop self._log_hold = {} # init-loop def user(self): return self._user_getlogin def user_mode(self): return self._user_mode def user_folder(self): for folder in self.user_folders(): if folder: return folder raise Exception("did not find any systemd/user folder") def system_folder(self): for folder in self.system_folders(): if folder: return folder raise Exception("did not find any systemd/system folder") def init_folders(self): if _init_folder1: yield _init_folder1 if _init_folder2: yield _init_folder2 if _init_folder9: yield _init_folder9 def preset_folders(self): if _preset_folder1: yield _preset_folder1 if _preset_folder2: yield _preset_folder2 if _preset_folder3: yield _preset_folder3 if _preset_folder4: yield _preset_folder4 if _preset_folder9: yield _preset_folder9 def user_folders(self): if _user_folder1: yield os.path.expanduser(_user_folder1) if _user_folder2: yield os.path.expanduser(_user_folder2) if _user_folder3: yield os.path.expanduser(_user_folder3) if _user_folder4: yield os.path.expanduser(_user_folder4) if _user_folder9: yield os.path.expanduser(_user_folder9) def system_folders(self): if _system_folder1: yield _system_folder1 if _system_folder2: yield _system_folder2 if _system_folder3: yield _system_folder3 if _system_folder4: yield _system_folder4 if _system_folder9: yield _system_folder9 def sysd_folders(self): """ if --user then these folders are preferred """ if self.user_mode(): for folder in self.user_folders(): yield folder if True: for folder in self.system_folders(): yield folder def scan_unit_sysd_files(self, module = None): # -> [ unit-names,... ] """ reads all unit files, returns the first filename for the unit given """ if self._file_for_unit_sysd is None: self._file_for_unit_sysd = {} for folder in self.sysd_folders(): if not folder: continue folder = os_path(self._root, folder) if not os.path.isdir(folder): continue for name in os.listdir(folder): path = os.path.join(folder, name) if os.path.isdir(path): continue service_name = name if service_name not in self._file_for_unit_sysd: self._file_for_unit_sysd[service_name] = path logg.debug("found %s sysd files", len(self._file_for_unit_sysd)) return list(self._file_for_unit_sysd.keys()) def scan_unit_sysv_files(self, module = None): # -> [ unit-names,... ] """ reads all init.d files, returns the first filename when unit is a '.service' """ if self._file_for_unit_sysv is None: self._file_for_unit_sysv = {} for folder in self.init_folders(): if not folder: continue folder = os_path(self._root, folder) if not os.path.isdir(folder): continue for name in os.listdir(folder): path = os.path.join(folder, name) if os.path.isdir(path): continue service_name = name + ".service" # simulate systemd if service_name not in self._file_for_unit_sysv: self._file_for_unit_sysv[service_name] = path logg.debug("found %s sysv files", len(self._file_for_unit_sysv)) return list(self._file_for_unit_sysv.keys()) def unit_sysd_file(self, module = None): # -> filename? """ file path for the given module (systemd) """ self.scan_unit_sysd_files() if module and module in self._file_for_unit_sysd: return self._file_for_unit_sysd[module] if module and unit_of(module) in self._file_for_unit_sysd: return self._file_for_unit_sysd[unit_of(module)] return None def unit_sysv_file(self, module = None): # -> filename? """ file path for the given module (sysv) """ self.scan_unit_sysv_files() if module and module in self._file_for_unit_sysv: return self._file_for_unit_sysv[module] if module and unit_of(module) in self._file_for_unit_sysv: return self._file_for_unit_sysv[unit_of(module)] return None def unit_file(self, module = None): # -> filename? """ file path for the given module (sysv or systemd) """ path = self.unit_sysd_file(module) if path is not None: return path path = self.unit_sysv_file(module) if path is not None: return path return None def is_sysv_file(self, filename): """ for routines that have a special treatment for init.d services """ self.unit_file() # scan all if not filename: return None if filename in self._file_for_unit_sysd.values(): return False if filename in self._file_for_unit_sysv.values(): return True return None # not True def is_user_conf(self, conf): if not conf: return False # no such conf >> ignored filename = conf.filename() if filename and "/user/" in filename: return True return False def not_user_conf(self, conf): """ conf can not be started as user service (when --user)""" if not conf: return True # no such conf >> ignored if not self.user_mode(): logg.debug("%s no --user mode >> accept", conf.filename()) return False if self.is_user_conf(conf): logg.debug("%s is /user/ conf >> accept", conf.filename()) return False # to allow for 'docker run -u user' with system services user = self.expand_special(conf.get("Service", "User", ""), conf) if user and user == self.user(): logg.debug("%s with User=%s >> accept", conf.filename(), user) return False return True def find_drop_in_files(self, unit): """ search for some.service.d/extra.conf files """ result = {} basename_d = unit + ".d" for folder in self.sysd_folders(): if not folder: continue folder = os_path(self._root, folder) override_d = os_path(folder, basename_d) if not os.path.isdir(override_d): continue for name in os.listdir(override_d): path = os.path.join(override_d, name) if os.path.isdir(path): continue if not path.endswith(".conf"): continue if name not in result: result[name] = path return result def load_sysd_template_conf(self, module): # -> conf? """ read the unit template with a UnitConfParser (systemd) """ if "@" in module: unit = parse_unit(module) service = "%s@.service" % unit.prefix return self.load_sysd_unit_conf(service) return None def load_sysd_unit_conf(self, module): # -> conf? """ read the unit file with a UnitConfParser (systemd) """ path = self.unit_sysd_file(module) if not path: return None if path in self._loaded_file_sysd: return self._loaded_file_sysd[path] masked = None if os.path.islink(path) and os.readlink(path).startswith("/dev"): masked = os.readlink(path) drop_in_files = {} data = UnitConfParser() if not masked: data.read_sysd(path) drop_in_files = self.find_drop_in_files(os.path.basename(path)) # load in alphabetic order, irrespective of location for name in sorted(drop_in_files): path = drop_in_files[name] data.read_sysd(path) conf = SystemctlConf(data, module) conf.masked = masked conf.drop_in_files = drop_in_files conf._root = self._root self._loaded_file_sysd[path] = conf return conf def load_sysv_unit_conf(self, module): # -> conf? """ read the unit file with a UnitConfParser (sysv) """ path = self.unit_sysv_file(module) if not path: return None if path in self._loaded_file_sysv: return self._loaded_file_sysv[path] data = UnitConfParser() data.read_sysv(path) conf = SystemctlConf(data, module) conf._root = self._root self._loaded_file_sysv[path] = conf return conf def load_unit_conf(self, module): # -> conf | None(not-found) """ read the unit file with a UnitConfParser (sysv or systemd) """ try: conf = self.load_sysd_unit_conf(module) if conf is not None: return conf conf = self.load_sysd_template_conf(module) if conf is not None: return conf conf = self.load_sysv_unit_conf(module) if conf is not None: return conf except Exception as e: logg.warning("%s not loaded: %s", module, e) return None def default_unit_conf(self, module, description = None): # -> conf """ a unit conf that can be printed to the user where attributes are empty and loaded() is False """ data = UnitConfParser() data.set("Unit","Id", module) data.set("Unit", "Names", module) data.set("Unit", "Description", description or ("NOT-FOUND "+module)) # assert(not data.loaded()) conf = SystemctlConf(data, module) conf._root = self._root return conf def get_unit_conf(self, module): # -> conf (conf | default-conf) """ accept that a unit does not exist and return a unit conf that says 'not-loaded' """ conf = self.load_unit_conf(module) if conf is not None: return conf return self.default_unit_conf(module) def match_sysd_templates(self, modules = None, suffix=".service"): # -> generate[ unit ] """ make a file glob on all known template units (systemd areas). It returns no modules (!!) if no modules pattern were given. The module string should contain an instance name already. """ modules = to_list(modules) if not modules: return self.scan_unit_sysd_files() for item in sorted(self._file_for_unit_sysd.keys()): if "@" not in item: continue service_unit = parse_unit(item) for module in modules: if "@" not in module: continue module_unit = parse_unit(module) if service_unit.prefix == module_unit.prefix: yield "%s@%s.%s" % (service_unit.prefix, module_unit.instance, service_unit.suffix) def match_sysd_units(self, modules = None, suffix=".service"): # -> generate[ unit ] """ make a file glob on all known units (systemd areas). It returns all modules if no modules pattern were given. Also a single string as one module pattern may be given. """ modules = to_list(modules) self.scan_unit_sysd_files() for item in sorted(self._file_for_unit_sysd.keys()): if not modules: yield item elif [ module for module in modules if fnmatch.fnmatchcase(item, module) ]: yield item elif [ module for module in modules if module+suffix == item ]: yield item def match_sysv_units(self, modules = None, suffix=".service"): # -> generate[ unit ] """ make a file glob on all known units (sysv areas). It returns all modules if no modules pattern were given. Also a single string as one module pattern may be given. """ modules = to_list(modules) self.scan_unit_sysv_files() for item in sorted(self._file_for_unit_sysv.keys()): if not modules: yield item elif [ module for module in modules if fnmatch.fnmatchcase(item, module) ]: yield item elif [ module for module in modules if module+suffix == item ]: yield item def match_units(self, modules = None, suffix=".service"): # -> [ units,.. ] """ Helper for about any command with multiple units which can actually be glob patterns on their respective unit name. It returns all modules if no modules pattern were given. Also a single string as one module pattern may be given. """ found = [] for unit in self.match_sysd_units(modules, suffix): if unit not in found: found.append(unit) for unit in self.match_sysd_templates(modules, suffix): if unit not in found: found.append(unit) for unit in self.match_sysv_units(modules, suffix): if unit not in found: found.append(unit) return found def list_service_unit_basics(self): """ show all the basic loading state of services """ filename = self.unit_file() # scan all result = [] for name, value in self._file_for_unit_sysd.items(): result += [ (name, "SysD", value) ] for name, value in self._file_for_unit_sysv.items(): result += [ (name, "SysV", value) ] return result def list_service_units(self, *modules): # -> [ (unit,loaded+active+substate,description) ] """ show all the service units """ result = {} active = {} substate = {} description = {} for unit in self.match_units(modules): result[unit] = "not-found" active[unit] = "inactive" substate[unit] = "dead" description[unit] = "" try: conf = self.get_unit_conf(unit) result[unit] = "loaded" description[unit] = self.get_description_from(conf) active[unit] = self.get_active_from(conf) substate[unit] = self.get_substate_from(conf) except Exception as e: logg.warning("list-units: %s", e) if self._unit_state: if self._unit_state not in [ result[unit], active[unit], substate[unit] ]: del result[unit] return [ (unit, result[unit] + " " + active[unit] + " " + substate[unit], description[unit]) for unit in sorted(result) ] def show_list_units(self, *modules): # -> [ (unit,loaded,description) ] """ [PATTERN]... -- List loaded units. If one or more PATTERNs are specified, only units matching one of them are shown. NOTE: This is the default command.""" hint = "To show all installed unit files use 'systemctl list-unit-files'." result = self.list_service_units(*modules) if self._no_legend: return result found = "%s loaded units listed." % len(result) return result + [ "", found, hint ] def list_service_unit_files(self, *modules): # -> [ (unit,enabled) ] """ show all the service units and the enabled status""" logg.debug("list service unit files for %s", modules) result = {} enabled = {} for unit in self.match_units(modules): result[unit] = None enabled[unit] = "" try: conf = self.get_unit_conf(unit) if self.not_user_conf(conf): result[unit] = None continue result[unit] = conf enabled[unit] = self.enabled_from(conf) except Exception as e: logg.warning("list-units: %s", e) return [ (unit, enabled[unit]) for unit in sorted(result) if result[unit] ] def each_target_file(self): folders = self.system_folders() if self.user_mode(): folders = self.user_folders() for folder in folders: if not os.path.isdir(folder): continue for filename in os.listdir(folder): if filename.endswith(".target"): yield (filename, os.path.join(folder, filename)) def list_target_unit_files(self, *modules): # -> [ (unit,enabled) ] """ show all the target units and the enabled status""" enabled = {} targets = {} for target, filepath in self.each_target_file(): logg.info("target %s", filepath) targets[target] = filepath enabled[target] = "static" for unit in _all_common_targets: targets[unit] = None enabled[unit] = "static" if unit in _all_common_enabled: enabled[unit] = "enabled" if unit in _all_common_disabled: enabled[unit] = "disabled" return [ (unit, enabled[unit]) for unit in sorted(targets) ] def show_list_unit_files(self, *modules): # -> [ (unit,enabled) ] """[PATTERN]... -- List installed unit files List installed unit files and their enablement state (as reported by is-enabled). If one or more PATTERNs are specified, only units whose filename (just the last component of the path) matches one of them are shown. This command reacts to limitations of --type being --type=service or --type=target (and --now for some basics).""" if self._now: result = self.list_service_unit_basics() elif self._unit_type == "target": result = self.list_target_unit_files() elif self._unit_type == "service": result = self.list_service_unit_files() elif self._unit_type: logg.warning("unsupported unit --type=%s", self._unit_type) result = [] else: result = self.list_target_unit_files() result += self.list_service_unit_files(*modules) if self._no_legend: return result found = "%s unit files listed." % len(result) return [ ("UNIT FILE", "STATE") ] + result + [ "", found ] ## ## def get_description(self, unit, default = None): return self.get_description_from(self.load_unit_conf(unit)) def get_description_from(self, conf, default = None): # -> text """ Unit.Description could be empty sometimes """ if not conf: return default or "" description = conf.get("Unit", "Description", default or "") return self.expand_special(description, conf) def read_pid_file(self, pid_file, default = None): pid = default if not pid_file: return default if not os.path.isfile(pid_file): return default if self.truncate_old(pid_file): return default try: # some pid-files from applications contain multiple lines for line in open(pid_file): if line.strip(): pid = to_int(line.strip()) break except Exception as e: logg.warning("bad read of pid file '%s': %s", pid_file, e) return pid def wait_pid_file(self, pid_file, timeout = None): # -> pid? """ wait some seconds for the pid file to appear and return the pid """ timeout = int(timeout or (DefaultTimeoutStartSec/2)) timeout = max(timeout, (MinimumTimeoutStartSec)) dirpath = os.path.dirname(os.path.abspath(pid_file)) for x in xrange(timeout): if not os.path.isdir(dirpath): time.sleep(1) # until TimeoutStartSec/2 continue pid = self.read_pid_file(pid_file) if not pid: time.sleep(1) # until TimeoutStartSec/2 continue if not pid_exists(pid): time.sleep(1) # until TimeoutStartSec/2 continue return pid return None def test_pid_file(self, unit): # -> text """ support for the testsuite.py """ conf = self.get_unit_conf(unit) return self.pid_file_from(conf) or self.status_file_from(conf) def pid_file_from(self, conf, default = ""): """ get the specified pid file path (not a computed default) """ pid_file = conf.get("Service", "PIDFile", default) return self.expand_special(pid_file, conf) def read_mainpid_from(self, conf, default): """ MAINPID is either the PIDFile content written from the application or it is the value in the status file written by this systemctl.py code """ pid_file = self.pid_file_from(conf) if pid_file: return self.read_pid_file(pid_file, default) status = self.read_status_from(conf) return status.get("MainPID", default) def clean_pid_file_from(self, conf): pid_file = self.pid_file_from(conf) if pid_file and os.path.isfile(pid_file): try: os.remove(pid_file) except OSError as e: logg.warning("while rm %s: %s", pid_file, e) self.write_status_from(conf, MainPID=None) def get_status_file(self, unit): # for testing conf = self.get_unit_conf(unit) return self.status_file_from(conf) def status_file_from(self, conf, default = None): if default is None: default = self.default_status_file(conf) if conf is None: return default status_file = conf.get("Service", "StatusFile", default) # this not a real setting, but do the expand_special anyway return self.expand_special(status_file, conf) def default_status_file(self, conf): # -> text """ default file pattern where to store a status mark """ folder = conf.os_path_var(self._pid_file_folder) name = "%s.status" % conf.name() return os.path.join(folder, name) def clean_status_from(self, conf): status_file = self.status_file_from(conf) if os.path.exists(status_file): os.remove(status_file) conf.status = {} def write_status_from(self, conf, **status): # -> bool(written) """ if a status_file is known then path is created and the give status is written as the only content. """ status_file = self.status_file_from(conf) if not status_file: logg.debug("status %s but no status_file", conf.name()) return False dirpath = os.path.dirname(os.path.abspath(status_file)) if not os.path.isdir(dirpath): os.makedirs(dirpath) if conf.status is None: conf.status = self.read_status_from(conf) if True: for key in sorted(status.keys()): value = status[key] if key.upper() == "AS": key = "ActiveState" if key.upper() == "EXIT": key = "ExecMainCode" if value is None: try: del conf.status[key] except KeyError: pass else: conf.status[key] = value try: with open(status_file, "w") as f: for key in sorted(conf.status): value = conf.status[key] if key == "MainPID" and str(value) == "0": logg.warning("ignore writing MainPID=0") continue content = "{}={}\n".format(key, str(value)) logg.debug("writing to %s\n\t%s", status_file, content.strip()) f.write(content) except IOError as e: logg.error("writing STATUS %s: %s\n\t to status file %s", status, e, status_file) return True def read_status_from(self, conf, defaults = None): status_file = self.status_file_from(conf) status = {} if hasattr(defaults, "keys"): for key in defaults.keys(): status[key] = defaults[key] elif isinstance(defaults, string_types): status["ActiveState"] = defaults if not status_file: logg.debug("no status file. returning %s", status) return status if not os.path.isfile(status_file): logg.debug("no status file: %s\n returning %s", status_file, status) return status if self.truncate_old(status_file): logg.debug("old status file: %s\n returning %s", status_file, status) return status try: logg.debug("reading %s", status_file) for line in open(status_file): if line.strip(): m = re.match(r"(\w+)[:=](.*)", line) if m: key, value = m.group(1), m.group(2) if key.strip(): status[key.strip()] = value.strip() elif line in [ "active", "inactive", "failed"]: status["ActiveState"] = line else: logg.warning("ignored %s", line.strip()) except: logg.warning("bad read of status file '%s'", status_file) return status def get_status_from(self, conf, name, default = None): if conf.status is None: conf.status = self.read_status_from(conf) return conf.status.get(name, default) def set_status_from(self, conf, name, value): if conf.status is None: conf.status = self.read_status_from(conf) if value is None: try: del conf.status[name] except KeyError: pass else: conf.status[name] = value # def wait_boot(self, hint = None): booted = self.get_boottime() while True: now = time.time() if booted + EpsilonTime <= now: break time.sleep(EpsilonTime) logg.info(" %s ................. boot sleep %ss", hint or "", EpsilonTime) def get_boottime(self): if "oldest" in COVERAGE: return self.get_boottime_oldest() for pid in xrange(10): proc = "/proc/%s/status" % pid try: if os.path.exists(proc): return os.path.getmtime(proc) except Exception as e: # pragma: nocover logg.warning("could not access %s: %s", proc, e) return self.get_boottime_oldest() def get_boottime_oldest(self): # otherwise get the oldest entry in /proc booted = time.time() for name in os.listdir("/proc"): proc = "/proc/%s/status" % name try: if os.path.exists(proc): ctime = os.path.getmtime(proc) if ctime < booted: booted = ctime except Exception as e: # pragma: nocover logg.warning("could not access %s: %s", proc, e) return booted def get_filetime(self, filename): return os.path.getmtime(filename) def truncate_old(self, filename): filetime = self.get_filetime(filename) boottime = self.get_boottime() if isinstance(filetime, float): filetime -= EpsilonTime if filetime >= boottime : logg.debug(" file time: %s", datetime.datetime.fromtimestamp(filetime)) logg.debug(" boot time: %s", datetime.datetime.fromtimestamp(boottime)) return False # OK logg.info("truncate old %s", filename) logg.info(" file time: %s", datetime.datetime.fromtimestamp(filetime)) logg.info(" boot time: %s", datetime.datetime.fromtimestamp(boottime)) try: shutil_truncate(filename) except Exception as e: logg.warning("while truncating: %s", e) return True # truncated def getsize(self, filename): if not filename: return 0 if not os.path.isfile(filename): return 0 if self.truncate_old(filename): return 0 try: return os.path.getsize(filename) except Exception as e: logg.warning("while reading file size: %s\n of %s", e, filename) return 0 # def read_env_file(self, env_file): # -> generate[ (name,value) ] """ EnvironmentFile= is being scanned """ if env_file.startswith("-"): env_file = env_file[1:] if not os.path.isfile(os_path(self._root, env_file)): return try: for real_line in open(os_path(self._root, env_file)): line = real_line.strip() if not line or line.startswith("#"): continue m = re.match(r"(?:export +)?([\w_]+)[=]'([^']*)'", line) if m: yield m.group(1), m.group(2) continue m = re.match(r'(?:export +)?([\w_]+)[=]"([^"]*)"', line) if m: yield m.group(1), m.group(2) continue m = re.match(r'(?:export +)?([\w_]+)[=](.*)', line) if m: yield m.group(1), m.group(2) continue except Exception as e: logg.info("while reading %s: %s", env_file, e) def read_env_part(self, env_part): # -> generate[ (name, value) ] """ Environment== is being scanned """ ## systemd Environment= spec says it is a space-seperated list of ## assignments. In order to use a space or an equals sign in a value ## one should enclose the whole assignment with double quotes: ## Environment="VAR1=word word" VAR2=word3 "VAR3=$word 5 6" ## and the $word is not expanded by other environment variables. try: for real_line in env_part.split("\n"): line = real_line.strip() for found in re.finditer(r'\s*("[\w_]+=[^"]*"|[\w_]+=\S*)', line): part = found.group(1) if part.startswith('"'): part = part[1:-1] name, value = part.split("=", 1) yield name, value except Exception as e: logg.info("while reading %s: %s", env_part, e) def show_environment(self, unit): """ [UNIT]. -- show environment parts """ conf = self.load_unit_conf(unit) if conf is None: logg.error("Unit %s could not be found.", unit) return False if _unit_property: return conf.getlist("Service", _unit_property) return self.get_env(conf) def extra_vars(self): return self._extra_vars # from command line def get_env(self, conf): env = os.environ.copy() for env_part in conf.getlist("Service", "Environment", []): for name, value in self.read_env_part(self.expand_special(env_part, conf)): env[name] = value # a '$word' is not special here for env_file in conf.getlist("Service", "EnvironmentFile", []): for name, value in self.read_env_file(self.expand_special(env_file, conf)): env[name] = self.expand_env(value, env) logg.debug("extra-vars %s", self.extra_vars()) for extra in self.extra_vars(): if extra.startswith("@"): for name, value in self.read_env_file(extra[1:]): logg.info("override %s=%s", name, value) env[name] = self.expand_env(value, env) else: for name, value in self.read_env_part(extra): logg.info("override %s=%s", name, value) env[name] = value # a '$word' is not special here return env def expand_env(self, cmd, env): def get_env1(m): if m.group(1) in env: return env[m.group(1)] logg.debug("can not expand $%s", m.group(1)) return "" # empty string def get_env2(m): if m.group(1) in env: return env[m.group(1)] logg.debug("can not expand ${%s}", m.group(1)) return "" # empty string # maxdepth = 20 expanded = re.sub("[$](\w+)", lambda m: get_env1(m), cmd.replace("\\\n","")) for depth in xrange(maxdepth): new_text = re.sub("[$][{](\w+)[}]", lambda m: get_env2(m), expanded) if new_text == expanded: return expanded expanded = new_text logg.error("shell variable expansion exceeded maxdepth %s", maxdepth) return expanded def expand_special(self, cmd, conf = None): """ expand %i %t and similar special vars. They are being expanded before any other expand_env takes place which handles shell-style $HOME references. """ def sh_escape(value): return "'" + value.replace("'","\\'") + "'" def get_confs(conf): confs={ "%": "%" } if not conf: return confs unit = parse_unit(conf.name()) confs["N"] = unit.name confs["n"] = sh_escape(unit.name) confs["P"] = unit.prefix confs["p"] = sh_escape(unit.prefix) confs["I"] = unit.instance confs["i"] = sh_escape(unit.instance) confs["J"] = unit.component confs["j"] = sh_escape(unit.component) confs["f"] = sh_escape(conf.filename()) VARTMP = "/var/tmp" TMP = "/tmp" RUN = "/run" DAT = "/var/lib" LOG = "/var/log" CACHE = "/var/cache" CONFIG = "/etc" HOME = "/root" USER = "root" UID = 0 SHELL = "/bin/sh" if self.is_user_conf(conf): USER = os_getlogin() HOME = get_home() RUN = os.environ.get("XDG_RUNTIME_DIR", get_runtime_dir()) CONFIG = os.environ.get("XDG_CONFIG_HOME", HOME + "/.config") CACHE = os.environ.get("XDG_CACHE_HOME", HOME + "/.cache") SHARE = os.environ.get("XDG_DATA_HOME", HOME + "/.local/share") DAT = CONFIG LOG = os.path.join(CONFIG, "log") SHELL = os.environ.get("SHELL", SHELL) VARTMP = os.environ.get("TMPDIR", os.environ.get("TEMP", os.environ.get("TMP", VARTMP))) TMP = os.environ.get("TMPDIR", os.environ.get("TEMP", os.environ.get("TMP", TMP))) confs["V"] = os_path(self._root, VARTMP) confs["T"] = os_path(self._root, TMP) confs["t"] = os_path(self._root, RUN) confs["S"] = os_path(self._root, DAT) confs["s"] = SHELL confs["h"] = HOME confs["u"] = USER confs["C"] = os_path(self._root, CACHE) confs["E"] = os_path(self._root, CONFIG) return confs def get_conf1(m): confs = get_confs(conf) if m.group(1) in confs: return confs[m.group(1)] logg.warning("can not expand %%%s", m.group(1)) return "''" # empty escaped string return re.sub("[%](.)", lambda m: get_conf1(m), cmd) def exec_cmd(self, cmd, env, conf = None): """ expand ExecCmd statements including %i and $MAINPID """ cmd1 = cmd.replace("\\\n","") # according to documentation the %n / %% need to be expanded where in # most cases they are shell-escaped values. So we do it before shlex. cmd2 = self.expand_special(cmd1, conf) # according to documentation, when bar="one two" then the expansion # of '$bar' is ["one","two"] and '${bar}' becomes ["one two"]. We # tackle that by expand $bar before shlex, and the rest thereafter. def get_env1(m): if m.group(1) in env: return env[m.group(1)] logg.debug("can not expand $%s", m.group(1)) return "" # empty string def get_env2(m): if m.group(1) in env: return env[m.group(1)] logg.debug("can not expand ${%s}", m.group(1)) return "" # empty string cmd3 = re.sub("[$](\w+)", lambda m: get_env1(m), cmd2) newcmd = [] for part in shlex.split(cmd3): newcmd += [ re.sub("[$][{](\w+)[}]", lambda m: get_env2(m), part) ] return newcmd def path_journal_log(self, conf): # never None """ /var/log/zzz.service.log or /var/log/default.unit.log """ filename = os.path.basename(conf.filename() or "") unitname = (conf.name() or "default")+".unit" name = filename or unitname log_folder = conf.os_path_var(self._journal_log_folder) log_file = name.replace(os.path.sep,".") + ".log" if log_file.startswith("."): log_file = "dot."+log_file return os.path.join(log_folder, log_file) def open_journal_log(self, conf): log_file = self.path_journal_log(conf) log_folder = os.path.dirname(log_file) if not os.path.isdir(log_folder): os.makedirs(log_folder) return open(os.path.join(log_file), "a") def chdir_workingdir(self, conf): """ if specified then change the working directory """ # the original systemd will start in '/' even if User= is given if self._root: os.chdir(self._root) workingdir = conf.get("Service", "WorkingDirectory", "") if workingdir: ignore = False if workingdir.startswith("-"): workingdir = workingdir[1:] ignore = True into = os_path(self._root, self.expand_special(workingdir, conf)) try: logg.debug("chdir workingdir '%s'", into) os.chdir(into) return False except Exception as e: if not ignore: logg.error("chdir workingdir '%s': %s", into, e) return into else: logg.debug("chdir workingdir '%s': %s", into, e) return None return None def notify_socket_from(self, conf, socketfile = None): """ creates a notify-socket for the (non-privileged) user """ NotifySocket = collections.namedtuple("NotifySocket", ["socket", "socketfile" ]) notify_socket_folder = conf.os_path_var(_notify_socket_folder) notify_name = "notify." + str(conf.name() or "systemctl") notify_socket = os.path.join(notify_socket_folder, notify_name) socketfile = socketfile or notify_socket if len(socketfile) > 100: logg.debug("https://unix.stackexchange.com/questions/367008/%s", "why-is-socket-path-length-limited-to-a-hundred-chars") logg.debug("old notify socketfile (%s) = %s", len(socketfile), socketfile) notify_socket_folder = re.sub("^(/var)?", get_runtime_dir(), _notify_socket_folder) notify_name = notify_name[0:min(100-len(notify_socket_folder),len(notify_name))] socketfile = os.path.join(notify_socket_folder, notify_name) # occurs during testsuite.py for ~user/test.tmp/root path logg.info("new notify socketfile (%s) = %s", len(socketfile), socketfile) try: if not os.path.isdir(os.path.dirname(socketfile)): os.makedirs(os.path.dirname(socketfile)) if os.path.exists(socketfile): os.unlink(socketfile) except Exception as e: logg.warning("error %s: %s", socketfile, e) sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) sock.bind(socketfile) os.chmod(socketfile, 0o777) # the service my run under some User=setting return NotifySocket(sock, socketfile) def read_notify_socket(self, notify, timeout): notify.socket.settimeout(timeout or DefaultMaximumTimeout) result = "" try: result, client_address = notify.socket.recvfrom(4096) if result: result = result.decode("utf-8") result_txt = result.replace("\n","|") result_len = len(result) logg.debug("read_notify_socket(%s):%s", result_len, result_txt) except socket.timeout as e: if timeout > 2: logg.debug("socket.timeout %s", e) return result def wait_notify_socket(self, notify, timeout, pid = None): if not os.path.exists(notify.socketfile): logg.info("no $NOTIFY_SOCKET exists") return {} # logg.info("wait $NOTIFY_SOCKET, timeout %s", timeout) results = {} seenREADY = None for attempt in xrange(timeout+1): if pid and not self.is_active_pid(pid): logg.info("dead PID %s", pid) return results if not attempt: # first one time.sleep(1) # until TimeoutStartSec continue result = self.read_notify_socket(notify, 1) # sleep max 1 second if not result: # timeout time.sleep(1) # until TimeoutStartSec continue for name, value in self.read_env_part(result): results[name] = value if name == "READY": seenREADY = value if name in ["STATUS", "ACTIVESTATE"]: logg.debug("%s: %s", name, value) # TODO: update STATUS -> SubState if seenREADY: break if not seenREADY: logg.info(".... timeout while waiting for 'READY=1' status on $NOTIFY_SOCKET") logg.debug("notify = %s", results) try: notify.socket.close() except Exception as e: logg.debug("socket.close %s", e) return results def start_modules(self, *modules): """ [UNIT]... -- start these units /// SPECIAL: with --now or --init it will run the init-loop and stop the units afterwards """ found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] init = self._now or self._init return self.start_units(units, init) and found_all def start_units(self, units, init = None): """ fails if any unit does not start /// SPECIAL: may run the init-loop and stop the named units afterwards """ self.wait_system() done = True started_units = [] for unit in self.sortedAfter(units): started_units.append(unit) if not self.start_unit(unit): done = False if init: logg.info("init-loop start") sig = self.init_loop_until_stop(started_units) logg.info("init-loop %s", sig) for unit in reversed(started_units): self.stop_unit(unit) return done def start_unit(self, unit): conf = self.load_unit_conf(unit) if conf is None: logg.debug("unit could not be loaded (%s)", unit) logg.error("Unit %s could not be found.", unit) return False if self.not_user_conf(conf): logg.error("Unit %s not for --user mode", unit) return False return self.start_unit_from(conf) def get_TimeoutStartSec(self, conf): timeout = conf.get("Service", "TimeoutSec", DefaultTimeoutStartSec) timeout = conf.get("Service", "TimeoutStartSec", timeout) return time_to_seconds(timeout, DefaultMaximumTimeout) def start_unit_from(self, conf): if not conf: return False if self.syntax_check(conf) > 100: return False with waitlock(conf): logg.debug(" start unit %s => %s", conf.name(), conf.filename()) return self.do_start_unit_from(conf) def do_start_unit_from(self, conf): timeout = self.get_TimeoutStartSec(conf) doRemainAfterExit = conf.getbool("Service", "RemainAfterExit", "no") runs = conf.get("Service", "Type", "simple").lower() env = self.get_env(conf) self.exec_check_service(conf, env, "Exec") # all... # for StopPost on failure: returncode = 0 service_result = "success" if True: if runs in [ "simple", "forking", "notify" ]: env["MAINPID"] = str(self.read_mainpid_from(conf, "")) for cmd in conf.getlist("Service", "ExecStartPre", []): check, cmd = checkstatus(cmd) newcmd = self.exec_cmd(cmd, env, conf) logg.info(" pre-start %s", shell_cmd(newcmd)) forkpid = os.fork() if not forkpid: self.execve_from(conf, newcmd, env) # pragma: nocover run = subprocess_waitpid(forkpid) logg.debug(" pre-start done (%s) <-%s>", run.returncode or "OK", run.signal or "") if run.returncode and check: logg.error("the ExecStartPre control process exited with error code") active = "failed" self.write_status_from(conf, AS=active ) return False if runs in [ "sysv" ]: status_file = self.status_file_from(conf) if True: exe = conf.filename() cmd = "'%s' start" % exe env["SYSTEMCTL_SKIP_REDIRECT"] = "yes" newcmd = self.exec_cmd(cmd, env, conf) logg.info("%s start %s", runs, shell_cmd(newcmd)) forkpid = os.fork() if not forkpid: # pragma: no cover os.setsid() # detach child process from parent self.execve_from(conf, newcmd, env) run = subprocess_waitpid(forkpid) self.set_status_from(conf, "ExecMainCode", run.returncode) logg.info("%s start done (%s) <-%s>", runs, run.returncode or "OK", run.signal or "") active = run.returncode and "failed" or "active" self.write_status_from(conf, AS=active ) return True elif runs in [ "oneshot" ]: status_file = self.status_file_from(conf) if self.get_status_from(conf, "ActiveState", "unknown") == "active": logg.warning("the service was already up once") return True for cmd in conf.getlist("Service", "ExecStart", []): check, cmd = checkstatus(cmd) newcmd = self.exec_cmd(cmd, env, conf) logg.info("%s start %s", runs, shell_cmd(newcmd)) forkpid = os.fork() if not forkpid: # pragma: no cover os.setsid() # detach child process from parent self.execve_from(conf, newcmd, env) run = subprocess_waitpid(forkpid) if run.returncode and check: returncode = run.returncode service_result = "failed" logg.error("%s start %s (%s) <-%s>", runs, service_result, run.returncode or "OK", run.signal or "") break logg.info("%s start done (%s) <-%s>", runs, run.returncode or "OK", run.signal or "") if True: self.set_status_from(conf, "ExecMainCode", returncode) active = returncode and "failed" or "active" self.write_status_from(conf, AS=active) elif runs in [ "simple" ]: status_file = self.status_file_from(conf) pid = self.read_mainpid_from(conf, "") if self.is_active_pid(pid): logg.warning("the service is already running on PID %s", pid) return True if doRemainAfterExit: logg.debug("%s RemainAfterExit -> AS=active", runs) self.write_status_from(conf, AS="active") cmdlist = conf.getlist("Service", "ExecStart", []) for idx, cmd in enumerate(cmdlist): logg.debug("ExecStart[%s]: %s", idx, cmd) for cmd in cmdlist: pid = self.read_mainpid_from(conf, "") env["MAINPID"] = str(pid) newcmd = self.exec_cmd(cmd, env, conf) logg.info("%s start %s", runs, shell_cmd(newcmd)) forkpid = os.fork() if not forkpid: # pragma: no cover os.setsid() # detach child process from parent self.execve_from(conf, newcmd, env) self.write_status_from(conf, MainPID=forkpid) logg.info("%s started PID %s", runs, forkpid) env["MAINPID"] = str(forkpid) time.sleep(MinimumYield) run = subprocess_testpid(forkpid) if run.returncode is not None: logg.info("%s stopped PID %s (%s) <-%s>", runs, run.pid, run.returncode or "OK", run.signal or "") if doRemainAfterExit: self.set_status_from(conf, "ExecMainCode", run.returncode) active = run.returncode and "failed" or "active" self.write_status_from(conf, AS=active) if run.returncode: service_result = "failed" break elif runs in [ "notify" ]: # "notify" is the same as "simple" but we create a $NOTIFY_SOCKET # and wait for startup completion by checking the socket messages pid = self.read_mainpid_from(conf, "") if self.is_active_pid(pid): logg.error("the service is already running on PID %s", pid) return False notify = self.notify_socket_from(conf) if notify: env["NOTIFY_SOCKET"] = notify.socketfile logg.debug("use NOTIFY_SOCKET=%s", notify.socketfile) if doRemainAfterExit: logg.debug("%s RemainAfterExit -> AS=active", runs) self.write_status_from(conf, AS="active") cmdlist = conf.getlist("Service", "ExecStart", []) for idx, cmd in enumerate(cmdlist): logg.debug("ExecStart[%s]: %s", idx, cmd) mainpid = None for cmd in cmdlist: mainpid = self.read_mainpid_from(conf, "") env["MAINPID"] = str(mainpid) newcmd = self.exec_cmd(cmd, env, conf) logg.info("%s start %s", runs, shell_cmd(newcmd)) forkpid = os.fork() if not forkpid: # pragma: no cover os.setsid() # detach child process from parent self.execve_from(conf, newcmd, env) # via NOTIFY # self.write_status_from(conf, MainPID=forkpid) logg.info("%s started PID %s", runs, forkpid) mainpid = forkpid self.write_status_from(conf, MainPID=mainpid) env["MAINPID"] = str(mainpid) time.sleep(MinimumYield) run = subprocess_testpid(forkpid) if run.returncode is not None: logg.info("%s stopped PID %s (%s) <-%s>", runs, run.pid, run.returncode or "OK", run.signal or "") if doRemainAfterExit: self.set_status_from(conf, "ExecMainCode", run.returncode or 0) active = run.returncode and "failed" or "active" self.write_status_from(conf, AS=active) if run.returncode: service_result = "failed" break if service_result in [ "success" ] and mainpid: logg.debug("okay, wating on socket for %ss", timeout) results = self.wait_notify_socket(notify, timeout, mainpid) if "MAINPID" in results: new_pid = results["MAINPID"] if new_pid and to_int(new_pid) != mainpid: logg.info("NEW PID %s from sd_notify (was PID %s)", new_pid, mainpid) self.write_status_from(conf, MainPID=new_pid) mainpid = new_pid logg.info("%s start done %s", runs, mainpid) pid = self.read_mainpid_from(conf, "") if pid: env["MAINPID"] = str(pid) else: service_result = "timeout" # "could not start service" elif runs in [ "forking" ]: pid_file = self.pid_file_from(conf) for cmd in conf.getlist("Service", "ExecStart", []): check, cmd = checkstatus(cmd) newcmd = self.exec_cmd(cmd, env, conf) if not newcmd: continue logg.info("%s start %s", runs, shell_cmd(newcmd)) forkpid = os.fork() if not forkpid: # pragma: no cover os.setsid() # detach child process from parent self.execve_from(conf, newcmd, env) logg.info("%s started PID %s", runs, forkpid) run = subprocess_waitpid(forkpid) if run.returncode and check: returncode = run.returncode service_result = "failed" logg.info("%s stopped PID %s (%s) <-%s>", runs, run.pid, run.returncode or "OK", run.signal or "") if pid_file and service_result in [ "success" ]: pid = self.wait_pid_file(pid_file) # application PIDFile logg.info("%s start done PID %s [%s]", runs, pid, pid_file) if pid: env["MAINPID"] = str(pid) if not pid_file: time.sleep(MinimumTimeoutStartSec) logg.warning("No PIDFile for forking %s", conf.filename()) status_file = self.status_file_from(conf) self.set_status_from(conf, "ExecMainCode", returncode) active = returncode and "failed" or "active" self.write_status_from(conf, AS=active) else: logg.error("unsupported run type '%s'", runs) return False # POST sequence active = self.is_active_from(conf) if not active: logg.warning("%s start not active", runs) # according to the systemd documentation, a failed start-sequence # should execute the ExecStopPost sequence allowing some cleanup. env["SERVICE_RESULT"] = service_result for cmd in conf.getlist("Service", "ExecStopPost", []): check, cmd = checkstatus(cmd) newcmd = self.exec_cmd(cmd, env, conf) logg.info("post-fail %s", shell_cmd(newcmd)) forkpid = os.fork() if not forkpid: self.execve_from(conf, newcmd, env) # pragma: nocover run = subprocess_waitpid(forkpid) logg.debug("post-fail done (%s) <-%s>", run.returncode or "OK", run.signal or "") return False else: for cmd in conf.getlist("Service", "ExecStartPost", []): check, cmd = checkstatus(cmd) newcmd = self.exec_cmd(cmd, env, conf) logg.info("post-start %s", shell_cmd(newcmd)) forkpid = os.fork() if not forkpid: self.execve_from(conf, newcmd, env) # pragma: nocover run = subprocess_waitpid(forkpid) logg.debug("post-start done (%s) <-%s>", run.returncode or "OK", run.signal or "") return True def extend_exec_env(self, env): env = env.copy() # implant DefaultPath into $PATH path = env.get("PATH", DefaultPath) parts = path.split(os.pathsep) for part in DefaultPath.split(os.pathsep): if part and part not in parts: parts.append(part) env["PATH"] = str(os.pathsep).join(parts) # reset locale to system default for name in ResetLocale: if name in env: del env[name] locale = {} for var, val in self.read_env_file("/etc/locale.conf"): locale[var] = val env[var] = val if "LANG" not in locale: env["LANG"] = locale.get("LANGUAGE", locale.get("LC_CTYPE", "C")) return env def execve_from(self, conf, cmd, env): """ this code is commonly run in a child process // returns exit-code""" runs = conf.get("Service", "Type", "simple").lower() logg.debug("%s process for %s", runs, conf.filename()) inp = open("/dev/zero") out = self.open_journal_log(conf) os.dup2(inp.fileno(), sys.stdin.fileno()) os.dup2(out.fileno(), sys.stdout.fileno()) os.dup2(out.fileno(), sys.stderr.fileno()) runuser = self.expand_special(conf.get("Service", "User", ""), conf) rungroup = self.expand_special(conf.get("Service", "Group", ""), conf) envs = shutil_setuid(runuser, rungroup) badpath = self.chdir_workingdir(conf) # some dirs need setuid before if badpath: logg.error("(%s): bad workingdir: '%s'", shell_cmd(cmd), badpath) sys.exit(1) env = self.extend_exec_env(env) env.update(envs) # set $HOME to ~$USER try: if "spawn" in COVERAGE: os.spawnvpe(os.P_WAIT, cmd[0], cmd, env) sys.exit(0) else: # pragma: nocover os.execve(cmd[0], cmd, env) except Exception as e: logg.error("(%s): %s", shell_cmd(cmd), e) sys.exit(1) def test_start_unit(self, unit): """ helper function to test the code that is normally forked off """ conf = self.load_unit_conf(unit) env = self.get_env(conf) for cmd in conf.getlist("Service", "ExecStart", []): newcmd = self.exec_cmd(cmd, env, conf) return self.execve_from(conf, newcmd, env) return None def stop_modules(self, *modules): """ [UNIT]... -- stop these units """ found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] return self.stop_units(units) and found_all def stop_units(self, units): """ fails if any unit fails to stop """ self.wait_system() done = True for unit in self.sortedBefore(units): if not self.stop_unit(unit): done = False return done def stop_unit(self, unit): conf = self.load_unit_conf(unit) if conf is None: logg.error("Unit %s could not be found.", unit) return False if self.not_user_conf(conf): logg.error("Unit %s not for --user mode", unit) return False return self.stop_unit_from(conf) def get_TimeoutStopSec(self, conf): timeout = conf.get("Service", "TimeoutSec", DefaultTimeoutStartSec) timeout = conf.get("Service", "TimeoutStopSec", timeout) return time_to_seconds(timeout, DefaultMaximumTimeout) def stop_unit_from(self, conf): if not conf: return False if self.syntax_check(conf) > 100: return False with waitlock(conf): logg.info(" stop unit %s => %s", conf.name(), conf.filename()) return self.do_stop_unit_from(conf) def do_stop_unit_from(self, conf): timeout = self.get_TimeoutStopSec(conf) runs = conf.get("Service", "Type", "simple").lower() env = self.get_env(conf) self.exec_check_service(conf, env, "ExecStop") returncode = 0 service_result = "success" if runs in [ "sysv" ]: status_file = self.status_file_from(conf) if True: exe = conf.filename() cmd = "'%s' stop" % exe env["SYSTEMCTL_SKIP_REDIRECT"] = "yes" newcmd = self.exec_cmd(cmd, env, conf) logg.info("%s stop %s", runs, shell_cmd(newcmd)) forkpid = os.fork() if not forkpid: self.execve_from(conf, newcmd, env) # pragma: nocover run = subprocess_waitpid(forkpid) if run.returncode: self.set_status_from(conf, "ExecStopCode", run.returncode) self.write_status_from(conf, AS="failed") else: self.clean_status_from(conf) # "inactive" return True elif runs in [ "oneshot" ]: status_file = self.status_file_from(conf) if self.get_status_from(conf, "ActiveState", "unknown") == "inactive": logg.warning("the service is already down once") return True for cmd in conf.getlist("Service", "ExecStop", []): check, cmd = checkstatus(cmd) logg.debug("{env} %s", env) newcmd = self.exec_cmd(cmd, env, conf) logg.info("%s stop %s", runs, shell_cmd(newcmd)) forkpid = os.fork() if not forkpid: self.execve_from(conf, newcmd, env) # pragma: nocover run = subprocess_waitpid(forkpid) if run.returncode and check: returncode = run.returncode service_result = "failed" break if True: if returncode: self.set_status_from(conf, "ExecStopCode", returncode) self.write_status_from(conf, AS="failed") else: self.clean_status_from(conf) # "inactive" ### fallback Stop => Kill for ["simple","notify","forking"] elif not conf.getlist("Service", "ExecStop", []): logg.info("no ExecStop => systemctl kill") if True: self.do_kill_unit_from(conf) self.clean_pid_file_from(conf) self.clean_status_from(conf) # "inactive" elif runs in [ "simple", "notify" ]: status_file = self.status_file_from(conf) size = os.path.exists(status_file) and os.path.getsize(status_file) logg.info("STATUS %s %s", status_file, size) pid = 0 for cmd in conf.getlist("Service", "ExecStop", []): check, cmd = checkstatus(cmd) env["MAINPID"] = str(self.read_mainpid_from(conf, "")) newcmd = self.exec_cmd(cmd, env, conf) logg.info("%s stop %s", runs, shell_cmd(newcmd)) forkpid = os.fork() if not forkpid: self.execve_from(conf, newcmd, env) # pragma: nocover run = subprocess_waitpid(forkpid) run = must_have_failed(run, newcmd) # TODO: a workaround # self.write_status_from(conf, MainPID=run.pid) # no ExecStop if run.returncode and check: returncode = run.returncode service_result = "failed" break pid = env.get("MAINPID",0) if pid: if self.wait_vanished_pid(pid, timeout): self.clean_pid_file_from(conf) self.clean_status_from(conf) # "inactive" else: logg.info("%s sleep as no PID was found on Stop", runs) time.sleep(MinimumTimeoutStopSec) pid = self.read_mainpid_from(conf, "") if not pid or not pid_exists(pid) or pid_zombie(pid): self.clean_pid_file_from(conf) self.clean_status_from(conf) # "inactive" elif runs in [ "forking" ]: status_file = self.status_file_from(conf) pid_file = self.pid_file_from(conf) for cmd in conf.getlist("Service", "ExecStop", []): active = self.is_active_from(conf) if pid_file: new_pid = self.read_mainpid_from(conf, "") if new_pid: env["MAINPID"] = str(new_pid) check, cmd = checkstatus(cmd) logg.debug("{env} %s", env) newcmd = self.exec_cmd(cmd, env, conf) logg.info("fork stop %s", shell_cmd(newcmd)) forkpid = os.fork() if not forkpid: self.execve_from(conf, newcmd, env) # pragma: nocover run = subprocess_waitpid(forkpid) if run.returncode and check: returncode = run.returncode service_result = "failed" break pid = env.get("MAINPID",0) if pid: if self.wait_vanished_pid(pid, timeout): self.clean_pid_file_from(conf) else: logg.info("%s sleep as no PID was found on Stop", runs) time.sleep(MinimumTimeoutStopSec) pid = self.read_mainpid_from(conf, "") if not pid or not pid_exists(pid) or pid_zombie(pid): self.clean_pid_file_from(conf) if returncode: if os.path.isfile(status_file): self.set_status_from(conf, "ExecStopCode", returncode) self.write_status_from(conf, AS="failed") else: self.clean_status_from(conf) # "inactive" else: logg.error("unsupported run type '%s'", runs) return False # POST sequence active = self.is_active_from(conf) if not active: env["SERVICE_RESULT"] = service_result for cmd in conf.getlist("Service", "ExecStopPost", []): check, cmd = checkstatus(cmd) newcmd = self.exec_cmd(cmd, env, conf) logg.info("post-stop %s", shell_cmd(newcmd)) forkpid = os.fork() if not forkpid: self.execve_from(conf, newcmd, env) # pragma: nocover run = subprocess_waitpid(forkpid) logg.debug("post-stop done (%s) <-%s>", run.returncode or "OK", run.signal or "") return service_result == "success" def wait_vanished_pid(self, pid, timeout): if not pid: return True logg.info("wait for PID %s to vanish (%ss)", pid, timeout) for x in xrange(int(timeout)): if not self.is_active_pid(pid): logg.info("wait for PID %s is done (%s.)", pid, x) return True time.sleep(1) # until TimeoutStopSec logg.info("wait for PID %s failed (%s.)", pid, x) return False def reload_modules(self, *modules): """ [UNIT]... -- reload these units """ self.wait_system() found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] return self.reload_units(units) and found_all def reload_units(self, units): """ fails if any unit fails to reload """ self.wait_system() done = True for unit in self.sortedAfter(units): if not self.reload_unit(unit): done = False return done def reload_unit(self, unit): conf = self.load_unit_conf(unit) if conf is None: logg.error("Unit %s could not be found.", unit) return False if self.not_user_conf(conf): logg.error("Unit %s not for --user mode", unit) return False return self.reload_unit_from(conf) def reload_unit_from(self, conf): if not conf: return False if self.syntax_check(conf) > 100: return False with waitlock(conf): logg.info(" reload unit %s => %s", conf.name(), conf.filename()) return self.do_reload_unit_from(conf) def do_reload_unit_from(self, conf): runs = conf.get("Service", "Type", "simple").lower() env = self.get_env(conf) self.exec_check_service(conf, env, "ExecReload") if runs in [ "sysv" ]: status_file = self.status_file_from(conf) if True: exe = conf.filename() cmd = "'%s' reload" % exe env["SYSTEMCTL_SKIP_REDIRECT"] = "yes" newcmd = self.exec_cmd(cmd, env, conf) logg.info("%s reload %s", runs, shell_cmd(newcmd)) forkpid = os.fork() if not forkpid: self.execve_from(conf, newcmd, env) # pragma: nocover run = subprocess_waitpid(forkpid) self.set_status_from(conf, "ExecReloadCode", run.returncode) if run.returncode: self.write_status_from(conf, AS="failed") return False else: self.write_status_from(conf, AS="active") return True elif runs in [ "simple", "notify", "forking" ]: if not self.is_active_from(conf): logg.info("no reload on inactive service %s", conf.name()) return True for cmd in conf.getlist("Service", "ExecReload", []): env["MAINPID"] = str(self.read_mainpid_from(conf, "")) check, cmd = checkstatus(cmd) newcmd = self.exec_cmd(cmd, env, conf) logg.info("%s reload %s", runs, shell_cmd(newcmd)) forkpid = os.fork() if not forkpid: self.execve_from(conf, newcmd, env) # pragma: nocover run = subprocess_waitpid(forkpid) if check and run.returncode: logg.error("Job for %s failed because the control process exited with error code. (%s)", conf.name(), run.returncode) return False time.sleep(MinimumYield) return True elif runs in [ "oneshot" ]: logg.debug("ignored run type '%s' for reload", runs) return True else: logg.error("unsupported run type '%s'", runs) return False def restart_modules(self, *modules): """ [UNIT]... -- restart these units """ found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] return self.restart_units(units) and found_all def restart_units(self, units): """ fails if any unit fails to restart """ self.wait_system() done = True for unit in self.sortedAfter(units): if not self.restart_unit(unit): done = False return done def restart_unit(self, unit): conf = self.load_unit_conf(unit) if conf is None: logg.error("Unit %s could not be found.", unit) return False if self.not_user_conf(conf): logg.error("Unit %s not for --user mode", unit) return False return self.restart_unit_from(conf) def restart_unit_from(self, conf): if not conf: return False if self.syntax_check(conf) > 100: return False with waitlock(conf): logg.info(" restart unit %s => %s", conf.name(), conf.filename()) if not self.is_active_from(conf): return self.do_start_unit_from(conf) else: return self.do_restart_unit_from(conf) def do_restart_unit_from(self, conf): logg.info("(restart) => stop/start") self.do_stop_unit_from(conf) return self.do_start_unit_from(conf) def try_restart_modules(self, *modules): """ [UNIT]... -- try-restart these units """ found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] return self.try_restart_units(units) and found_all def try_restart_units(self, units): """ fails if any module fails to try-restart """ self.wait_system() done = True for unit in self.sortedAfter(units): if not self.try_restart_unit(unit): done = False return done def try_restart_unit(self, unit): """ only do 'restart' if 'active' """ conf = self.load_unit_conf(unit) if conf is None: logg.error("Unit %s could not be found.", unit) return False if self.not_user_conf(conf): logg.error("Unit %s not for --user mode", unit) return False with waitlock(conf): logg.info(" try-restart unit %s => %s", conf.name(), conf.filename()) if self.is_active_from(conf): return self.do_restart_unit_from(conf) return True def reload_or_restart_modules(self, *modules): """ [UNIT]... -- reload-or-restart these units """ found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] return self.reload_or_restart_units(units) and found_all def reload_or_restart_units(self, units): """ fails if any unit does not reload-or-restart """ self.wait_system() done = True for unit in self.sortedAfter(units): if not self.reload_or_restart_unit(unit): done = False return done def reload_or_restart_unit(self, unit): """ do 'reload' if specified, otherwise do 'restart' """ conf = self.load_unit_conf(unit) if conf is None: logg.error("Unit %s could not be found.", unit) return False if self.not_user_conf(conf): logg.error("Unit %s not for --user mode", unit) return False return self.reload_or_restart_unit_from(conf) def reload_or_restart_unit_from(self, conf): """ do 'reload' if specified, otherwise do 'restart' """ if not conf: return False with waitlock(conf): logg.info(" reload-or-restart unit %s => %s", conf.name(), conf.filename()) return self.do_reload_or_restart_unit_from(conf) def do_reload_or_restart_unit_from(self, conf): if not self.is_active_from(conf): # try: self.stop_unit_from(conf) # except Exception as e: pass return self.do_start_unit_from(conf) elif conf.getlist("Service", "ExecReload", []): logg.info("found service to have ExecReload -> 'reload'") return self.do_reload_unit_from(conf) else: logg.info("found service without ExecReload -> 'restart'") return self.do_restart_unit_from(conf) def reload_or_try_restart_modules(self, *modules): """ [UNIT]... -- reload-or-try-restart these units """ found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] return self.reload_or_try_restart_units(units) and found_all def reload_or_try_restart_units(self, units): """ fails if any unit fails to reload-or-try-restart """ self.wait_system() done = True for unit in self.sortedAfter(units): if not self.reload_or_try_restart_unit(unit): done = False return done def reload_or_try_restart_unit(self, unit): conf = self.load_unit_conf(unit) if conf is None: logg.error("Unit %s could not be found.", unit) return False if self.not_user_conf(conf): logg.error("Unit %s not for --user mode", unit) return False return self.reload_or_try_restart_unit_from(conf) def reload_or_try_restart_unit_from(self, conf): with waitlock(conf): logg.info(" reload-or-try-restart unit %s => %s", conf.name(), conf.filename()) return self.do_reload_or_try_restart_unit_from(conf) def do_reload_or_try_restart_unit_from(self, conf): if conf.getlist("Service", "ExecReload", []): return self.do_reload_unit_from(conf) elif not self.is_active_from(conf): return True else: return self.do_restart_unit_from(conf) def kill_modules(self, *modules): """ [UNIT]... -- kill these units """ found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] return self.kill_units(units) and found_all def kill_units(self, units): """ fails if any unit could not be killed """ self.wait_system() done = True for unit in self.sortedBefore(units): if not self.kill_unit(unit): done = False return done def kill_unit(self, unit): conf = self.load_unit_conf(unit) if conf is None: logg.error("Unit %s could not be found.", unit) return False if self.not_user_conf(conf): logg.error("Unit %s not for --user mode", unit) return False return self.kill_unit_from(conf) def kill_unit_from(self, conf): if not conf: return False with waitlock(conf): logg.info(" kill unit %s => %s", conf.name(), conf.filename()) return self.do_kill_unit_from(conf) def do_kill_unit_from(self, conf): started = time.time() doSendSIGKILL = conf.getbool("Service", "SendSIGKILL", "yes") doSendSIGHUP = conf.getbool("Service", "SendSIGHUP", "no") useKillMode = conf.get("Service", "KillMode", "control-group") useKillSignal = conf.get("Service", "KillSignal", "SIGTERM") kill_signal = getattr(signal, useKillSignal) timeout = self.get_TimeoutStopSec(conf) status_file = self.status_file_from(conf) size = os.path.exists(status_file) and os.path.getsize(status_file) logg.info("STATUS %s %s", status_file, size) mainpid = to_int(self.read_mainpid_from(conf, "")) self.clean_status_from(conf) # clear RemainAfterExit and TimeoutStartSec if not mainpid: if useKillMode in ["control-group"]: logg.warning("no main PID [%s]", conf.filename()) logg.warning("and there is no control-group here") else: logg.info("no main PID [%s]", conf.filename()) return False if not pid_exists(mainpid) or pid_zombie(mainpid): logg.debug("ignoring children when mainpid is already dead") # because we list child processes, not processes in control-group return True pidlist = self.pidlist_of(mainpid) # here if pid_exists(mainpid): logg.info("stop kill PID %s", mainpid) self._kill_pid(mainpid, kill_signal) if useKillMode in ["control-group"]: if len(pidlist) > 1: logg.info("stop control-group PIDs %s", pidlist) for pid in pidlist: if pid != mainpid: self._kill_pid(pid, kill_signal) if doSendSIGHUP: logg.info("stop SendSIGHUP to PIDs %s", pidlist) for pid in pidlist: self._kill_pid(pid, signal.SIGHUP) # wait for the processes to have exited while True: dead = True for pid in pidlist: if pid_exists(pid) and not pid_zombie(pid): dead = False break if dead: break if time.time() > started + timeout: logg.info("service PIDs not stopped after %s", timeout) break time.sleep(1) # until TimeoutStopSec if dead or not doSendSIGKILL: logg.info("done kill PID %s %s", mainpid, dead and "OK") return dead if useKillMode in [ "control-group", "mixed" ]: logg.info("hard kill PIDs %s", pidlist) for pid in pidlist: if pid != mainpid: self._kill_pid(pid, signal.SIGKILL) time.sleep(MinimumYield) # useKillMode in [ "control-group", "mixed", "process" ] if pid_exists(mainpid): logg.info("hard kill PID %s", mainpid) self._kill_pid(mainpid, signal.SIGKILL) time.sleep(MinimumYield) dead = not pid_exists(mainpid) or pid_zombie(mainpid) logg.info("done hard kill PID %s %s", mainpid, dead and "OK") return dead def _kill_pid(self, pid, kill_signal = None): try: sig = kill_signal or signal.SIGTERM os.kill(pid, sig) except OSError as e: if e.errno == errno.ESRCH or e.errno == errno.ENOENT: logg.debug("kill PID %s => No such process", pid) return True else: logg.error("kill PID %s => %s", pid, str(e)) return False return not pid_exists(pid) or pid_zombie(pid) def is_active_modules(self, *modules): """ [UNIT].. -- check if these units are in active state implements True if all is-active = True """ # systemctl returns multiple lines, one for each argument # "active" when is_active # "inactive" when not is_active # "unknown" when not enabled # The return code is set to # 0 when "active" # 1 when unit is not found # 3 when any "inactive" or "unknown" # However: # TODO!!!!! BUG in original systemctl!! # documentation says " exit code 0 if at least one is active" # and "Unless --quiet is specified, print the unit state" units = [] results = [] for module in modules: units = self.match_units([ module ]) if not units: logg.error("Unit %s could not be found.", unit_of(module)) results += [ "unknown" ] continue for unit in units: active = self.get_active_unit(unit) enabled = self.enabled_unit(unit) if enabled != "enabled": active = "unknown" results += [ active ] break ## how it should work: status = "active" in results ## how 'systemctl' works: non_active = [ result for result in results if result != "active" ] status = not non_active if not status: status = 3 if not _quiet: return status, results else: return status def is_active_from(self, conf): """ used in try-restart/other commands to check if needed. """ if not conf: return False return self.get_active_from(conf) == "active" def active_pid_from(self, conf): if not conf: return False pid = self.read_mainpid_from(conf, "") return self.is_active_pid(pid) def is_active_pid(self, pid): """ returns pid if the pid is still an active process """ if pid and pid_exists(pid) and not pid_zombie(pid): return pid # usually a string (not null) return None def get_active_unit(self, unit): """ returns 'active' 'inactive' 'failed' 'unknown' """ conf = self.get_unit_conf(unit) if not conf.loaded(): logg.warning("Unit %s could not be found.", unit) return "unknown" return self.get_active_from(conf) def get_active_from(self, conf): """ returns 'active' 'inactive' 'failed' 'unknown' """ # used in try-restart/other commands to check if needed. if not conf: return "unknown" pid_file = self.pid_file_from(conf) if pid_file: # application PIDFile if not os.path.exists(pid_file): return "inactive" status_file = self.status_file_from(conf) if self.getsize(status_file): state = self.get_status_from(conf, "ActiveState", "") if state: logg.info("get_status_from %s => %s", conf.name(), state) return state pid = self.read_mainpid_from(conf, "") logg.debug("pid_file '%s' => PID %s", pid_file or status_file, pid) if pid: if not pid_exists(pid) or pid_zombie(pid): return "failed" return "active" else: return "inactive" def get_substate_from(self, conf): """ returns 'running' 'exited' 'dead' 'failed' 'plugged' 'mounted' """ if not conf: return False pid_file = self.pid_file_from(conf) if pid_file: if not os.path.exists(pid_file): return "dead" status_file = self.status_file_from(conf) if self.getsize(status_file): state = self.get_status_from(conf, "ActiveState", "") if state: if state in [ "active" ]: return self.get_status_from(conf, "SubState", "running") else: return self.get_status_from(conf, "SubState", "dead") pid = self.read_mainpid_from(conf, "") logg.debug("pid_file '%s' => PID %s", pid_file or status_file, pid) if pid: if not pid_exists(pid) or pid_zombie(pid): return "failed" return "running" else: return "dead" def is_failed_modules(self, *modules): """ [UNIT]... -- check if these units are in failes state implements True if any is-active = True """ units = [] results = [] for module in modules: units = self.match_units([ module ]) if not units: logg.error("Unit %s could not be found.", unit_of(module)) results += [ "unknown" ] continue for unit in units: active = self.get_active_unit(unit) enabled = self.enabled_unit(unit) if enabled != "enabled": active = "unknown" results += [ active ] break status = "failed" in results if not _quiet: return status, results else: return status def is_failed_from(self, conf): if conf is None: return True return self.get_active_from(conf) == "failed" def reset_failed_modules(self, *modules): """ [UNIT]... -- Reset failed state for all, one, or more units """ units = [] status = True for module in modules: units = self.match_units([ module ]) if not units: logg.error("Unit %s could not be found.", unit_of(module)) return 1 for unit in units: if not self.reset_failed_unit(unit): logg.error("Unit %s could not be reset.", unit_of(module)) status = False break return status def reset_failed_unit(self, unit): conf = self.get_unit_conf(unit) if not conf.loaded(): logg.warning("Unit %s could not be found.", unit) return False if self.not_user_conf(conf): logg.error("Unit %s not for --user mode", unit) return False return self.reset_failed_from(conf) def reset_failed_from(self, conf): if conf is None: return True if not self.is_failed_from(conf): return False done = False status_file = self.status_file_from(conf) if status_file and os.path.exists(status_file): try: os.remove(status_file) done = True logg.debug("done rm %s", status_file) except Exception as e: logg.error("while rm %s: %s", status_file, e) pid_file = self.pid_file_from(conf) if pid_file and os.path.exists(pid_file): try: os.remove(pid_file) done = True logg.debug("done rm %s", pid_file) except Exception as e: logg.error("while rm %s: %s", pid_file, e) return done def status_modules(self, *modules): """ [UNIT]... check the status of these units. """ found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] status, result = self.status_units(units) if not found_all: status = 3 # same as (dead) # original behaviour return (status, result) def status_units(self, units): """ concatenates the status output of all units and the last non-successful statuscode """ status, result = 0, "" for unit in units: status1, result1 = self.status_unit(unit) if status1: status = status1 if result: result += "\n\n" result += result1 return status, result def status_unit(self, unit): conf = self.get_unit_conf(unit) result = "%s - %s" % (unit, self.get_description_from(conf)) loaded = conf.loaded() if loaded: filename = conf.filename() enabled = self.enabled_from(conf) result += "\n Loaded: {loaded} ({filename}, {enabled})".format(**locals()) for path in conf.overrides(): result += "\n Drop-In: {path}".format(**locals()) else: result += "\n Loaded: failed" return 3, result active = self.get_active_from(conf) substate = self.get_substate_from(conf) result += "\n Active: {} ({})".format(active, substate) if active == "active": return 0, result else: return 3, result def cat_modules(self, *modules): """ [UNIT]... show the *.system file for these" """ found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] done, result = self.cat_units(units) return (done and found_all, result) def cat_units(self, units): done = True result = "" for unit in units: text = self.cat_unit(unit) if not text: done = False else: if result: result += "\n\n" result += text return done, result def cat_unit(self, unit): try: unit_file = self.unit_file(unit) if unit_file: return open(unit_file).read() logg.error("no file for unit '%s'", unit) except Exception as e: print("Unit {} is not-loaded: {}".format(unit, e)) return False ## ## def load_preset_files(self, module = None): # -> [ preset-file-names,... ] """ reads all preset files, returns the scanned files """ if self._preset_file_list is None: self._preset_file_list = {} for folder in self.preset_folders(): if not folder: continue if self._root: folder = os_path(self._root, folder) if not os.path.isdir(folder): continue for name in os.listdir(folder): if not name.endswith(".preset"): continue if name not in self._preset_file_list: path = os.path.join(folder, name) if os.path.isdir(path): continue preset = PresetFile().read(path) self._preset_file_list[name] = preset logg.debug("found %s preset files", len(self._preset_file_list)) return sorted(self._preset_file_list.keys()) def get_preset_of_unit(self, unit): """ [UNIT] check the *.preset of this unit """ self.load_preset_files() for filename in sorted(self._preset_file_list.keys()): preset = self._preset_file_list[filename] status = preset.get_preset(unit) if status: return status return None def preset_modules(self, *modules): """ [UNIT]... -- set 'enabled' when in *.preset """ if self.user_mode(): logg.warning("preset makes no sense in --user mode") return True found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] return self.preset_units(units) and found_all def preset_units(self, units): """ fails if any unit could not be changed """ self.wait_system() fails = 0 found = 0 for unit in units: status = self.get_preset_of_unit(unit) if not status: continue found += 1 if status.startswith("enable"): if self._preset_mode == "disable": continue logg.info("preset enable %s", unit) if not self.enable_unit(unit): logg.warning("failed to enable %s", unit) fails += 1 if status.startswith("disable"): if self._preset_mode == "enable": continue logg.info("preset disable %s", unit) if not self.disable_unit(unit): logg.warning("failed to disable %s", unit) fails += 1 return not fails and not not found def system_preset_all(self, *modules): """ 'preset' all services enable or disable services according to *.preset files """ if self.user_mode(): logg.warning("preset-all makes no sense in --user mode") return True found_all = True units = self.match_units() # TODO: how to handle module arguments return self.preset_units(units) and found_all def wanted_from(self, conf, default = None): if not conf: return default return conf.get("Install", "WantedBy", default, True) def enablefolders(self, wanted): if self.user_mode(): for folder in self.user_folders(): yield self.default_enablefolder(wanted, folder) if True: for folder in self.system_folders(): yield self.default_enablefolder(wanted, folder) def enablefolder(self, wanted = None): if self.user_mode(): user_folder = self.user_folder() return self.default_enablefolder(wanted, user_folder) else: return self.default_enablefolder(wanted) def default_enablefolder(self, wanted = None, basefolder = None): basefolder = basefolder or self.system_folder() if not wanted: return wanted if not wanted.endswith(".wants"): wanted = wanted + ".wants" return os.path.join(basefolder, wanted) def enable_modules(self, *modules): """ [UNIT]... -- enable these units """ found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: logg.info("matched %s", unit) #++ if unit not in units: units += [ unit ] return self.enable_units(units) and found_all def enable_units(self, units): self.wait_system() done = True for unit in units: if not self.enable_unit(unit): done = False elif self._now: self.start_unit(unit) return done def enable_unit(self, unit): unit_file = self.unit_file(unit) if not unit_file: logg.error("Unit %s could not be found.", unit) return False if self.is_sysv_file(unit_file): if self.user_mode(): logg.error("Initscript %s not for --user mode", unit) return False return self.enable_unit_sysv(unit_file) conf = self.get_unit_conf(unit) if self.not_user_conf(conf): logg.error("Unit %s not for --user mode", unit) return False wanted = self.wanted_from(self.get_unit_conf(unit)) if not wanted: return False # "static" is-enabled folder = self.enablefolder(wanted) if self._root: folder = os_path(self._root, folder) if not os.path.isdir(folder): os.makedirs(folder) target = os.path.join(folder, os.path.basename(unit_file)) if True: _f = self._force and "-f" or "" logg.info("ln -s {_f} '{unit_file}' '{target}'".format(**locals())) if self._force and os.path.islink(target): os.remove(target) if not os.path.islink(target): os.symlink(unit_file, target) return True def rc3_root_folder(self): old_folder = "/etc/rc3.d" new_folder = "/etc/init.d/rc3.d" if self._root: old_folder = os_path(self._root, old_folder) new_folder = os_path(self._root, new_folder) if os.path.isdir(old_folder): return old_folder return new_folder def rc5_root_folder(self): old_folder = "/etc/rc5.d" new_folder = "/etc/init.d/rc5.d" if self._root: old_folder = os_path(self._root, old_folder) new_folder = os_path(self._root, new_folder) if os.path.isdir(old_folder): return old_folder return new_folder def enable_unit_sysv(self, unit_file): # a "multi-user.target"/rc3 is also started in /rc5 rc3 = self._enable_unit_sysv(unit_file, self.rc3_root_folder()) rc5 = self._enable_unit_sysv(unit_file, self.rc5_root_folder()) return rc3 and rc5 def _enable_unit_sysv(self, unit_file, rc_folder): name = os.path.basename(unit_file) nameS = "S50"+name nameK = "K50"+name if not os.path.isdir(rc_folder): os.makedirs(rc_folder) # do not double existing entries for found in os.listdir(rc_folder): m = re.match(r"S\d\d(.*)", found) if m and m.group(1) == name: nameS = found m = re.match(r"K\d\d(.*)", found) if m and m.group(1) == name: nameK = found target = os.path.join(rc_folder, nameS) if not os.path.exists(target): os.symlink(unit_file, target) target = os.path.join(rc_folder, nameK) if not os.path.exists(target): os.symlink(unit_file, target) return True def disable_modules(self, *modules): """ [UNIT]... -- disable these units """ found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] return self.disable_units(units) and found_all def disable_units(self, units): self.wait_system() done = True for unit in units: if not self.disable_unit(unit): done = False return done def disable_unit(self, unit): unit_file = self.unit_file(unit) if not unit_file: logg.error("Unit %s could not be found.", unit) return False if self.is_sysv_file(unit_file): if self.user_mode(): logg.error("Initscript %s not for --user mode", unit) return False return self.disable_unit_sysv(unit_file) conf = self.get_unit_conf(unit) if self.not_user_conf(conf): logg.error("Unit %s not for --user mode", unit) return False wanted = self.wanted_from(self.get_unit_conf(unit)) if not wanted: return False # "static" is-enabled for folder in self.enablefolders(wanted): if self._root: folder = os_path(self._root, folder) target = os.path.join(folder, os.path.basename(unit_file)) if os.path.isfile(target): try: _f = self._force and "-f" or "" logg.info("rm {_f} '{target}'".format(**locals())) os.remove(target) except IOError as e: logg.error("disable %s: %s", target, e) except OSError as e: logg.error("disable %s: %s", target, e) return True def disable_unit_sysv(self, unit_file): rc3 = self._disable_unit_sysv(unit_file, self.rc3_root_folder()) rc5 = self._disable_unit_sysv(unit_file, self.rc5_root_folder()) return rc3 and rc5 def _disable_unit_sysv(self, unit_file, rc_folder): # a "multi-user.target"/rc3 is also started in /rc5 name = os.path.basename(unit_file) nameS = "S50"+name nameK = "K50"+name # do not forget the existing entries for found in os.listdir(rc_folder): m = re.match(r"S\d\d(.*)", found) if m and m.group(1) == name: nameS = found m = re.match(r"K\d\d(.*)", found) if m and m.group(1) == name: nameK = found target = os.path.join(rc_folder, nameS) if os.path.exists(target): os.unlink(target) target = os.path.join(rc_folder, nameK) if os.path.exists(target): os.unlink(target) return True def is_enabled_sysv(self, unit_file): name = os.path.basename(unit_file) target = os.path.join(self.rc3_root_folder(), "S50%s" % name) if os.path.exists(target): return True return False def is_enabled_modules(self, *modules): """ [UNIT]... -- check if these units are enabled returns True if any of them is enabled.""" found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] return self.is_enabled_units(units) # and found_all def is_enabled_units(self, units): """ true if any is enabled, and a list of infos """ result = False infos = [] for unit in units: infos += [ self.enabled_unit(unit) ] if self.is_enabled(unit): result = True return result, infos def is_enabled(self, unit): unit_file = self.unit_file(unit) if not unit_file: logg.error("Unit %s could not be found.", unit) return False if self.is_sysv_file(unit_file): return self.is_enabled_sysv(unit_file) wanted = self.wanted_from(self.get_unit_conf(unit)) if not wanted: return True # "static" for folder in self.enablefolders(wanted): if self._root: folder = os_path(self._root, folder) target = os.path.join(folder, os.path.basename(unit_file)) if os.path.isfile(target): return True return False def enabled_unit(self, unit): conf = self.get_unit_conf(unit) return self.enabled_from(conf) def enabled_from(self, conf): unit_file = conf.filename() if self.is_sysv_file(unit_file): state = self.is_enabled_sysv(unit_file) if state: return "enabled" return "disabled" if conf.masked: return "masked" wanted = self.wanted_from(conf) if not wanted: return "static" for folder in self.enablefolders(wanted): if self._root: folder = os_path(self._root, folder) target = os.path.join(folder, os.path.basename(unit_file)) if os.path.isfile(target): return "enabled" return "disabled" def mask_modules(self, *modules): """ [UNIT]... -- mask non-startable units """ found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] return self.mask_units(units) and found_all def mask_units(self, units): self.wait_system() done = True for unit in units: if not self.mask_unit(unit): done = False return done def mask_unit(self, unit): unit_file = self.unit_file(unit) if not unit_file: logg.error("Unit %s could not be found.", unit) return False if self.is_sysv_file(unit_file): logg.error("Initscript %s can not be masked", unit) return False conf = self.get_unit_conf(unit) if self.not_user_conf(conf): logg.error("Unit %s not for --user mode", unit) return False folder = self.mask_folder() if self._root: folder = os_path(self._root, folder) if not os.path.isdir(folder): os.makedirs(folder) target = os.path.join(folder, os.path.basename(unit_file)) if True: _f = self._force and "-f" or "" logg.debug("ln -s {_f} /dev/null '{target}'".format(**locals())) if self._force and os.path.islink(target): os.remove(target) if not os.path.exists(target): os.symlink("/dev/null", target) logg.info("Created symlink {target} -> /dev/null".format(**locals())) return True elif os.path.islink(target): logg.debug("mask symlink does already exist: %s", target) return True else: logg.error("mask target does already exist: %s", target) return False def mask_folder(self): for folder in self.mask_folders(): if folder: return folder raise Exception("did not find any systemd/system folder") def mask_folders(self): if self.user_mode(): for folder in self.user_folders(): yield folder if True: for folder in self.system_folders(): yield folder def unmask_modules(self, *modules): """ [UNIT]... -- unmask non-startable units """ found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] return self.unmask_units(units) and found_all def unmask_units(self, units): self.wait_system() done = True for unit in units: if not self.unmask_unit(unit): done = False return done def unmask_unit(self, unit): unit_file = self.unit_file(unit) if not unit_file: logg.error("Unit %s could not be found.", unit) return False if self.is_sysv_file(unit_file): logg.error("Initscript %s can not be un/masked", unit) return False conf = self.get_unit_conf(unit) if self.not_user_conf(conf): logg.error("Unit %s not for --user mode", unit) return False folder = self.mask_folder() if self._root: folder = os_path(self._root, folder) target = os.path.join(folder, os.path.basename(unit_file)) if True: _f = self._force and "-f" or "" logg.info("rm {_f} '{target}'".format(**locals())) if os.path.islink(target): os.remove(target) return True elif not os.path.exists(target): logg.debug("Symlink did exist anymore: %s", target) return True else: logg.warning("target is not a symlink: %s", target) return True def list_dependencies_modules(self, *modules): """ [UNIT]... show the dependency tree" """ found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] return self.list_dependencies_units(units) # and found_all def list_dependencies_units(self, units): if self._now: return self.list_start_dependencies_units(units) result = [] for unit in units: if result: result += [ "", "" ] result += self.list_dependencies_unit(unit) return result def list_dependencies_unit(self, unit): result = [] for line in self.list_dependencies(unit, ""): result += [ line ] return result def list_dependencies(self, unit, indent = None, mark = None, loop = []): mapping = {} mapping["Requires"] = "required to start" mapping["Wants"] = "wanted to start" mapping["Requisite"] = "required started" mapping["Bindsto"] = "binds to start" mapping["PartOf"] = "part of started" mapping[".requires"] = ".required to start" mapping[".wants"] = ".wanted to start" mapping["PropagateReloadTo"] = "(to be reloaded as well)" mapping["Conflicts"] = "(to be stopped on conflict)" restrict = ["Requires", "Requisite", "ConsistsOf", "Wants", "BindsTo", ".requires", ".wants"] indent = indent or "" mark = mark or "" deps = self.get_dependencies_unit(unit) conf = self.get_unit_conf(unit) if not conf.loaded(): if not self._show_all: return yield "%s(%s): %s" % (indent, unit, mark) else: yield "%s%s: %s" % (indent, unit, mark) for stop_recursion in [ "Conflict", "conflict", "reloaded", "Propagate" ]: if stop_recursion in mark: return for dep in deps: if dep in loop: logg.debug("detected loop at %s", dep) continue new_loop = loop + list(deps.keys()) new_indent = indent + "| " new_mark = deps[dep] if not self._show_all: if new_mark not in restrict: continue if new_mark in mapping: new_mark = mapping[new_mark] restrict = ["Requires", "Requisite", "ConsistsOf", "Wants", "BindsTo", ".requires", ".wants"] for line in self.list_dependencies(dep, new_indent, new_mark, new_loop): yield line def get_dependencies_unit(self, unit): conf = self.get_unit_conf(unit) deps = {} for style in [ "Requires", "Wants", "Requisite", "BindsTo", "PartOf", ".requires", ".wants", "PropagateReloadTo", "Conflicts", ]: if style.startswith("."): for folder in self.sysd_folders(): if not folder: continue require_path = os.path.join(folder, unit + style) if self._root: require_path = os_path(self._root, require_path) if os.path.isdir(require_path): for required in os.listdir(require_path): if required not in deps: deps[required] = style else: for requirelist in conf.getlist("Unit", style, []): for required in requirelist.strip().split(" "): deps[required.strip()] = style return deps def get_start_dependencies(self, unit): # pragma: no cover """ the list of services to be started as well / TODO: unused """ deps = {} unit_deps = self.get_dependencies_unit(unit) for dep_unit, dep_style in unit_deps.items(): restrict = ["Requires", "Requisite", "ConsistsOf", "Wants", "BindsTo", ".requires", ".wants"] if dep_style in restrict: if dep_unit in deps: if dep_style not in deps[dep_unit]: deps[dep_unit].append( dep_style) else: deps[dep_unit] = [ dep_style ] next_deps = self.get_start_dependencies(dep_unit) for dep, styles in next_deps.items(): for style in styles: if dep in deps: if style not in deps[dep]: deps[dep].append(style) else: deps[dep] = [ style ] return deps def list_start_dependencies_units(self, units): unit_order = [] deps = {} for unit in units: unit_order.append(unit) # unit_deps = self.get_start_dependencies(unit) # TODO unit_deps = self.get_dependencies_unit(unit) for dep_unit, styles in unit_deps.items(): styles = to_list(styles) for dep_style in styles: if dep_unit in deps: if dep_style not in deps[dep_unit]: deps[dep_unit].append( dep_style) else: deps[dep_unit] = [ dep_style ] deps_conf = [] for dep in deps: if dep in unit_order: continue conf = self.get_unit_conf(dep) if conf.loaded(): deps_conf.append(conf) for unit in unit_order: deps[unit] = [ "Requested" ] conf = self.get_unit_conf(unit) if conf.loaded(): deps_conf.append(conf) result = [] for dep in sortedAfter(deps_conf, cmp=compareAfter): line = (dep.name(), "(%s)" % (" ".join(deps[dep.name()]))) result.append(line) return result def sortedAfter(self, unitlist): """ get correct start order for the unit list (ignoring masked units) """ conflist = [ self.get_unit_conf(unit) for unit in unitlist ] if True: conflist = [] for unit in unitlist: conf = self.get_unit_conf(unit) if conf.masked: logg.debug("ignoring masked unit %s", unit) continue conflist.append(conf) sortlist = sortedAfter(conflist) return [ item.name() for item in sortlist ] def sortedBefore(self, unitlist): """ get correct start order for the unit list (ignoring masked units) """ conflist = [ self.get_unit_conf(unit) for unit in unitlist ] if True: conflist = [] for unit in unitlist: conf = self.get_unit_conf(unit) if conf.masked: logg.debug("ignoring masked unit %s", unit) continue conflist.append(conf) sortlist = sortedAfter(reversed(conflist)) return [ item.name() for item in reversed(sortlist) ] def system_daemon_reload(self): """ reload does will only check the service files here. The returncode will tell the number of warnings, and it is over 100 if it can not continue even for the relaxed systemctl.py style of execution. """ errors = 0 for unit in self.match_units(): try: conf = self.get_unit_conf(unit) except Exception as e: logg.error("%s: can not read unit file %s\n\t%s", unit, conf.filename(), e) continue errors += self.syntax_check(conf) if errors: logg.warning(" (%s) found %s problems", errors, errors % 100) return True # errors def syntax_check(self, conf): if conf.filename() and conf.filename().endswith(".service"): return self.syntax_check_service(conf) return 0 def syntax_check_service(self, conf): unit = conf.name() if not conf.data.has_section("Service"): logg.error(" %s: a .service file without [Service] section", unit) return 101 errors = 0 haveType = conf.get("Service", "Type", "simple") haveExecStart = conf.getlist("Service", "ExecStart", []) haveExecStop = conf.getlist("Service", "ExecStop", []) haveExecReload = conf.getlist("Service", "ExecReload", []) usedExecStart = [] usedExecStop = [] usedExecReload = [] if haveType not in [ "simple", "forking", "notify", "oneshot", "dbus", "idle", "sysv"]: logg.error(" %s: Failed to parse service type, ignoring: %s", unit, haveType) errors += 100 for line in haveExecStart: if not line.startswith("/") and not line.startswith("-/"): logg.error(" %s: Executable path is not absolute, ignoring: %s", unit, line.strip()) errors += 1 usedExecStart.append(line) for line in haveExecStop: if not line.startswith("/") and not line.startswith("-/"): logg.error(" %s: Executable path is not absolute, ignoring: %s", unit, line.strip()) errors += 1 usedExecStop.append(line) for line in haveExecReload: if not line.startswith("/") and not line.startswith("-/"): logg.error(" %s: Executable path is not absolute, ignoring: %s", unit, line.strip()) errors += 1 usedExecReload.append(line) if haveType in ["simple", "notify", "forking"]: if not usedExecStart and not usedExecStop: logg.error(" %s: Service lacks both ExecStart and ExecStop= setting. Refusing.", unit) errors += 101 elif not usedExecStart and haveType != "oneshot": logg.error(" %s: Service has no ExecStart= setting, which is only allowed for Type=oneshot services. Refusing.", unit) errors += 101 if len(usedExecStart) > 1 and haveType != "oneshot": logg.error(" %s: there may be only one ExecStart statement (unless for 'oneshot' services)." + "\n\t\t\tYou can use ExecStartPre / ExecStartPost to add additional commands.", unit) errors += 1 if len(usedExecStop) > 1 and haveType != "oneshot": logg.info(" %s: there should be only one ExecStop statement (unless for 'oneshot' services)." + "\n\t\t\tYou can use ExecStopPost to add additional commands (also executed on failed Start)", unit) if len(usedExecReload) > 1: logg.info(" %s: there should be only one ExecReload statement." + "\n\t\t\tUse ' ; ' for multiple commands (ExecReloadPost or ExedReloadPre do not exist)", unit) if len(usedExecReload) > 0 and "/bin/kill " in usedExecReload[0]: logg.warning(" %s: the use of /bin/kill is not recommended for ExecReload as it is asychronous." + "\n\t\t\tThat means all the dependencies will perform the reload simultanously / out of order.", unit) if conf.getlist("Service", "ExecRestart", []): #pragma: no cover logg.error(" %s: there no such thing as an ExecRestart (ignored)", unit) if conf.getlist("Service", "ExecRestartPre", []): #pragma: no cover logg.error(" %s: there no such thing as an ExecRestartPre (ignored)", unit) if conf.getlist("Service", "ExecRestartPost", []): #pragma: no cover logg.error(" %s: there no such thing as an ExecRestartPost (ignored)", unit) if conf.getlist("Service", "ExecReloadPre", []): #pragma: no cover logg.error(" %s: there no such thing as an ExecReloadPre (ignored)", unit) if conf.getlist("Service", "ExecReloadPost", []): #pragma: no cover logg.error(" %s: there no such thing as an ExecReloadPost (ignored)", unit) if conf.getlist("Service", "ExecStopPre", []): #pragma: no cover logg.error(" %s: there no such thing as an ExecStopPre (ignored)", unit) for env_file in conf.getlist("Service", "EnvironmentFile", []): if env_file.startswith("-"): continue if not os.path.isfile(os_path(self._root, env_file)): logg.error(" %s: Failed to load environment files: %s", unit, env_file) errors += 101 return errors def exec_check_service(self, conf, env, exectype = ""): if not conf: return True if not conf.data.has_section("Service"): return True #pragma: no cover haveType = conf.get("Service", "Type", "simple") if haveType in [ "sysv" ]: return True # we don't care about that abspath = 0 notexists = 0 for execs in [ "ExecStartPre", "ExecStart", "ExecStartPost", "ExecStop", "ExecStopPost", "ExecReload" ]: if not execs.startswith(exectype): continue for cmd in conf.getlist("Service", execs, []): check, cmd = checkstatus(cmd) newcmd = self.exec_cmd(cmd, env, conf) if not newcmd: continue exe = newcmd[0] if not exe: continue if exe[0] != "/": logg.error(" Exec is not an absolute path: %s=%s", execs, cmd) abspath += 1 if not os.path.isfile(exe): logg.error(" Exec command does not exist: (%s) %s", execs, exe) notexists += 1 newexe1 = os.path.join("/usr/bin", exe) newexe2 = os.path.join("/bin", exe) if os.path.exists(newexe1): logg.error(" but this does exist: %s %s", " " * len(execs), newexe1) elif os.path.exists(newexe2): logg.error(" but this does exist: %s %s", " " * len(execs), newexe2) if not abspath and not notexists: return True if True: filename = conf.filename() if len(filename) > 45: filename = "..." + filename[-42:] logg.error(" !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") logg.error(" Found %s problems in %s", abspath + notexists, filename) time.sleep(1) if abspath: logg.error(" The SystemD commands must always be absolute paths by definition.") time.sleep(1) logg.error(" Earlier versions of systemctl.py did use a subshell thus using $PATH") time.sleep(1) logg.error(" however newer versions use execve just like the real SystemD daemon") time.sleep(1) logg.error(" so that your docker-only service scripts may start to fail suddenly.") time.sleep(1) if notexists: logg.error(" Now %s executable paths were not found in the current environment.", notexists) time.sleep(1) logg.error(" !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") return False def show_modules(self, *modules): """ [PATTERN]... -- Show properties of one or more units Show properties of one or more units (or the manager itself). If no argument is specified, properties of the manager will be shown. If a unit name is specified, properties of the unit is shown. By default, empty properties are suppressed. Use --all to show those too. To select specific properties to show, use --property=. This command is intended to be used whenever computer-parsable output is required. Use status if you are looking for formatted human-readable output. NOTE: only a subset of properties is implemented """ notfound = [] found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) units += [ module ] found_all = False continue for unit in matched: if unit not in units: units += [ unit ] return self.show_units(units) + notfound # and found_all def show_units(self, units): logg.debug("show --property=%s", self._unit_property) result = [] for unit in units: if result: result += [ "" ] for var, value in self.show_unit_items(unit): if self._unit_property: if self._unit_property != var: continue else: if not value and not self._show_all: continue result += [ "%s=%s" % (var, value) ] return result def show_unit_items(self, unit): """ [UNIT]... -- show properties of a unit. """ logg.info("try read unit %s", unit) conf = self.get_unit_conf(unit) for entry in self.each_unit_items(unit, conf): yield entry def each_unit_items(self, unit, conf): loaded = conf.loaded() if not loaded: loaded = "not-loaded" if "NOT-FOUND" in self.get_description_from(conf): loaded = "not-found" yield "Id", unit yield "Names", unit yield "Description", self.get_description_from(conf) # conf.get("Unit", "Description") yield "PIDFile", self.pid_file_from(conf) # not self.pid_file_from w/o default location yield "MainPID", self.active_pid_from(conf) or "0" # status["MainPID"] or PIDFile-read yield "SubState", self.get_substate_from(conf) # status["SubState"] or notify-result yield "ActiveState", self.get_active_from(conf) # status["ActiveState"] yield "LoadState", loaded yield "UnitFileState", self.enabled_from(conf) yield "TimeoutStartUSec", seconds_to_time(self.get_TimeoutStartSec(conf)) yield "TimeoutStopUSec", seconds_to_time(self.get_TimeoutStopSec(conf)) env_parts = [] for env_part in conf.getlist("Service", "Environment", []): env_parts.append(self.expand_special(env_part, conf)) if env_parts: yield "Environment", " ".join(env_parts) env_files = [] for env_file in conf.getlist("Service", "EnvironmentFile", []): env_files.append(self.expand_special(env_file, conf)) if env_files: yield "EnvironmentFile", " ".join(env_files) # igno_centos = [ "netconsole", "network" ] igno_opensuse = [ "raw", "pppoe", "*.local", "boot.*", "rpmconf*", "purge-kernels.service", "after-local.service", "postfix*" ] igno_ubuntu = [ "mount*", "umount*", "ondemand", "*.local" ] igno_always = [ "network*", "dbus", "systemd-*" ] def _ignored_unit(self, unit, ignore_list): for ignore in ignore_list: if fnmatch.fnmatchcase(unit, ignore): return True # ignore if fnmatch.fnmatchcase(unit, ignore+".service"): return True # ignore return False def system_default_services(self, sysv = "S", default_target = None): """ show the default services This is used internally to know the list of service to be started in 'default' runlevel when the container is started through default initialisation. It will ignore a number of services - use '--all' to show a longer list of services and use '--all --force' if not even a minimal filter shall be used. """ igno = self.igno_centos + self.igno_opensuse + self.igno_ubuntu + self.igno_always if self._show_all: igno = self.igno_always if self._force: igno = [] logg.debug("ignored services filter for default.target:\n\t%s", igno) return self.enabled_default_services(sysv, default_target, igno) def enabled_default_services(self, sysv = "S", default_target = None, igno = []): if self.user_mode(): return self.enabled_default_user_services(sysv, default_target, igno) else: return self.enabled_default_system_services(sysv, default_target, igno) def enabled_default_user_services(self, sysv = "S", default_target = None, igno = []): logg.debug("check for default user services") default_target = default_target or self._default_target default_services = [] for basefolder in self.user_folders(): if not basefolder: continue folder = self.default_enablefolder(default_target, basefolder) if self._root: folder = os_path(self._root, folder) if os.path.isdir(folder): for unit in sorted(os.listdir(folder)): path = os.path.join(folder, unit) if os.path.isdir(path): continue if self._ignored_unit(unit, igno): continue # ignore if unit.endswith(".service"): default_services.append(unit) for basefolder in self.system_folders(): if not basefolder: continue folder = self.default_enablefolder(default_target, basefolder) if self._root: folder = os_path(self._root, folder) if os.path.isdir(folder): for unit in sorted(os.listdir(folder)): path = os.path.join(folder, unit) if os.path.isdir(path): continue if self._ignored_unit(unit, igno): continue # ignore if unit.endswith(".service"): conf = self.load_unit_conf(unit) if self.not_user_conf(conf): pass else: default_services.append(unit) return default_services def enabled_default_system_services(self, sysv = "S", default_target = None, igno = []): logg.debug("check for default system services") default_target = default_target or self._default_target default_services = [] for basefolder in self.system_folders(): if not basefolder: continue folder = self.default_enablefolder(default_target, basefolder) if self._root: folder = os_path(self._root, folder) if os.path.isdir(folder): for unit in sorted(os.listdir(folder)): path = os.path.join(folder, unit) if os.path.isdir(path): continue if self._ignored_unit(unit, igno): continue # ignore if unit.endswith(".service"): default_services.append(unit) for folder in [ self.rc3_root_folder() ]: if not os.path.isdir(folder): logg.warning("non-existant %s", folder) continue for unit in sorted(os.listdir(folder)): path = os.path.join(folder, unit) if os.path.isdir(path): continue m = re.match(sysv+r"\d\d(.*)", unit) if m: service = m.group(1) unit = service + ".service" if self._ignored_unit(unit, igno): continue # ignore default_services.append(unit) return default_services def system_default(self, arg = True): """ start units for default system level This will go through the enabled services in the default 'multi-user.target'. However some services are ignored as being known to be installation garbage from unintended services. Use '--all' so start all of the installed services and with '--all --force' even those services that are otherwise wrong. /// SPECIAL: with --now or --init the init-loop is run and afterwards a system_halt is performed with the enabled services to be stopped.""" self.sysinit_status(SubState = "initializing") logg.info("system default requested - %s", arg) init = self._now or self._init self.start_system_default(init = init) def start_system_default(self, init = False): """ detect the default.target services and start them. When --init is given then the init-loop is run and the services are stopped again by 'systemctl halt'.""" default_target = self._default_target default_services = self.system_default_services("S", default_target) self.sysinit_status(SubState = "starting") self.start_units(default_services) logg.info(" -- system is up") if init: logg.info("init-loop start") sig = self.init_loop_until_stop(default_services) logg.info("init-loop %s", sig) self.stop_system_default() def stop_system_default(self): """ detect the default.target services and stop them. This is commonly run through 'systemctl halt' or at the end of a 'systemctl --init default' loop.""" default_target = self._default_target default_services = self.system_default_services("K", default_target) self.sysinit_status(SubState = "stopping") self.stop_units(default_services) logg.info(" -- system is down") def system_halt(self, arg = True): """ stop units from default system level """ logg.info("system halt requested - %s", arg) self.stop_system_default() try: os.kill(1, signal.SIGQUIT) # exit init-loop on no_more_procs except Exception as e: logg.warning("SIGQUIT to init-loop on PID-1: %s", e) def system_get_default(self): """ get current default run-level""" current = self._default_target folder = os_path(self._root, self.mask_folder()) target = os.path.join(folder, "default.target") if os.path.islink(target): current = os.path.basename(os.readlink(target)) return current def set_default_modules(self, *modules): """ set current default run-level""" if not modules: logg.debug(".. no runlevel given") return (1, "Too few arguments") current = self._default_target folder = os_path(self._root, self.mask_folder()) target = os.path.join(folder, "default.target") if os.path.islink(target): current = os.path.basename(os.readlink(target)) err, msg = 0, "" for module in modules: if module == current: continue targetfile = None for targetname, targetpath in self.each_target_file(): if targetname == module: targetfile = targetpath if not targetfile: err, msg = 3, "No such runlevel %s" % (module) continue # if os.path.islink(target): os.unlink(target) if not os.path.isdir(os.path.dirname(target)): os.makedirs(os.path.dirname(target)) os.symlink(targetfile, target) msg = "Created symlink from %s -> %s" % (target, targetfile) logg.debug("%s", msg) return (err, msg) def init_modules(self, *modules): """ [UNIT*] -- init loop: '--init default' or '--init start UNIT*' The systemctl init service will start the enabled 'default' services, and then wait for any zombies to be reaped. When a SIGINT is received then a clean shutdown of the enabled services is ensured. A Control-C in in interactive mode will also run 'stop' on all the enabled services. // When a UNIT name is given then only that one is started instead of the services in the 'default.target'. Using 'init UNIT' is better than '--init start UNIT' because the UNIT is also stopped cleanly even when it was never enabled in the system. /// SPECIAL: when using --now then only the init-loop is started, with the reap-zombies function and waiting for an interrupt. (and no unit is started/stoppped wether given or not). """ if self._now: return self.init_loop_until_stop([]) if not modules: # like 'systemctl --init default' if self._now or self._show_all: logg.debug("init default --now --all => no_more_procs") self.exit_when_no_more_procs = True return self.start_system_default(init = True) # # otherwise quit when all the init-services have died self.exit_when_no_more_services = True if self._now or self._show_all: logg.debug("init services --now --all => no_more_procs") self.exit_when_no_more_procs = True found_all = True units = [] for module in modules: matched = self.match_units([ module ]) if not matched: logg.error("Unit %s could not be found.", unit_of(module)) found_all = False continue for unit in matched: if unit not in units: units += [ unit ] logg.info("init %s -> start %s", ",".join(modules), ",".join(units)) done = self.start_units(units, init = True) logg.info("-- init is done") return done # and found_all def start_log_files(self, units): self._log_file = {} self._log_hold = {} for unit in units: conf = self.load_unit_conf(unit) if not conf: continue log_path = self.path_journal_log(conf) try: opened = os.open(log_path, os.O_RDONLY | os.O_NONBLOCK) self._log_file[unit] = opened self._log_hold[unit] = b"" except Exception as e: logg.error("can not open %s log: %s\n\t%s", unit, log_path, e) def read_log_files(self, units): BUFSIZE=8192 for unit in units: if unit in self._log_file: new_text = b"" while True: buf = os.read(self._log_file[unit], BUFSIZE) if not buf: break new_text += buf continue text = self._log_hold[unit] + new_text if not text: continue lines = text.split(b"\n") if not text.endswith(b"\n"): self._log_hold[unit] = lines[-1] lines = lines[:-1] for line in lines: prefix = unit.encode("utf-8") content = prefix+b": "+line+b"\n" os.write(1, content) try: os.fsync(1) except: pass def stop_log_files(self, units): for unit in units: try: if unit in self._log_file: if self._log_file[unit]: os.close(self._log_file[unit]) except Exception as e: logg.error("can not close log: %s\n\t%s", unit, e) self._log_file = {} self._log_hold = {} def init_loop_until_stop(self, units): """ this is the init-loop - it checks for any zombies to be reaped and waits for an interrupt. When a SIGTERM /SIGINT /Control-C signal is received then the signal name is returned. Any other signal will just raise an Exception like one would normally expect. As a special the 'systemctl halt' emits SIGQUIT which puts it into no_more_procs mode.""" signal.signal(signal.SIGQUIT, lambda signum, frame: ignore_signals_and_raise_keyboard_interrupt("SIGQUIT")) signal.signal(signal.SIGINT, lambda signum, frame: ignore_signals_and_raise_keyboard_interrupt("SIGINT")) signal.signal(signal.SIGTERM, lambda signum, frame: ignore_signals_and_raise_keyboard_interrupt("SIGTERM")) self.start_log_files(units) self.sysinit_status(ActiveState = "active", SubState = "running") result = None while True: try: time.sleep(InitLoopSleep) self.read_log_files(units) ##### the reaper goes round running = self.system_reap_zombies() # logg.debug("reap zombies - init-loop found %s running procs", running) if self.exit_when_no_more_services: active = False for unit in units: conf = self.load_unit_conf(unit) if not conf: continue if self.is_active_from(conf): active = True if not active: logg.info("no more services - exit init-loop") break if self.exit_when_no_more_procs: if not running: logg.info("no more procs - exit init-loop") break except KeyboardInterrupt as e: if e.args and e.args[0] == "SIGQUIT": # the original systemd puts a coredump on that signal. logg.info("SIGQUIT - switch to no more procs check") self.exit_when_no_more_procs = True continue signal.signal(signal.SIGTERM, signal.SIG_DFL) signal.signal(signal.SIGINT, signal.SIG_DFL) logg.info("interrupted - exit init-loop") result = e.message or "STOPPED" break except Exception as e: logg.info("interrupted - exception %s", e) raise self.sysinit_status(ActiveState = None, SubState = "degraded") self.read_log_files(units) self.read_log_files(units) self.stop_log_files(units) logg.debug("done - init loop") return result def system_reap_zombies(self): """ check to reap children """ selfpid = os.getpid() running = 0 for pid in os.listdir("/proc"): try: pid = int(pid) except: continue if pid == selfpid: continue proc_status = "/proc/%s/status" % pid if os.path.isfile(proc_status): zombie = False ppid = -1 try: for line in open(proc_status): m = re.match(r"State:\s*Z.*", line) if m: zombie = True m = re.match(r"PPid:\s*(\d+)", line) if m: ppid = int(m.group(1)) except IOError as e: logg.warning("%s : %s", proc_status, e) continue if zombie and ppid == os.getpid(): logg.info("reap zombie %s", pid) try: os.waitpid(pid, os.WNOHANG) except OSError as e: logg.warning("reap zombie %s: %s", e.strerror) if os.path.isfile(proc_status): if pid > 1: running += 1 return running # except PID 0 and PID 1 def sysinit_status(self, **status): conf = self.sysinit_target() self.write_status_from(conf, **status) def sysinit_target(self): if not self._sysinit_target: self._sysinit_target = self.default_unit_conf("sysinit.target", "System Initialization") return self._sysinit_target def is_system_running(self): conf = self.sysinit_target() status_file = self.status_file_from(conf) if not os.path.isfile(status_file): time.sleep(EpsilonTime) if not os.path.isfile(status_file): return "offline" status = self.read_status_from(conf) return status.get("SubState", "unknown") def system_is_system_running(self): state = self.is_system_running() if self._quiet: return state in [ "running" ] else: if state in [ "running" ]: return True, state else: return False, state def wait_system(self, target = None): target = target or SysInitTarget for attempt in xrange(int(SysInitWait)): state = self.is_system_running() if "init" in state: if target in [ "sysinit.target", "basic.target" ]: logg.info("system not initialized - wait %s", target) time.sleep(1) continue if "start" in state or "stop" in state: if target in [ "basic.target" ]: logg.info("system not running - wait %s", target) time.sleep(1) continue if "running" not in state: logg.info("system is %s", state) break def pidlist_of(self, pid): try: pid = int(pid) except: return [] pidlist = [ pid ] pids = [ pid ] for depth in xrange(ProcMaxDepth): for pid in os.listdir("/proc"): try: pid = int(pid) except: continue proc_status = "/proc/%s/status" % pid if os.path.isfile(proc_status): try: for line in open(proc_status): if line.startswith("PPid:"): ppid = line[len("PPid:"):].strip() try: ppid = int(ppid) except: continue if ppid in pidlist and pid not in pids: pids += [ pid ] except IOError as e: logg.warning("%s : %s", proc_status, e) continue if len(pids) != len(pidlist): pidlist = pids[:] continue return pids def etc_hosts(self): path = "/etc/hosts" if self._root: return os_path(self._root, path) return path def force_ipv4(self, *args): """ only ipv4 localhost in /etc/hosts """ logg.debug("checking /etc/hosts for '::1 localhost'") lines = [] for line in open(self.etc_hosts()): if "::1" in line: newline = re.sub("\\slocalhost\\s", " ", line) if line != newline: logg.info("/etc/hosts: '%s' => '%s'", line.rstrip(), newline.rstrip()) line = newline lines.append(line) f = open(self.etc_hosts(), "w") for line in lines: f.write(line) f.close() def force_ipv6(self, *args): """ only ipv4 localhost in /etc/hosts """ logg.debug("checking /etc/hosts for '127.0.0.1 localhost'") lines = [] for line in open(self.etc_hosts()): if "127.0.0.1" in line: newline = re.sub("\\slocalhost\\s", " ", line) if line != newline: logg.info("/etc/hosts: '%s' => '%s'", line.rstrip(), newline.rstrip()) line = newline lines.append(line) f = open(self.etc_hosts(), "w") for line in lines: f.write(line) f.close() def show_help(self, *args): """[command] -- show this help """ lines = [] okay = True prog = os.path.basename(sys.argv[0]) if not args: argz = {} for name in dir(self): arg = None if name.startswith("system_"): arg = name[len("system_"):].replace("_","-") if name.startswith("show_"): arg = name[len("show_"):].replace("_","-") if name.endswith("_of_unit"): arg = name[:-len("_of_unit")].replace("_","-") if name.endswith("_modules"): arg = name[:-len("_modules")].replace("_","-") if arg: argz[arg] = name lines.append("%s command [options]..." % prog) lines.append("") lines.append("Commands:") for arg in sorted(argz): name = argz[arg] method = getattr(self, name) doc = "..." doctext = getattr(method, "__doc__") if doctext: doc = doctext elif not self._show_all: continue # pragma: nocover firstline = doc.split("\n")[0] doc_text = firstline.strip() if "--" not in firstline: doc_text = "-- " + doc_text lines.append(" %s %s" % (arg, firstline.strip())) return lines for arg in args: arg = arg.replace("-","_") func1 = getattr(self.__class__, arg+"_modules", None) func2 = getattr(self.__class__, arg+"_of_unit", None) func3 = getattr(self.__class__, "show_"+arg, None) func4 = getattr(self.__class__, "system_"+arg, None) func = func1 or func2 or func3 or func4 if func is None: print("error: no such command '%s'" % arg) okay = False else: doc_text = "..." doc = getattr(func, "__doc__", None) if doc: doc_text = doc.replace("\n","\n\n", 1).strip() if "--" not in doc_text: doc_text = "-- " + doc_text else: logg.debug("__doc__ of %s is none", func_name) if not self._show_all: continue lines.append("%s %s %s" % (prog, arg, doc_text)) if not okay: self.show_help() return False return lines def systemd_version(self): """ the version line for systemd compatibility """ return "systemd %s\n - via systemctl.py %s" % (self._systemd_version, __version__) def systemd_features(self): """ the info line for systemd features """ features1 = "-PAM -AUDIT -SELINUX -IMA -APPARMOR -SMACK" features2 = " +SYSVINIT -UTMP -LIBCRYPTSETUP -GCRYPT -GNUTLS" features3 = " -ACL -XZ -LZ4 -SECCOMP -BLKID -ELFUTILS -KMOD -IDN" return features1+features2+features3 def systems_version(self): return [ self.systemd_version(), self.systemd_features() ] def print_result(result): # logg_info = logg.info # logg_debug = logg.debug def logg_info(*msg): pass def logg_debug(*msg): pass exitcode = 0 if result is None: logg_info("EXEC END None") elif result is True: logg_info("EXEC END True") result = None exitcode = 0 elif result is False: logg_info("EXEC END False") result = None exitcode = 1 elif isinstance(result, tuple) and len(result) == 2: exitcode, status = result logg_info("EXEC END %s '%s'", exitcode, status) if exitcode is True: exitcode = 0 if exitcode is False: exitcode = 1 result = status elif isinstance(result, int): logg_info("EXEC END %s", result) exitcode = result result = None # if result is None: pass elif isinstance(result, string_types): print(result) result1 = result.split("\n")[0][:-20] if result == result1: logg_info("EXEC END '%s'", result) else: logg_info("EXEC END '%s...'", result1) logg_debug(" END '%s'", result) elif isinstance(result, list) or hasattr(result, "next") or hasattr(result, "__next__"): shown = 0 for element in result: if isinstance(element, tuple): print("\t".join([ str(elem) for elem in element] )) else: print(element) shown += 1 logg_info("EXEC END %s items", shown) logg_debug(" END %s", result) elif hasattr(result, "keys"): shown = 0 for key in sorted(result.keys()): element = result[key] if isinstance(element, tuple): print(key,"=","\t".join([ str(elem) for elem in element])) else: print("%s=%s" % (key,element)) shown += 1 logg_info("EXEC END %s items", shown) logg_debug(" END %s", result) else: logg.warning("EXEC END Unknown result type %s", str(type(result))) return exitcode if __name__ == "__main__": import optparse _o = optparse.OptionParser("%prog [options] command [name...]", epilog="use 'help' command for more information") _o.add_option("--version", action="store_true", help="Show package version") _o.add_option("--system", action="store_true", default=False, help="Connect to system manager (default)") # overrides --user _o.add_option("--user", action="store_true", default=_user_mode, help="Connect to user service manager") # _o.add_option("-H", "--host", metavar="[USER@]HOST", # help="Operate on remote host*") # _o.add_option("-M", "--machine", metavar="CONTAINER", # help="Operate on local container*") _o.add_option("-t","--type", metavar="TYPE", dest="unit_type", default=_unit_type, help="List units of a particual type") _o.add_option("--state", metavar="STATE", default=_unit_state, help="List units with particular LOAD or SUB or ACTIVE state") _o.add_option("-p", "--property", metavar="NAME", dest="unit_property", default=_unit_property, help="Show only properties by this name") _o.add_option("-a", "--all", action="store_true", dest="show_all", default=_show_all, help="Show all loaded units/properties, including dead empty ones. To list all units installed on the system, use the 'list-unit-files' command instead") _o.add_option("-l","--full", action="store_true", default=_full, help="Don't ellipsize unit names on output (never ellipsized)") _o.add_option("--reverse", action="store_true", help="Show reverse dependencies with 'list-dependencies' (ignored)") _o.add_option("--job-mode", metavar="MODE", help="Specifiy how to deal with already queued jobs, when queuing a new job (ignored)") _o.add_option("--show-types", action="store_true", help="When showing sockets, explicitly show their type (ignored)") _o.add_option("-i","--ignore-inhibitors", action="store_true", help="When shutting down or sleeping, ignore inhibitors (ignored)") _o.add_option("--kill-who", metavar="WHO", help="Who to send signal to (ignored)") _o.add_option("-s", "--signal", metavar="SIG", help="Which signal to send (ignored)") _o.add_option("--now", action="store_true", default=_now, help="Start or stop unit in addition to enabling or disabling it") _o.add_option("-q","--quiet", action="store_true", default=_quiet, help="Suppress output") _o.add_option("--no-block", action="store_true", default=False, help="Do not wait until operation finished (ignored)") _o.add_option("--no-legend", action="store_true", default=_no_legend, help="Do not print a legend (column headers and hints)") _o.add_option("--no-wall", action="store_true", default=False, help="Don't send wall message before halt/power-off/reboot (ignored)") _o.add_option("--no-reload", action="store_true", help="Don't reload daemon after en-/dis-abling unit files (ignored)") _o.add_option("--no-ask-password", action="store_true", default=_no_ask_password, help="Do not ask for system passwords") # _o.add_option("--global", action="store_true", dest="globally", default=_globally, # help="Enable/disable unit files globally") # for all user logins # _o.add_option("--runtime", action="store_true", # help="Enable unit files only temporarily until next reboot") _o.add_option("--force", action="store_true", default=_force, help="When enabling unit files, override existing symblinks / When shutting down, execute action immediately") _o.add_option("--preset-mode", metavar="TYPE", default=_preset_mode, help="Apply only enable, only disable, or all presets [%default]") _o.add_option("--root", metavar="PATH", default=_root, help="Enable unit files in the specified root directory (used for alternative root prefix)") _o.add_option("-n","--lines", metavar="NUM", help="Number of journal entries to show (ignored)") _o.add_option("-o","--output", metavar="CAT", help="change journal output mode [short, ..., cat] (ignored)") _o.add_option("--plain", action="store_true", help="Print unit dependencies as a list instead of a tree (ignored)") _o.add_option("--no-pager", action="store_true", help="Do not pipe output into pager (ignored)") # _o.add_option("--coverage", metavar="OPTIONLIST", default=COVERAGE, help="..support for coverage (e.g. spawn,oldest,sleep) [%default]") _o.add_option("-e","--extra-vars", "--environment", metavar="NAME=VAL", action="append", default=[], help="..override settings in the syntax of 'Environment='") _o.add_option("-v","--verbose", action="count", default=0, help="..increase debugging information level") _o.add_option("-4","--ipv4", action="store_true", default=False, help="..only keep ipv4 localhost in /etc/hosts") _o.add_option("-6","--ipv6", action="store_true", default=False, help="..only keep ipv6 localhost in /etc/hosts") _o.add_option("-1","--init", action="store_true", default=False, help="..keep running as init-process (default if PID 1)") opt, args = _o.parse_args() logging.basicConfig(level = max(0, logging.FATAL - 10 * opt.verbose)) logg.setLevel(max(0, logging.ERROR - 10 * opt.verbose)) # COVERAGE = opt.coverage if "sleep" in COVERAGE: MinimumTimeoutStartSec = 7 MinimumTimeoutStopSec = 7 if "quick" in COVERAGE: MinimumTimeoutStartSec = 4 MinimumTimeoutStopSec = 4 DefaultTimeoutStartSec = 9 DefaultTimeoutStopSec = 9 _extra_vars = opt.extra_vars _force = opt.force _full = opt.full _no_legend = opt.no_legend _no_ask_password = opt.no_ask_password _now = opt.now _preset_mode = opt.preset_mode _quiet = opt.quiet _root = opt.root _show_all = opt.show_all _unit_state = opt.state _unit_type = opt.unit_type _unit_property = opt.unit_property # being PID 1 (or 0) in a container will imply --init _pid = os.getpid() _init = opt.init or _pid in [ 1, 0 ] _user_mode = opt.user if os.geteuid() and _pid in [ 1, 0 ]: _user_mode = True if opt.system: _user_mode = False # override --user # if _user_mode: systemctl_debug_log = os_path(_root, _var_path(_systemctl_debug_log)) systemctl_extra_log = os_path(_root, _var_path(_systemctl_extra_log)) else: systemctl_debug_log = os_path(_root, _systemctl_debug_log) systemctl_extra_log = os_path(_root, _systemctl_extra_log) if os.access(systemctl_extra_log, os.W_OK): loggfile = logging.FileHandler(systemctl_extra_log) loggfile.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s")) logg.addHandler(loggfile) logg.setLevel(max(0, logging.INFO - 10 * opt.verbose)) if os.access(systemctl_debug_log, os.W_OK): loggfile = logging.FileHandler(systemctl_debug_log) loggfile.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s")) logg.addHandler(loggfile) logg.setLevel(logging.DEBUG) logg.info("EXEC BEGIN %s %s%s%s", os.path.realpath(sys.argv[0]), " ".join(args), _user_mode and " --user" or " --system", _init and " --init" or "", ) # # systemctl = Systemctl() if opt.version: args = [ "version" ] if not args: if _init: args = [ "default" ] else: args = [ "list-units" ] logg.debug("======= systemctl.py " + " ".join(args)) command = args[0] modules = args[1:] if opt.ipv4: systemctl.force_ipv4() elif opt.ipv6: systemctl.force_ipv6() found = False # command NAME if command.startswith("__"): command_name = command[2:] command_func = getattr(systemctl, command_name, None) if callable(command_func) and not found: found = True result = command_func(*modules) command_name = command.replace("-","_").replace(".","_")+"_modules" command_func = getattr(systemctl, command_name, None) if callable(command_func) and not found: systemctl.wait_boot(command_name) found = True result = command_func(*modules) command_name = "show_"+command.replace("-","_").replace(".","_") command_func = getattr(systemctl, command_name, None) if callable(command_func) and not found: systemctl.wait_boot(command_name) found = True result = command_func(*modules) command_name = "system_"+command.replace("-","_").replace(".","_") command_func = getattr(systemctl, command_name, None) if callable(command_func) and not found: systemctl.wait_boot(command_name) found = True result = command_func() command_name = "systems_"+command.replace("-","_").replace(".","_") command_func = getattr(systemctl, command_name, None) if callable(command_func) and not found: systemctl.wait_boot(command_name) found = True result = command_func() if not found: logg.error("Unknown operation %s.", command) sys.exit(1) # sys.exit(print_result(result)) docker-systemctl-replacement-1.4.3424/notes/000077500000000000000000000000001355427602500207035ustar00rootroot00000000000000docker-systemctl-replacement-1.4.3424/notes/HINTS.md000066400000000000000000000203621355427602500221150ustar00rootroot00000000000000# Tricks and Hints ## The --now internals The option "--now" is only used for a very low number of systemctl commands. In the systemctl.py variant it is (ab)used to run an alternative method. ## systemctl.py list-unit-files --now Shows the list of files found on the disk. Those are not parsed and they are not check for an enabled-link. It is just what is there as a first step after scanning for systemd-relevant unit information. ## systemctl.py list-dependencies --now Shows the start-order of services. Use -v debugging to see how the start-rank is computed from the Before/After clauses in the unit files. Note that upon a " operation" the start order is run through in reverse order. The current 'default' target does not filter the list. Instead it only uses Before/After on the units in the 'multi-user.target' but it does not process any of its required units. # Implementation helpers ## docker images --filter https://github.com/moby/moby/blob/10c0af083544460a2ddc2218f37dc24a077f7d90/docs/reference/commandline/images.md#filtering docker images --filter dangling=true || dangling=false docker images --filter label= || label== whereas filter by name is just an argument "docker images " ## docker images --format docker images --format "{{.ID}}: {{.Repository}}\t{{.Size}}" and "{{.Tag}} {{.Digest}} {{.CreatedSince}} {{.CreatedAt}}" ## IPv4 -vs- IPv6 For java / tomcat applications one can add a java-option to use ipv4 -Djava.net.preferIPv4Stack=true For docker-compose 2.1+ one can add a sysctl to disable ipv6. sysctls: - net.ipv6.conf.eth0.disable_ipv6=1 - net.ipv6.conf.all.disable_ipv6=1 And there is an option to disable '::1 localhost' with systemctl.py systemctl.py --ipv4 daemon-reload ## oneshoot services https://www.freedesktop.org/software/systemd/man/systemd.service.html There is a note that after "start unit" the unit is considered to be active. A second "start unit" shall NOT run the execs again. Indirectly it hints to the detail that a "start unit" does not run any execs when "is-active unit" true. See issue #6. ## system specifies https://www.freedesktop.org/software/systemd/man/systemd.unit.html * "%n" Full unit name * "%N" Unescaped full unit name = Same as "%n", but with escaping undone * "%p" Prefix name = For instantiated units, this refers to the string before the "@" character of the unit name. For non-instantiated units, this refers to the name of the unit with the type suffix removed. * "%P" Unescaped prefix name = Same as "%p", but with escaping undone * "%i" Instance name = For instantiated units: this is the string between the "@" character and the suffix of the unit name. * "%I" Unescaped instance name = Same as "%i", but with escaping undone * "%f" Unescaped filename = This is either the unescaped instance name (if applicable) with / prepended (if applicable), or the unescaped prefix name prepended with /. * "%t" Runtime directory = This is either /run (for the system manager) or the path "$XDG_RUNTIME_DIR" resolves to (for user managers). * "%u" User name = This is the name of the user running the service manager instance. In case of the system manager this resolves to "root". * "%U" User UID = This is the numeric UID of the user running the service manager instance. In case of the system manager this resolves to "0". * "%h" User home directory = This is the home directory of the user running the service manager instance. In case of the system manager this resolves to "/root". * "%s" User shell = This is the shell of the user running the service manager instance. In case of the system manager this resolves to "/bin/sh". * "%m" Machine ID = The machine ID of the running system, formatted as string. See machine-id(5) for more information. * "%b" Boot ID = The boot ID of the running system, formatted as string. See random(4) for more information. * "%H" Host name = The hostname of the running system at the point in time the unit configuration is loaded. * "%v" Kernel release = Identical to uname -r output * "%%" Single percent sign = Use "%%" in place of "%" to specify a single percent sign. ## KillMode with SendSIGKILL and SendSIGHUP When grepping through existing *.service files one can see a number of references to those two options * SendSIGKILL=no / postgresql + plymouth-start * SendSIGHUP=yes / getty + console + journal-service For a docker container one can safely exclude the latter but the usage for postgresql makes it an importation option being in use. In both cases the option changes the behaviour of "systemctl kill". https://www.freedesktop.org/software/systemd/man/systemd.kill.html As one can already guess, the default for SendSIGHUP is "no" and it means that after the "KillSignal=" it should immediately send an addtional SIGHUP - which is defined to also be forwarded to the children of a program. One can also guess the default for SendSIGKILL to be "yes". Here it is defined to work only after a timeout - as it shall send a KILL to all remaining processes(!) of a service that are still around. The third signal in use is a SIGTERM which is the default that is sometimes overridden as "KillSignal=SIGHUP". Obviously it does not make sense to mix Kill-SIGHUP and SendSIGHUP and there is no such thing to exist in real service systemd files. Last not least, there is also a "KillMode=control-group" default. Which effectivly means that the specified sequences of Signals (SIGTERM + SIGHUP & SIGKILL) should be sent to all processes. As it is implemented for systemctl.py 1.0 there are some differences in the handling - a "systemctl.py stop" will only execute ExecStop but no implicit "systemctl kill" after that (KillMode=none). And the SIGTERM & SIGKILL will only go the $MAINPID of a service as far as we know about it (KillMode=process). If there is NO ExecStop then there is a fallback from "signal stop" to "signal kill". ## Start Exec-Mode It may be a little known detail from the systemd specs but in reality one should NOT have multiple ExecStart lines in a spec file. (quoting from specs: "Unless Type= is oneshot, exactly one command must be given.") Instead the systemd specification has a number of prefixes for the ExecStart line that modify the behaviour that are somewhat related to the call of fork+execve. https://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart= * a "-" prefix is the most obvious: ignore the return value * a "@" prefix allows to have argv[0] to be not the called program * a "+" prefix makes for a priviledged services process which basically means that User/Group changes should NOT be made (User/Group would affect ExecStart/ExecStop otherwise) * and some unintelligible description of "!" and "!!" prefixes the seem to be alterations of a "+" Moreover, "-" and "@" and "+" may be combined and the prefix characters may occur in any order. Phew! And just for another thing - one might expect that along with ExecStart / ExecStop / ExecReload there also Exec-statements for other frontend commands - including ExecXyPre/ExecXyPost statements for each of them. But this is not the case - specifically there exists NO ExecRestart statement in the systemd specs. Only these are valid: * ExecStartPre * ExecStart * ExecStartPost * ExecReload * ExecStop * ExecStopPost On top of that, if any of the ExecStopPre, ExecStart, ExecStopPost commands fails then you will also see that the original systemd daemon will execute the ExecStopPost lines to cleanup the system. (and it will NOT run the ExecStop lines in that case). In order for ExecStopPost to know the reason why it has been called there are additional environment variables around * $SERVICE_RESULT = "success", "timeout", "exit-code", "signal".. * $EXIT_CODE = "exited", "killed" (the most relevant styles) * $EXIT_STATUS - the exit-code of $MAINPID .. and for the combination of "success" + "exited" this is always 0. That's a bit tricky to follow and "systemctl.py" does not do it. One can assume that there is hardly any programmer to have taken advantage of that - it sounds a bit like wizardry around. As for the hint of "only one ExecStart" - while the script after ExecStart is NOT run in a shell you can still use multiple commands by having them seperated with "{space}{semicolon}{space}". And a stray semicolon may instead be escaped with "\;". Phew! docker-systemctl-replacement-1.4.3424/notes/NOTES.md000066400000000000000000000126241355427602500221220ustar00rootroot00000000000000# Not A Daemon The real systemd environment is based on a central daemon that the "systemctl" command talks to. That one loads the unit files on disk into memory (hence "systemctl daemon-reload"). That one allows some features that "systemctl.py" can not provide at all. ## Restart=on-failure The daemon can watch over its spawned processes (especially with Type=simple) which allows it to automatically restart them if the user wants to. Restart=on-failure Restart=always RestartSec=3 These things will probably never be supported with "systemctl.py". One would need to run some background program somehow. Perhaps it would be an option when running as the init-process but it is currently not a goal for the 1.0 release. ## /var/run/unit.service.pid The daemon will also know the PID of its spawned processes (again with Type=simple) and the list is really just kept in memory. The "systemctl.py" needs to store all operational information however on disk. As such, every process that does not specify a "PIDFile=" in the unit descriptor will nethertheless get a pid file on disk. By convention it uses the filename of the unit descriptor with an attached ".pid" and it is stored in /var/run where most services will store their pid file anyway. So when the is-active attribute is asked then "systemctl.py" will really look into that pid-file and check if the pid is alive. It is that simple. (If the unit file declares a PIDFile then the pid from that one is read - hopefully in a format that one can understand.) ## /var/log/journal/etc.unit.service.log Likewise one can just store the output of the child processes in memory so the output is diverted into /var/log/journal/. That directory would also used by the systemd daemon but in a different way. So far there is no need to adapt the format. Looking at the style of journalctl is yet another thing that one could think about for systemctl.py after the 1.0 release. ## new substate via NOTIFY_SOCKET The current "systemctl.py" script has actually implemented a good portion of the $NOTIFY_SOCKET sd_notify portion. However that one is only used to implement a dynamic waiting time for the process to start. So it creates a /var/run/systemd/notify socket and then it reads from that socket until "READY=1" is seen. As soon as READY is received, the systemctl.py will end and with it the notify-socket is removed. A real notify-socket programm may however send attributes to the running systemd-daemon - and most importantly the substate can give an overview on the health of a system. Since the next notify-socket will only exist on Restart, there is no chance for that with "systemctl.py" # Not A Network The "systemctl.py" script is commonly used in a docker container, hence the project name "docker-systemctl-replacement". For a docker container however all the network setup is managed on the outside of the container - you do have an emulated "eth0" but that's as much as you can do. And you should leave it alone anyway. Quite a number of service unit files however declare dependencies on some network feature. We do have to ignore them all. Likewise any existing unit file relating to network stuff - including some firewall rules - has to be disregarded compeletely. This however follows some heuristic assumptions. Some unit files are skipped.... but may be that will be found to be wrong in the future. Or may be some other unit descriptor needs to be skipped as well. ## Not A Volume Manager The same applies to the management of volumes. That is a thing that the docker daemon will do for you. So any program with such a feature needs to told to not do anything. That is most importantly for applications handling backup operations. Simply assume that no special disk operations can be done in a container. And hopefully the application does not require selinux to be present. ## No Remote SystemD Quite a number of the options for a real "systemctl" are about not connecting to the local system daemon, but pointing to a different machine. Or to a systemd inside a container. Or to a systemd subprocess for specific user. That is not supported by "systemctl.py" and those options have been removed completely from the commandline interface. Any program trying to use these should fail early with our docker-systemctl-replacement program. # Usage Without Docker Whereas the "systemctl.py" is commonly used to be run inside a docker container, it does still have some good points in a normal system - even in parallel with normal "systemctl". That is because "systemctl.py" can check the validity of the unit.service files around. It's internal logic does understand quite a lot of the features that lie therein. And so if the "systemctl.py" does not like a thing that is probably also wrong for a real system process. ## Check with --root=path Most importantly that is used for its own testsuite.py - the first half of the tests will create a tmp.xxxx subdirectory where some generated x.service files are stored. Then the option --root=path is used to inspect their content if they are okay as a whole. Notably, the --root=path feature is also known to the real "systemctl" program. But most people will not likely have used it before - it is meant to configure a chroot-environment with the general systemd daemon. And if you run "systemctl.py" without a --root=path then it will just look at the current system as it is. Which may reveal some interesting things you have never noticed before. docker-systemctl-replacement-1.4.3424/notes/STARTORDER.md000066400000000000000000000152621355427602500227240ustar00rootroot00000000000000# Start/Stop ordering and Dependencies There is a lot of confusion what the various definitions for dependencies After/Wants/Requires will actually do. Note that the list of Requires/etc services comes from multiple Requires/etc text-lines and on each text-line one may define multiple names of dependencies. The entry point for reading should be: https://www.freedesktop.org/software/systemd/man/systemd.unit.html#Requires= ## Dependencies ### Requires It says >>Configures requirement dependencies on other units. If this unit gets activated, the units listed here will be activated as well.<< / >>Note that requirement dependencies do not influence the order in which services are started or stopped. This has to be configured independently with the After= or Before= options.<< So actually, if there is a Requires but no After then the current service and its required dependency service may be started in parallel. Additionally >>If one of the other [Requires] units gets deactivated or its activation fails, this unit will be deactivated.<< / >>Often, it is a better choice to use Wants= instead of Requires= in order to achieve a system that is more robust when dealing with failing services.<< So theoretically, if we have a phpadmin service then if there is a call to "systemctl stop mariadb" then it should automatically do some "systemctl stop phpadmin". However if the mariadb-service fails and it just goes into an inactive-state then the phpadmin service continues to run. >>Use the BindsTo= dependency type together with After= to ensure that a unit may never be in active state without a specific other unit also in active state<< ### Wants We have >>A weaker version of Requires=. Units listed in this option will be started if the configuring unit is. However, if the listed units fail to start or cannot be added to the transaction, this has no impact on the validity of the transaction as a whole. This is the recommended way to hook start-up of one unit to the start-up of another unit.<< So just like "Requires" when "systemctl start phpadmin" then it will also execute "systemctl start mariadb" (possibly in parallel). However mariadb might not be installed or it is "systemctl mask"ed, in which case there is not start-action attempted for it. ### Requisite / BindsTo / PartOf / PropagateReloadTo For Bindsto we have >>When used in conjunction with After= on the same unit the behaviour of BindsTo= is even stronger. In this case, the unit bound to strictly has to be in active state for this unit to also be in active state.<< More importantly, this is completely transitive. For Requisite we have >>Similar to Requires=. However, if the units listed here are not started already, they will not be started and the transaction will fail immediately.<< In other words, "systemctl start phpadmin" will not run "systemctl start mariadb" but when mariadb is inactive then the call to start-phpadmin will fail. And for PartOf we have >>Configures dependencies similar to Requires=, but limited to stopping and restarting of units. When systemd stops or restarts the units listed here, the action is propagated to this unit.<< And for PropagatesReloadTo=, ReloadPropagatedFrom= we have >>Issuing a reload request on a unit will automatically also enqueue a reload request on all units that the reload request shall be propagated to via these two settings.<< ### Conflicts This is an exclusive-mark, so if service A is started then it should stop service B and vice versa. ## systemctl.py As there is no parallism in systemctl we can actually ignore anything that looks like After/Before. ### start logic Without a global dependency graph we would do: GIVEN Requires / BindsTo AND EXISTS. ON "start THEN IF inactive THEN do "start " and wait for result. AND mark a "dependency-start " IF active THEN do not wait. AFTER wait IF active THEN do "start ". BUT... AFTER wait IF inactive THEN fail "start ". GIVEN Requires / BindsTo AND MISSING. ON "start " THEN fail "start ". GIVEN Wants AND EXISTS. ON "start THEN : IF inactive THEN do "start " and wait for result. :: AND mark a "dependency-start " : IF active THEN do not wait. : AFTER wait IF active THEN do "start ". : AFTER wait IF inactive THEN do "start ". GIVEN Wants AND MISSING. ON "start " THEN do "start ". GIVEN Requisite AND EXISTS. ON "start THEN : IF inactive THEN fail "start " : IF active THEN do "start " GIVEN Conflicts AND EXISTS. ON "start " THEN IF active THEN do "stop " ### stop logic There is no explanatation whether a dependency that was indirectly started should also be stopped when the original script is being stopped. Without a global dependency graph we would do: GIVEN Requires / BindsTo / Wants AND EXISTS. : IF active AND "dependency-start " : THEN do "stop " and drop dependency-start mark ### restart logic Without a global dependency graph we would do: Nothing is propagated ### reload logic Without a global dependency graph we would do: GIVEN PropagatesTo AND EXISTS. : IF active THEN do "reload " ## .wants directories The list of Wants may be extended by symlinks in a directory for the service file. So if there is a file 'phpadmin.service' then there MAY be a directory 'phpadmin.service.wants/'. The list of Requires may be extended by symlinks in directory for the service file. So if there is a file 'phpadmin.service' then there MAY be a directory 'phpadmin.service.requires/'. No other types of directory wants/requires are defined. ### After / Before Each service may have a list of dependencies. The order of their start is determined by the After and Before clauses within their files. Theoretically this should be transitive. So if a dependency does have other dependencies then their order should be looked up as well. ### list-dependencies There is a "systemctl list-dependencies" That one will also show dependencies of dependencies. Note that we only check the dependencies in the "[Unit]" section and not in the "[Install]" section. The install section will tell the "enable " command where to set a symlink in a .wants/.requires directory. As such, after an enable-ment it should pop up as a dependency. docker-systemctl-replacement-1.4.3424/requirements.txt000066400000000000000000000001401355427602500230320ustar00rootroot00000000000000docker-mirror-packages-repo -e git+https://github.com/gdraheim/docker-mirror-packages-repo.git docker-systemctl-replacement-1.4.3424/testsuite.py000077500000000000000000036726011355427602500222000ustar00rootroot00000000000000#! /usr/bin/env python """ Testcases for docker-systemctl-replacement functionality """ from __future__ import print_function __copyright__ = "(C) Guido Draheim, licensed under the EUPL""" __version__ = "1.4.3424" ## NOTE: ## The testcases 1000...4999 are using a --root=subdir environment ## The testcases 5000...9999 will start a docker container to work. import subprocess import os.path import time import datetime import unittest import shutil import inspect import types import logging import re import sys from fnmatch import fnmatchcase as fnmatch from glob import glob import json logg = logging.getLogger("TESTING") _python = "/usr/bin/python" _systemctl_py = "files/docker/systemctl.py" COVERAGE = "" # make it an image name = detect_local_system() TODO = False CENTOSVER = { "7.3": "7.3.1611", "7.4": "7.4.1708", "7.5": "7.5.1804" } TESTED_OS = [ "centos:7.3.1611", "centos:7.4.1708", "centos:7.5.1804" ] TESTED_OS += [ "opensuse:42.2", "opensuse:42.3", "opensuse/leap:15.0" ] TESTED_OS += [ "ubuntu:14.04", "ubuntu:16.04", "ubuntu:18.04" ] IMAGES = "localhost:5000/systemctl/testing" IMAGE = "" CENTOS = "centos:7.5.1804" UBUNTU = "ubuntu:18.04" OPENSUSE = "opensuse/leap:15.0" SOMETIME = "" DOCKER_SOCKET = "/var/run/docker.sock" PSQL_TOOL = "/usr/bin/psql" realpath = os.path.realpath _top_list = "ps -eo etime,pid,ppid,args --sort etime,pid" def _recent(top_list): result = [] for line in lines(top_list): if "[kworker" in line: continue if " containerd-shim " in line: continue if " mplayer " in line: continue if " chrome " in line: continue if "/chrome" in line: continue if "/testsuite" in line: continue if _top_list in line: continue # matching on ELAPSED TIME up to 4 minutes if re.search("^\\s*[0]*[0123]:[0-9]*\\s", line): result.append(" "+line) if " ELAPSED " in line: result.append(" "+line) return "\n".join(result) def package_tool(image): if "opensuse" in image: return "zypper" if "ubuntu" in image: return "apt-get" return "yum" def refresh_tool(image): ## https://github.com/openSUSE/docker-containers/issues/64 # {package} rr oss-update" # {package} ar -f http://download.opensuse.org/update/leap/42.3/oss/openSUSE:Leap:42.3:Update.repo" if "opensuse:42.3" in image: return "bash -c 'zypper mr --no-gpgcheck oss-update && zypper refresh'" if "opensuse/leap:15.1" in image: return "bash -c 'zypper mr --no-gpgcheck repo-update && zypper refresh'" if "opensuse" in image: return "zypper refresh" if "ubuntu" in image: return "apt-get update" return "true" def coverage_tool(image = None, python = None): image = image or IMAGE python = python or _python if python.endswith("3"): return "coverage3" return "coverage2" def coverage_run(image = None, python = None): options = " run '--omit=*/six.py,*/extern/*.py,*/unitconfparser.py' --append -- " return coverage_tool(image, python) + options def coverage_package(image = None, python = None): python = python or _python package = "python-coverage" if python.endswith("3"): package = "python3-coverage" logg.info("detect coverage_package for %s => %s", python, package) return package def cover(image = None, python = None): if not COVERAGE: return "" return coverage_run(image, python) def sh____(cmd, shell=True): if isinstance(cmd, basestring): logg.info(": %s", cmd) else: logg.info(": %s", " ".join(["'%s'" % item for item in cmd])) return subprocess.check_call(cmd, shell=shell) def sx____(cmd, shell=True): if isinstance(cmd, basestring): logg.info(": %s", cmd) else: logg.info(": %s", " ".join(["'%s'" % item for item in cmd])) return subprocess.call(cmd, shell=shell) def output(cmd, shell=True): if isinstance(cmd, basestring): logg.info(": %s", cmd) else: logg.info(": %s", " ".join(["'%s'" % item for item in cmd])) run = subprocess.Popen(cmd, shell=shell, stdout=subprocess.PIPE) out, err = run.communicate() return out def output2(cmd, shell=True): if isinstance(cmd, basestring): logg.info(": %s", cmd) else: logg.info(": %s", " ".join(["'%s'" % item for item in cmd])) run = subprocess.Popen(cmd, shell=shell, stdout=subprocess.PIPE) out, err = run.communicate() return out, run.returncode def output3(cmd, shell=True): if isinstance(cmd, basestring): logg.info(": %s", cmd) else: logg.info(": %s", " ".join(["'%s'" % item for item in cmd])) run = subprocess.Popen(cmd, shell=shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = run.communicate() return out, err, run.returncode def _lines(lines): if isinstance(lines, basestring): lines = lines.split("\n") if len(lines) and lines[-1] == "": lines = lines[:-1] return lines def lines(text): lines = [] for line in _lines(text): lines.append(line.rstrip()) return lines def each_grep(pattern, lines): for line in _lines(lines): if re.search(pattern, line.rstrip()): yield line.rstrip() def grep(pattern, lines): return list(each_grep(pattern, lines)) def greps(lines, pattern): return list(each_grep(pattern, lines)) def running(lines): return list(each_non_runuser(each_non_defunct(lines))) def each_non_defunct(lines): for line in _lines(lines): if '' in line: continue yield line def each_non_runuser(lines): for line in _lines(lines): if 'runuser -u' in line: continue yield line def beep(): if os.name == "nt": import winsound frequency = 2500 duration = 1000 winsound.Beep(frequency, duration) else: # using 'sox' on Linux as "\a" is usually disabled # sx___("play -n synth 0.1 tri 1000.0") sx____("play -V1 -q -n -c1 synth 0.1 sine 500") def download(base_url, filename, into): if not os.path.isdir(into): os.makedirs(into) if not os.path.exists(os.path.join(into, filename)): sh____("cd {into} && wget {base_url}/{filename}".format(**locals())) def text_file(filename, content): filedir = os.path.dirname(filename) if not os.path.isdir(filedir): os.makedirs(filedir) f = open(filename, "w") if content.startswith("\n"): x = re.match("(?s)\n( *)", content) indent = x.group(1) for line in content[1:].split("\n"): if line.startswith(indent): line = line[len(indent):] f.write(line+"\n") else: f.write(content) f.close() logg.info("::: made %s", filename) def shell_file(filename, content): text_file(filename, content) os.chmod(filename, 0o775) def copy_file(filename, target): targetdir = os.path.dirname(target) if not os.path.isdir(targetdir): os.makedirs(targetdir) shutil.copyfile(filename, target) def copy_tool(filename, target): copy_file(filename, target) os.chmod(target, 0o755) def get_caller_name(): frame = inspect.currentframe().f_back.f_back return frame.f_code.co_name def get_caller_caller_name(): frame = inspect.currentframe().f_back.f_back.f_back return frame.f_code.co_name def os_path(root, path): if not root: return path if not path: return path while path.startswith(os.path.sep): path = path[1:] return os.path.join(root, path) def os_getlogin(): """ NOT using os.getlogin() """ import pwd return pwd.getpwuid(os.geteuid()).pw_name ############ local mirror helpers ############# def ip_container(name): values = output("docker inspect "+name) values = json.loads(values) if not values or "NetworkSettings" not in values[0]: logg.critical(" docker inspect %s => %s ", name, values) return values[0]["NetworkSettings"]["IPAddress"] def detect_local_system(): """ checks the controller host (a real machine / your laptop) and returns a matching image name for it (docker style) """ distro, version = "", "" if os.path.exists("/etc/os-release"): # rhel:7.4 # VERSION="7.4 (Maipo)" ID="rhel" VERSION_ID="7.4" # centos:7.3 # VERSION="7 (Core)" ID="centos" VERSION_ID="7" # centos:7.4 # VERSION="7 (Core)" ID="centos" VERSION_ID="7" # centos:7.5.1804 # VERSION="7 (Core)" ID="centos" VERSION_ID="7" # opensuse:42.3 # VERSION="42.3" ID=opensuse VERSION_ID="42.3" # opensuse/leap:15.0 # VERSION="15.0" ID="opensuse-leap" VERSION_ID="15.0" # ubuntu:16.04 # VERSION="16.04.3 LTS (Xenial Xerus)" ID=ubuntu VERSION_ID="16.04" # ubuntu:18.04 # VERSION="18.04.1 LTS (Bionic Beaver)" ID=ubuntu VERSION_ID="18.04" for line in open("/etc/os-release"): key, value = "", "" m = re.match('^([_\\w]+)=([^"].*).*', line.strip()) if m: key, value = m.group(1), m.group(2) m = re.match('^([_\\w]+)="([^"]*)".*', line.strip()) if m: key, value = m.group(1), m.group(2) # logg.debug("%s => '%s' '%s'", line.strip(), key, value) if key in ["ID"]: distro = value.replace("-","/") if key in ["VERSION_ID"]: version = value if os.path.exists("/etc/redhat-release"): for line in open("/etc/redhat-release"): m = re.search("release (\\d+[.]\\d+).*", line) if m: distro = "rhel" version = m.group(1) if os.path.exists("/etc/centos-release"): # CentOS Linux release 7.5.1804 (Core) for line in open("/etc/centos-release"): m = re.search("release (\\d+[.]\\d+).*", line) if m: distro = "centos" version = m.group(1) logg.info(":: local_system %s:%s", distro, version) if distro and version: return "%s:%s" % (distro, version) return "" ############ the real testsuite ############## class DockerSystemctlReplacementTest(unittest.TestCase): def caller_testname(self): name = get_caller_caller_name() x1 = name.find("_") if x1 < 0: return name x2 = name.find("_", x1+1) if x2 < 0: return name return name[:x2] def testname(self, suffix = None): name = self.caller_testname() if suffix: return name + "_" + suffix return name def testport(self): testname = self.caller_testname() m = re.match("test_([0123456789]+)", testname) if m: port = int(m.group(1)) if 5000 <= port and port <= 9999: return port seconds = int(str(int(time.time()))[-4:]) return 6000 + (seconds % 2000) def testdir(self, testname = None, keep = False): testname = testname or self.caller_testname() newdir = "tmp/tmp."+testname if os.path.isdir(newdir) and not keep: shutil.rmtree(newdir) if not os.path.isdir(newdir): os.makedirs(newdir) return newdir def rm_testdir(self, testname = None): testname = testname or self.caller_testname() newdir = "tmp/tmp."+testname if os.path.isdir(newdir): shutil.rmtree(newdir) return newdir def makedirs(self, path): if not os.path.isdir(path): os.makedirs(path) def real_folders(self): yield "/etc/systemd/system" yield "/var/run/systemd/system" yield "/usr/lib/systemd/system" yield "/lib/systemd/system" yield "/etc/init.d" yield "/var/run/init.d" yield "/var/run" yield "/etc/sysconfig" yield "/etc/systemd/system/multi-user.target.wants" yield "/usr/bin" def rm_zzfiles(self, root): for folder in self.real_folders(): for item in glob(os_path(root, folder + "/zz*")): if os.path.isdir(item): logg.info("rmtree %s", item) shutil.rmtree(item) else: logg.info("rm %s", item) os.remove(item) for item in glob(os_path(root, folder + "/test_*")): if os.path.isdir(item): logg.info("rmtree %s", item) shutil.rmtree(item) else: logg.info("rm %s", item) os.remove(item) def coverage(self, testname = None): testname = testname or self.caller_testname() newcoverage = ".coverage."+testname time.sleep(1) if os.path.isfile(".coverage"): # shutil.copy(".coverage", newcoverage) f = open(".coverage") text = f.read() f.close() text2 = re.sub(r"(\]\}\})[^{}]*(\]\}\})$", r"\1", text) f = open(newcoverage, "w") f.write(text2) f.close() def root(self, testdir, real = None): if real: return "/" root_folder = os.path.join(testdir, "root") if not os.path.isdir(root_folder): os.makedirs(root_folder) return os.path.abspath(root_folder) def user(self): return os_getlogin() def local_system(self): return detect_local_system() def with_local_ubuntu_mirror(self, ver = None): """ detects a local ubuntu mirror or starts a local docker container with a ubunut repo mirror. It will return the extra_hosts setting to start other docker containers""" rmi = "localhost:5000/mirror-packages" rep = "ubuntu-repo" ver = ver or UBUNTU.split(":")[1] return self.with_local(rmi, rep, ver, "archive.ubuntu.com", "security.ubuntu.com") def with_local_centos_mirror(self, ver = None): """ detects a local centos mirror or starts a local docker container with a centos repo mirror. It will return the setting for extrahosts""" rmi = "localhost:5000/mirror-packages" rep = "centos-repo" ver = ver or CENTOS.split(":")[1] return self.with_local(rmi, rep, ver, "mirrorlist.centos.org") def with_local_opensuse_mirror(self, ver = None): """ detects a local opensuse mirror or starts a local docker container with a centos repo mirror. It will return the extra_hosts setting to start other docker containers""" rmi = "localhost:5000/mirror-packages" rep = "opensuse-repo" ver = ver or OPENSUSE.split(":")[1] return self.with_local(rmi, rep, ver, "download.opensuse.org") def with_local(self, rmi, rep, ver, *hosts): image = "{rmi}/{rep}:{ver}".format(**locals()) container = "{rep}-{ver}".format(**locals()) out, err, ok = output3("docker inspect {image}".format(**locals())) image_found = json.loads(out) if not image_found: return {} out, err, ok = output3("docker inspect {container}".format(**locals())) container_found = json.loads(out) if container_found: container_status = container_found[0]["State"]["Status"] logg.info("::: %s -> %s", container, container_status) latest_image_id = image_found[0]["Id"] container_image_id = container_found[0]["Image"] if latest_image_id != container_image_id or container_status not in ["running"]: cmd = "docker rm --force {container}" sx____(cmd.format(**locals())) container_found = [] if not container_found: cmd = "docker run --rm=true --detach --name {container} {image}" sh____(cmd.format(**locals())) ip_a = ip_container(container) logg.info("::: %s => %s", container, ip_a) return dict(zip(hosts, [ ip_a ] * len(hosts))) def with_local_mirror(self, image): """ attach local centos-repo / opensuse-repo to docker-start enviroment. Effectivly when it is required to 'docker start centos:x.y' then do 'docker start centos-repo:x.y' before and extend the original to 'docker start --add-host mirror...:centos-repo centos:x.y'. """ hosts = {} if image.startswith("centos:"): version = image[len("centos:"):] hosts = self.with_local_centos_mirror(version) if image.startswith("opensuse/leap:"): version = image[len("opensuse/leap:"):] hosts = self.with_local_opensuse_mirror(version) if image.startswith("opensuse:"): version = image[len("opensuse:"):] hosts = self.with_local_opensuse_mirror(version) if image.startswith("ubuntu:"): version = image[len("ubuntu:"):] hosts = self.with_local_ubuntu_mirror(version) return hosts def add_hosts(self, hosts): return " ".join(["--add-host %s:%s" % (host, ip_a) for host, ip_a in hosts.items() ]) # for host, ip_a in mapping.items(): # yield "--add-host {host}:{ip_a}" def local_image(self, image): """ attach local centos-repo / opensuse-repo to docker-start enviroment. Effectivly when it is required to 'docker start centos:x.y' then do 'docker start centos-repo:x.y' before and extend the original to 'docker start --add-host mirror...:centos-repo centos:x.y'. """ if os.environ.get("NONLOCAL",""): return image hosts = self.with_local_mirror(image) if hosts: add_hosts = self.add_hosts(hosts) logg.debug("%s %s", add_hosts, image) return "{add_hosts} {image}".format(**locals()) return image def local_addhosts(self, dockerfile): image = "" for line in open(dockerfile): m = re.match('[Ff][Rr][Oo][Mm] *"([^"]*)"', line) if m: image = m.group(1) break m = re.match("[Ff][Rr][Oo][Mm] *(\w[^ ]*)", line) if m: image = m.group(1).strip() break logg.debug("--\n-- '%s' FROM '%s'", dockerfile, image) if image: hosts = self.with_local_mirror(image) return self.add_hosts(hosts) return "" def drop_container(self, name): cmd = "docker rm --force {name}" sx____(cmd.format(**locals())) def drop_centos(self): self.drop_container("centos") def drop_ubuntu(self): self.drop_container("ubuntu") def drop_opensuse(self): self.drop_container("opensuse") def make_opensuse(self): self.make_container("opensuse", OPENSUSE) def make_ubuntu(self): self.make_container("ubuntu", UBUNTU) def make_centos(self): self.make_container("centos", CENTOS) def make_container(self, name, image): self.drop_container(name) local_image = self.local_image(image) cmd = "docker run --detach --name {name} {local_image} sleep 1000" sh____(cmd.format(**locals())) print(" # " + local_image) print(" docker exec -it "+name+" bash") def begin(self): self._started = time.time() logg.info("[[%s]]", datetime.datetime.fromtimestamp(self._started).strftime("%H:%M:%S")) return "-vv" def end(self, maximum = 99): runtime = time.time() - self._started self.assertLess(runtime, maximum) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def test_1000(self): logg.info("\n CENTOS = '%s'", CENTOS) self.with_local_centos_mirror() def test_1001_systemctl_testfile(self): """ the systemctl.py file to be tested does exist """ testname = self.testname() testdir = self.testdir() root = self.root(testdir) logg.info("...") logg.info("testname %s", testname) logg.info(" testdir %s", testdir) logg.info("and root %s", root) target = "/usr/bin/systemctl" target_folder = os_path(root, os.path.dirname(target)) os.makedirs(target_folder) target_systemctl = os_path(root, target) shutil.copy(_systemctl_py, target_systemctl) self.assertTrue(os.path.isfile(target_systemctl)) self.rm_testdir() self.coverage() def test_1002_systemctl_version(self): systemctl = cover() + _systemctl_py cmd = "{systemctl} --version" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, "systemd 219")) self.assertTrue(greps(out, "via systemctl.py")) self.assertTrue(greps(out, "[+]SYSVINIT")) self.coverage() def real_1002_systemctl_version(self): cmd = "systemctl --version" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"systemd [234]\d\d")) self.assertFalse(greps(out, "via systemctl.py")) self.assertTrue(greps(out, "[+]SYSVINIT")) def test_1003_systemctl_help(self): """ the '--help' option and 'help' command do work """ systemctl = cover() + _systemctl_py cmd = "{systemctl} --help" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, "--root=PATH")) self.assertTrue(greps(out, "--verbose")) self.assertTrue(greps(out, "--init")) self.assertTrue(greps(out, "for more information")) self.assertFalse(greps(out, "reload-or-try-restart")) cmd = "{systemctl} help" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertFalse(greps(out, "--verbose")) self.assertTrue(greps(out, "reload-or-try-restart")) self.coverage() def test_1005_systemctl_help_command(self): """ for any command, 'help command' shows the documentation """ systemctl = cover() + _systemctl_py cmd = "{systemctl} help list-unit-files" out, end = output2(cmd.format(**locals())) logg.info("%s\n%s", cmd, out) self.assertEqual(end, 0) self.assertFalse(greps(out, "for more information")) self.assertTrue(greps(out, "--type=service")) self.coverage() def test_1006_systemctl_help_command_other(self): """ for a non-existant command, 'help command' just shows the list """ systemctl = cover() + _systemctl_py cmd = "{systemctl} help list-foo" out, end = output2(cmd.format(**locals())) logg.info("%s\n%s", cmd, out) self.assertEqual(end, 1) self.assertFalse(greps(out, "for more information")) self.assertFalse(greps(out, "reload-or-try-restart")) self.assertTrue(greps(out, "no such command")) self.coverage() def test_1010_systemctl_daemon_reload(self): """ daemon-reload always succeeds (does nothing) """ systemctl = cover() + _systemctl_py cmd = "{systemctl} daemon-reload" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(lines(out), []) self.assertEqual(end, 0) self.coverage() def real_1011_systemctl_daemon_reload_root_ignored(self): self.test_1011_systemctl_daemon_reload_root_ignored(True) def test_1011_systemctl_daemon_reload_root_ignored(self, real = None): """ daemon-reload always succeeds (does nothing) """ testdir = self.testdir() root = self.root(testdir, real) systemctl = cover() + _systemctl_py + " --root=" + root if real: systemctl = "/usr/bin/systemctl" text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A [Service] ExecStart=/bin/sleep 3 """) # cmd = "{systemctl} daemon-reload" out,end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(lines(out), []) self.assertEqual(end, 0) self.rm_testdir() self.rm_zzfiles(root) self.coverage() def test_1020_systemctl_with_systemctl_log(self): """ when /var/log/systemctl.log exists then print INFO messages into it""" testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root logfile = os_path(root, "/var/log/systemctl.log") text_file(logfile,"") # cmd = "{systemctl} daemon-reload" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(len(greps(open(logfile), " INFO ")), 1) self.assertEqual(len(greps(open(logfile), " DEBUG ")), 0) self.rm_testdir() self.coverage() def test_1021_systemctl_with_systemctl_debug_log(self): """ when /var/log/systemctl.debug.log exists then print DEBUG messages into it""" testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root logfile = os_path(root, "/var/log/systemctl.debug.log") text_file(logfile,"") # cmd = "{systemctl} daemon-reload" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(len(greps(open(logfile), " INFO ")), 1) self.assertEqual(len(greps(open(logfile), " DEBUG ")), 3) self.rm_testdir() self.coverage() def test_1030_systemctl_force_ipv4(self): """ we can force --ipv4 for /etc/hosts """ testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/hosts"),""" 127.0.0.1 localhost localhost4 ::1 localhost localhost6""") hosts = open(os_path(root, "/etc/hosts")).read() self.assertEqual(len(lines(hosts)), 2) self.assertTrue(greps(hosts, "127.0.0.1.*localhost4")) self.assertTrue(greps(hosts, "::1.*localhost6")) self.assertTrue(greps(hosts, "127.0.0.1.*localhost ")) self.assertTrue(greps(hosts, "::1.*localhost ")) # cmd = "{systemctl} --ipv4 daemon-reload" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(lines(out), []) self.assertEqual(end, 0) hosts = open(os_path(root, "/etc/hosts")).read() self.assertEqual(len(lines(hosts)), 2) self.assertTrue(greps(hosts, "127.0.0.1.*localhost4")) self.assertTrue(greps(hosts, "::1.*localhost6")) self.assertTrue(greps(hosts, "127.0.0.1.*localhost ")) self.assertFalse(greps(hosts, "::1.*localhost ")) self.rm_testdir() self.coverage() def test_1031_systemctl_force_ipv6(self): """ we can force --ipv6 for /etc/hosts """ testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/hosts"),""" 127.0.0.1 localhost localhost4 ::1 localhost localhost6""") hosts = open(os_path(root, "/etc/hosts")).read() self.assertEqual(len(lines(hosts)), 2) self.assertTrue(greps(hosts, "127.0.0.1.*localhost4")) self.assertTrue(greps(hosts, "::1.*localhost6")) self.assertTrue(greps(hosts, "127.0.0.1.*localhost ")) self.assertTrue(greps(hosts, "::1.*localhost ")) # cmd = "{systemctl} --ipv6 daemon-reload" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(lines(out), []) self.assertEqual(end, 0) hosts = open(os_path(root, "/etc/hosts")).read() self.assertEqual(len(lines(hosts)), 2) self.assertTrue(greps(hosts, "127.0.0.1.*localhost4")) self.assertTrue(greps(hosts, "::1.*localhost6")) self.assertFalse(greps(hosts, "127.0.0.1.*localhost ")) self.assertTrue(greps(hosts, "::1.*localhost ")) self.rm_testdir() self.coverage() def test_1050_can_create_a_test_service(self): """ check that a unit file can be created for testing """ testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A""") textA = file(os_path(root, "/etc/systemd/system/zza.service")).read() self.assertTrue(greps(textA, "Testing A")) self.assertIn("\nDescription", textA) self.rm_testdir() self.coverage() def test_1051_can_parse_the_service_file(self): """ check that a unit file can be parsed atleast for a description """ testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A""") textA = file(os_path(root, "/etc/systemd/system/zza.service")).read() self.assertTrue(greps(textA, "Testing A")) cmd = "{systemctl} __get_description zza.service" out, end = output2(cmd.format(**locals())) logg.info("%s => \n%s", cmd, out) self.assertEqual(end, 0) self.assertTrue(greps(out, "Testing A")) self.rm_testdir() self.coverage() def test_1052_can_describe_a_pid_file(self): """ check that a unit file can have a specific pdi file """ testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A [Service] PIDFile=/var/run/foo.pid """) textA = file(os_path(root, "/etc/systemd/system/zza.service")).read() self.assertTrue(greps(textA, "Testing A")) self.assertTrue(greps(textA, "PIDFile=")) cmd = "{systemctl} __test_pid_file zza.service" out, end = output2(cmd.format(**locals())) logg.info("%s => \n%s", cmd, out) self.assertEqual(end, 0) self.assertTrue(greps(out, "/var/run/foo.pid")) self.rm_testdir() self.coverage() def test_1053_can_have_default_pid_file_for_simple_service(self): """ check that a unit file has a default pid file for simple services """ testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A [Service] Type=simple """) textA = file(os_path(root, "/etc/systemd/system/zza.service")).read() self.assertTrue(greps(textA, "Testing A")) self.assertFalse(greps(textA, "PIDFile=")) cmd = "{systemctl} __test_pid_file zza.service" out, end = output2(cmd.format(**locals())) logg.info("%s => \n%s", cmd, out) self.assertEqual(end, 0) self.assertTrue(greps(out, "/var/run/zza.service.status")) self.rm_testdir() self.coverage() def test_1055_other_services_use_a_status_file(self): """ check that other unit files may have a default status file """ testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A [Service] Type=oneshot """) textA = file(os_path(root, "/etc/systemd/system/zza.service")).read() self.assertTrue(greps(textA, "Testing A")) self.assertFalse(greps(textA, "PIDFile=")) cmd = "{systemctl} __get_status_file zza.service" out, end = output2(cmd.format(**locals())) logg.info("%s => \n%s", cmd, out) self.assertEqual(end, 0) self.assertTrue(greps(out, "/var/run/zza.service.status")) self.rm_testdir() self.coverage() def test_1060_can_have_shell_like_commments(self): """ check that a unit file can have comment lines with '#' """ testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A [Service] #PIDFile=/var/run/zzfoo.pid """) textA = file(os_path(root, "/etc/systemd/system/zza.service")).read() self.assertTrue(greps(textA, "Testing A")) self.assertTrue(greps(textA, "PIDFile=")) cmd = "{systemctl} __test_pid_file zza.service" out, end = output2(cmd.format(**locals())) logg.info("%s => \n%s", cmd, out) self.assertEqual(end, 0) self.assertFalse(greps(out, "/var/run/zzfoo.pid")) self.assertTrue(greps(out, "/var/run/zza.service.status")) self.rm_testdir() self.coverage() def test_1061_can_have_winini_like_commments(self): """ check that a unit file can have comment lines with ';' """ testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A [Service] ;PIDFile=/var/run/zzfoo.pid """) textA = file(os_path(root, "/etc/systemd/system/zza.service")).read() self.assertTrue(greps(textA, "Testing A")) self.assertTrue(greps(textA, "PIDFile=")) cmd = "{systemctl} __test_pid_file zza.service" out, end = output2(cmd.format(**locals())) logg.info("%s => \n%s", cmd, out) self.assertEqual(end, 0) self.assertFalse(greps(out, "/var/run/zzfoo.pid")) self.assertTrue(greps(out, "/var/run/zza.service.status")) self.rm_testdir() self.coverage() def test_1062_can_have_multi_line_settings_with_linebreak_mark(self): """ check that a unit file can have settings with '\\' at the line end """ testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A \ which is quite special [Service] PIDFile=/var/run/zzfoo.pid """) textA = file(os_path(root, "/etc/systemd/system/zza.service")).read() self.assertTrue(greps(textA, "Testing A")) self.assertTrue(greps(textA, "quite special")) self.assertTrue(greps(textA, "PIDFile=")) cmd = "{systemctl} __get_description zza.service" out, end = output2(cmd.format(**locals())) logg.info("%s => \n%s", cmd, out) self.assertEqual(end, 0) self.assertTrue(greps(out, "Testing A")) self.assertTrue(greps(out, "quite special")) cmd = "{systemctl} __test_pid_file zza.service" out, end = output2(cmd.format(**locals())) logg.info("%s => \n%s", cmd, out) self.assertEqual(end, 0) self.assertTrue(greps(out, "/var/run/zzfoo.pid")) self.rm_testdir() self.coverage() def test_1063_but_a_missing_linebreak_is_a_syntax_error(self): """ check that a unit file can have 'bad ini' lines throwing an exception """ # the original systemd daemon would ignore services with syntax errors testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A which is quite special [Service] PIDFile=/var/run/zzfoo.pid """) textA = file(os_path(root, "/etc/systemd/system/zza.service")).read() self.assertTrue(greps(textA, "Testing A")) self.assertTrue(greps(textA, "quite special")) self.assertTrue(greps(textA, "PIDFile=")) cmd = "{systemctl} __get_description zza.service" out, end = output2(cmd.format(**locals())) logg.info("%s => \n%s", cmd, out) self.assertEqual(end, 0) self.assertFalse(greps(out, "Testing A")) self.assertFalse(greps(out, "quite special")) cmd = "{systemctl} __test_pid_file zza.service" out, end = output2(cmd.format(**locals())) logg.info("%s => \n%s", cmd, out) self.assertEqual(end, 0) self.assertFalse(greps(out, "/var/run/zzfoo.pid")) self.rm_testdir() self.coverage() def test_1070_external_env_files_can_be_parsed(self): """ check that a unit file can have a valid EnvironmentFile for settings """ testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A which is quite special [Service] EnvironmentFile=/etc/sysconfig/zza.conf """) text_file(os_path(root, "/etc/sysconfig/zza.conf"),""" CONF1=a1 CONF2="b2" CONF3='c3' #CONF4=b4 """) cmd = "{systemctl} __read_env_file /etc/sysconfig/zza.conf -vv" out, end = output2(cmd.format(**locals())) logg.info("%s => \n%s", cmd, out) self.assertEqual(end, 0) self.assertTrue(greps(out, "CONF1")) self.assertTrue(greps(out, "CONF2")) self.assertTrue(greps(out, "CONF3")) self.assertFalse(greps(out, "CONF4")) self.assertTrue(greps(out, "a1")) self.assertTrue(greps(out, "b2")) self.assertTrue(greps(out, "c3")) self.assertFalse(greps(out, '"b2"')) self.assertFalse(greps(out, "'c3'")) self.rm_testdir() self.coverage() def test_1080_preset_files_can_be_parsed(self): """ check that preset files do work internally""" testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/etc/systemd/system/zzc.service"),""" [Unit] Description=Testing C [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/etc/systemd/system-preset/our.preset"),""" enable zzb.service disable zzc.service""") # cmd = "{systemctl} __load_preset_files" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"^our.preset")) self.assertEqual(len(lines(out)), 1) # cmd = "{systemctl} __get_preset_of_unit zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # self.assertTrue(greps(out, r"^our.preset")) self.assertEqual(len(lines(out)), 0) # cmd = "{systemctl} __get_preset_of_unit zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"^enable")) self.assertEqual(len(lines(out)), 1) # cmd = "{systemctl} __get_preset_of_unit zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"^disable")) self.assertEqual(len(lines(out)), 1) def test_1090_syntax_errors_are_shown_on_daemon_reload(self): """ check that preset files do work internally""" self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] Type=foo ExecStart=runA ExecReload=runB ExecStop=runC [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/etc/systemd/system/zzc.service"),""" [Unit] Description=Testing C [Service] Type=simple ExecReload=/bin/kill -SIGHUP $MAINPID ExecStop=/bin/kill $MAINPID [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/etc/systemd/system/zzd.service"),""" [Unit] Description=Testing D [Service] Type=forking [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/etc/systemd/system/zzg.service"),""" [Unit] Description=Testing G [Service] Type=foo ExecStart=runA ExecStart=runA2 ExecReload=runB ExecReload=runB2 ExecStop=runC ExecStop=runC2 [Install] WantedBy=multi-user.target""") # cmd = "{systemctl} daemon-reload -vv 2>&1" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"a.service:.* file without .Service. section")) self.assertTrue(greps(out, r"Failed to parse service type, ignoring: foo")) self.assertTrue(greps(out, r"b.service:.* Executable path is not absolute")) self.assertTrue(greps(out, r"c.service: Service has no ExecStart")) self.assertTrue(greps(out, r"d.service: Service lacks both ExecStart and ExecStop")) self.assertTrue(greps(out, r"g.service: there may be only one ExecStart statement")) self.assertTrue(greps(out, r"c.service: the use of /bin/kill is not recommended")) self.end() def real_1090_syntax_errors_are_shown_in_journal_after_try_start(self): """ check that preset files do work internally""" testname = self.testname() root = "" systemctl = "/usr/bin/systemctl" sx____("rm /etc/systemd/system/zz*") text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] Type=foo ExecStart=runA ExecReload=runB ExecStop=runC [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/etc/systemd/system/zzc.service"),""" [Unit] Description=Testing C [Service] Type=simple ExecReload=/bin/kill -SIGHUP $MAINPID ExecStop=/bin/kill $MAINPID [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/etc/systemd/system/zzd.service"),""" [Unit] Description=Testing D [Service] Type=forking [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/etc/systemd/system/zzg.service"),""" [Unit] Description=Testing G [Service] Type=foo ExecStart=runA ExecStart=runA2 ExecReload=runB ExecReload=runB2 ExecStop=runC ExecStop=runC2 [Install] WantedBy=multi-user.target""") # cmd = "{systemctl} daemon-reload 2>&1" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # there is not direct output self.assertFalse(greps(out, r"a.service:.* file without .Service. section")) self.assertFalse(greps(out, r"b.service:.* Executable path is not absolute")) self.assertFalse(greps(out, r"c.service:.* Service has no ExecStart")) self.assertFalse(greps(out, r"d.service:.* Service lacks both ExecStart and ExecStop")) self.assertFalse(greps(out, r"g.service:.* there may be only one ExecStart statement")) self.assertFalse(greps(out, r"g.service:.* there may be only one ExecStop statement")) self.assertFalse(greps(out, r"g.service:.* there may be only one ExecReload statement")) self.assertFalse(greps(out, r"c.service:.* the use of /bin/kill is not recommended")) # but let's try to start the services # cmd = "{systemctl} start zza zzb zzc zzd zzg 2>&1" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) # fails to start self.assertTrue(greps(out, r"failed to load: Invalid argument. See system logs and 'systemctl status zz\w.service' for details.")) cmd = "journalctl -xe --lines=50 2>&1" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertFalse(greps(out, r"a.service:.* file without .Service. section")) # systemctl.py special self.assertTrue(greps(out, r"Failed to parse service type, ignoring: foo")) self.assertTrue(greps(out, r"b.service:.* Executable path is not absolute")) self.assertTrue(greps(out, r"c.service:.* Service has no ExecStart")) self.assertTrue(greps(out, r"d.service:.* Service lacks both ExecStart= and ExecStop=")) self.assertFalse(greps(out, r"g.service:.* there may be only one ExecStart statement")) # systemctl.py special self.assertFalse(greps(out, r"g.service:.* there may be only one ExecStop statement")) # systemctl.py special self.assertFalse(greps(out, r"g.service:.* there may be only one ExecReload statement")) # systemctl.py special self.assertFalse(greps(out, r"c.service:.* the use of /bin/kill is not recommended")) # systemctl.py special sh____("rm /etc/systemd/system/zz*") def test_1099_errors_message_on_dot_include(self): """ check that '.include' is accepted but marked deprecated""" self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" .include /etc/systemd/system/zzb.service [Unit] Description=Testing A""") text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] Type=foo ExecStart=runA ExecReload=runB ExecStop=runC [Install] WantedBy=multi-user.target""") cmd = "{systemctl} status zza.service" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, out, err) # self.assertEqual(end, 0) self.assertTrue(greps(err, r"deprecated")) self.end() def real_1101_get_bad_command(self): self.test_1101_bad_command(True) def test_1101_bad_command(self, real = False): """ check that unknown commands work""" testname = self.testname() testdir = self.testdir() root = self.root(testdir, real) systemctl = cover() + _systemctl_py + " --root=" + root if real: vv, systemctl = "", "/usr/bin/systemctl" self.rm_zzfiles(root) # cmd = "{systemctl} incorrect" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, err, out) self.assertTrue(greps(err, "Unknown operation incorrect.")) self.assertFalse(greps(out, "units listed.")) self.assertEqual(end, 1) self.rm_zzfiles(root) self.rm_testdir() self.coverage() def real_1111_default_command(self): self.test_1111_default_command(True) def test_1111_default_command(self, real = False): """ check that default commands work""" testname = self.testname() testdir = self.testdir() root = self.root(testdir, real) systemctl = cover() + _systemctl_py + " --root=" + root if real: vv, systemctl = "", "/usr/bin/systemctl" self.rm_zzfiles(root) # cmd = "{systemctl}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, err, out) self.assertTrue(greps(out, "units listed.")) self.assertTrue(greps(out, "To show all installed unit files use 'systemctl list-unit-files'.")) self.assertEqual(end, 0) self.rm_zzfiles(root) self.rm_testdir() self.coverage() def real_1201_get_default(self): self.test_1201_get_default(True) def test_1201_get_default(self, real = False): """ check that get-default works""" testname = self.testname() testdir = self.testdir() root = self.root(testdir, real) systemctl = cover() + _systemctl_py + " --root=" + root if real: vv, systemctl = "", "/usr/bin/systemctl" self.rm_zzfiles(root) # cmd = "{systemctl} get-default" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) if real: self.assertTrue(greps(out, "graphical.target")) else: self.assertTrue(greps(out, "multi-user.target")) self.rm_zzfiles(root) self.rm_testdir() self.coverage() def real_1211_set_default(self): self.test_1211_set_default(True) def test_1211_set_default(self, real = False): """ check that set-default works""" testname = self.testname() testdir = self.testdir() root = self.root(testdir, real) systemctl = cover() + _systemctl_py + " --root=" + root if real: vv, systemctl = "", "/usr/bin/systemctl" self.rm_zzfiles(root) # cmd = "{systemctl} get-default" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) if real: old = "graphical.target" self.assertTrue(greps(out, old)) else: old = "multi-user.target" self.assertTrue(greps(out, old)) runlevel = "basic.target" cmd = "{systemctl} set-default {runlevel}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} get-default" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, runlevel)) # <<<<<<<<<< cmd = "{systemctl} set-default {old}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} get-default" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) if real: old = "graphical.target" self.assertTrue(greps(out, old)) else: old = "multi-user.target" self.assertTrue(greps(out, old)) self.rm_zzfiles(root) self.rm_testdir() self.coverage() def test_2001_can_create_test_services(self): """ check that two unit files can be created for testing """ testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B""") textA = file(os_path(root, "/etc/systemd/system/zza.service")).read() textB = file(os_path(root, "/etc/systemd/system/zzb.service")).read() self.assertTrue(greps(textA, "Testing A")) self.assertTrue(greps(textB, "Testing B")) self.assertIn("\nDescription", textA) self.assertIn("\nDescription", textB) self.rm_testdir() self.coverage() def test_2002_list_units(self): """ check that two unit files can be found for 'list-units' """ testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B""") cmd = "{systemctl} list-units" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"zza.service\s+loaded inactive dead\s+.*Testing A")) self.assertTrue(greps(out, r"zzb.service\s+loaded inactive dead\s+.*Testing B")) self.assertIn("loaded units listed.", out) self.assertIn("To show all installed unit files use", out) self.assertEqual(len(lines(out)), 5) cmd = "{systemctl} --no-legend list-units" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"zza.service\s+loaded inactive dead\s+.*Testing A")) self.assertTrue(greps(out, r"zzb.service\s+loaded inactive dead\s+.*Testing B")) self.assertNotIn("loaded units listed.", out) self.assertNotIn("To show all installed unit files use", out) self.assertEqual(len(lines(out)), 2) self.rm_testdir() self.coverage() def test_2003_list_unit_files(self): """ check that two unit service files can be found for 'list-unit-files' """ testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B""") cmd = "{systemctl} --type=service list-unit-files" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"zza.service\s+static")) self.assertTrue(greps(out, r"zzb.service\s+static")) self.assertIn("unit files listed.", out) self.assertEqual(len(lines(out)), 5) cmd = "{systemctl} --no-legend --type=service list-unit-files" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"zza.service\s+static")) self.assertTrue(greps(out, r"zzb.service\s+static")) self.assertNotIn("unit files listed.", out) self.assertEqual(len(lines(out)), 2) self.rm_testdir() self.coverage() def test_2004_list_unit_files_wanted(self): """ check that two unit files can be found for 'list-unit-files' with an enabled status """ testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Install] WantedBy=multi-user.target""") cmd = "{systemctl} --type=service list-unit-files" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"zza.service\s+static")) self.assertTrue(greps(out, r"zzb.service\s+disabled")) self.assertIn("unit files listed.", out) self.assertEqual(len(lines(out)), 5) cmd = "{systemctl} --no-legend --type=service list-unit-files" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"zza.service\s+static")) self.assertTrue(greps(out, r"zzb.service\s+disabled")) self.assertNotIn("unit files listed.", out) self.assertEqual(len(lines(out)), 2) self.rm_testdir() self.coverage() def test_2006_list_unit_files_wanted_and_unknown_type(self): """ check that two unit files can be found for 'list-unit-files' with an enabled status plus handling unkonwn services""" testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Install] WantedBy=multi-user.target""") cmd = "{systemctl} --type=foo list-unit-files" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertIn("0 unit files listed.", out) self.assertEqual(len(lines(out)), 3) self.rm_testdir() self.coverage() def test_2008_list_unit_files_locations(self): """ check that unit files can be found for 'list-unit-files' in different standard locations on disk. """ testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/usr/lib/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/lib/systemd/system/zzc.service"),""" [Unit] Description=Testing C [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/var/run/systemd/system/zzd.service"),""" [Unit] Description=Testing D [Install] WantedBy=multi-user.target""") cmd = "{systemctl} --type=service list-unit-files" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"zza.service\s+disabled")) self.assertTrue(greps(out, r"zzb.service\s+disabled")) self.assertTrue(greps(out, r"zzc.service\s+disabled")) self.assertTrue(greps(out, r"zzd.service\s+disabled")) self.assertIn("4 unit files listed.", out) self.assertEqual(len(lines(out)), 7) # cmd = "{systemctl} enable zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} enable zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} enable zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} enable zzd.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} --type=service list-unit-files" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"zza.service\s+enabled")) self.assertTrue(greps(out, r"zzb.service\s+enabled")) self.assertTrue(greps(out, r"zzc.service\s+enabled")) self.assertTrue(greps(out, r"zzd.service\s+enabled")) self.assertIn("4 unit files listed.", out) self.assertEqual(len(lines(out)), 7) # self.rm_testdir() self.coverage() def test_2010_list_unit_files_locations_user_mode(self): """ check that unit files can be found for 'list-unit-files' in different standard locations on disk for --user mode """ testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/usr/lib/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/lib/systemd/system/zzc.service"),""" [Unit] Description=Testing C [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/var/run/systemd/system/zzd.service"),""" [Unit] Description=Testing D [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/etc/systemd/user/zzu.service"),""" [Unit] Description=Testing U [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/usr/lib/systemd/user/zzv.service"),""" [Unit] Description=Testing V [Install] WantedBy=multi-user.target""") cmd = "{systemctl} --type=service list-unit-files --user" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertFalse(greps(out, r"zza.service\s+disabled")) self.assertFalse(greps(out, r"zzb.service\s+disabled")) self.assertFalse(greps(out, r"zzc.service\s+disabled")) self.assertFalse(greps(out, r"zzd.service\s+disabled")) self.assertTrue(greps(out, r"zzu.service\s+disabled")) self.assertTrue(greps(out, r"zzv.service\s+disabled")) self.assertIn("2 unit files listed.", out) self.assertEqual(len(lines(out)), 5) # cmd = "{systemctl} enable zza.service --user -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 1) cmd = "{systemctl} enable zzb.service --user -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 1) cmd = "{systemctl} enable zzu.service --user" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} enable zzv.service --user" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} --type=service list-unit-files --user" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"zzu.service\s+enabled")) self.assertTrue(greps(out, r"zzv.service\s+enabled")) self.assertIn("2 unit files listed.", out) self.assertEqual(len(lines(out)), 5) # self.rm_testdir() self.coverage() def test_2014_list_unit_files_locations_user_extra(self): """ check that unit files can be found for 'list-unit-files' in different standard locations on disk for --user mode with some system files to be pinned on our user. """ testname = self.testname() testdir = self.testdir() root = self.root(testdir) user = self.user() systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A [Service] User={user} [Install] WantedBy=multi-user.target""".format(**locals())) text_file(os_path(root, "/usr/lib/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Sevice] User={user} [Install] WantedBy=multi-user.target""".format(**locals())) text_file(os_path(root, "/lib/systemd/system/zzc.service"),""" [Unit] Description=Testing C [Sevice] User={user} [Install] WantedBy=multi-user.target""".format(**locals())) text_file(os_path(root, "/var/run/systemd/system/zzd.service"),""" [Unit] Description=Testing D [Sevice] User={user} [Install] WantedBy=multi-user.target""".format(**locals())) text_file(os_path(root, "/etc/systemd/user/zzu.service"),""" [Unit] Description=Testing U [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/usr/lib/systemd/user/zzv.service"),""" [Unit] Description=Testing V [Install] WantedBy=multi-user.target""") cmd = "{systemctl} --type=service list-unit-files --user" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"zza.service\s+disabled")) self.assertFalse(greps(out, r"zzb.service\s+disabled")) self.assertFalse(greps(out, r"zzc.service\s+disabled")) self.assertFalse(greps(out, r"zzd.service\s+disabled")) self.assertTrue(greps(out, r"zzu.service\s+disabled")) self.assertTrue(greps(out, r"zzv.service\s+disabled")) self.assertIn("3 unit files listed.", out) self.assertEqual(len(lines(out)), 6) # cmd = "{systemctl} enable zza.service --user -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} enable zzb.service --user -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 1) cmd = "{systemctl} enable zzu.service --user" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} enable zzv.service --user" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} --type=service list-unit-files --user" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"zza.service\s+enabled")) self.assertTrue(greps(out, r"zzu.service\s+enabled")) self.assertTrue(greps(out, r"zzv.service\s+enabled")) self.assertIn("3 unit files listed.", out) self.assertEqual(len(lines(out)), 6) # logg.info("enabled services for User=%s", user) self.rm_testdir() self.coverage() def test_2043_list_unit_files_common_targets(self): """ check that some unit target files can be found for 'list-unit-files' """ testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B""") cmd = "{systemctl} --no-legend --type=service list-unit-files" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"zza.service\s+static")) self.assertTrue(greps(out, r"zzb.service\s+static")) self.assertFalse(greps(out, r"multi-user.target\s+enabled")) self.assertEqual(len(lines(out)), 2) cmd = "{systemctl} --no-legend --type=target list-unit-files" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertFalse(greps(out, r"zza.service\s+static")) self.assertFalse(greps(out, r"zzb.service\s+static")) self.assertTrue(greps(out, r"multi-user.target\s+enabled")) self.assertGreater(len(lines(out)), 10) num_targets = len(lines(out)) cmd = "{systemctl} --no-legend list-unit-files" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"zza.service\s+static")) self.assertTrue(greps(out, r"zzb.service\s+static")) self.assertTrue(greps(out, r"multi-user.target\s+enabled")) self.assertEqual(len(lines(out)), num_targets + 2) self.rm_testdir() self.coverage() def test_2044_list_unit_files_now(self): """ check that 'list-unit-files --now' presents a special debug list """ testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B""") cmd = "{systemctl} --no-legend --now list-unit-files" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"zza.service\s+SysD\s+.*systemd/system/zza.service")) self.assertTrue(greps(out, r"zzb.service\s+SysD\s+.*systemd/system/zzb.service")) self.assertFalse(greps(out, r"multi-user.target")) self.assertFalse(greps(out, r"enabled")) self.assertEqual(len(lines(out)), 2) self.rm_testdir() self.coverage() def test_2140_show_environment_from_parts(self): """ check that the result of 'environment UNIT' can list the settings from different locations.""" testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/sysconfig/zzb.conf"),""" DEF1='def1' DEF2="def2" DEF3=def3 """) text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] EnvironmentFile=/etc/sysconfig/zzb.conf Environment=DEF5=def5 Environment=DEF6=def6 ExecStart=/usr/bin/printf $DEF1 $DEF2 \ $DEF3 $DEF4 $DEF5 [Install] WantedBy=multi-user.target""") cmd = "{systemctl} environment zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"^DEF1=def1")) self.assertTrue(greps(out, r"^DEF2=def2")) self.assertTrue(greps(out, r"^DEF3=def3")) self.assertFalse(greps(out, r"^DEF4=def4")) self.assertTrue(greps(out, r"^DEF5=def5")) self.assertTrue(greps(out, r"^DEF6=def6")) self.assertFalse(greps(out, r"^DEF7=def7")) a_lines = len(lines(out)) # self.rm_testdir() self.coverage() def real_2147_show_environment_from_some_parts(self): self.test_2147_show_environment_from_some_parts(True) def test_2147_show_environment_from_some_parts(self, real = False): """ check that the result of 'environment UNIT' can list the settings from different locations.""" testname = self.testname() testdir = self.testdir() root = self.root(testdir, real) systemctl = cover() + _systemctl_py + " --root=" + root if real: systemctl = "/usr/bin/systemctl" text_file(os_path(root, "/etc/sysconfig/zzb.conf"),""" DEF1='def1' DEF2="def2" DEF3=def3 """) text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] EnvironmentFile=/etc/sysconfig/zzb.conf EnvironmentFile=-/etc/sysconfig/zz-not-existant.conf Environment=DEF5=def5 Environment=DEF6=def6 ExecStart=/usr/bin/printf $DEF1 $DEF2 \ $DEF3 $DEF4 $DEF5 [Install] WantedBy=multi-user.target""") cmd = "{systemctl} environment zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"^DEF1=def1")) self.assertTrue(greps(out, r"^DEF2=def2")) self.assertTrue(greps(out, r"^DEF3=def3")) self.assertFalse(greps(out, r"^DEF4=def4")) self.assertTrue(greps(out, r"^DEF5=def5")) self.assertTrue(greps(out, r"^DEF6=def6")) self.assertFalse(greps(out, r"^DEF7=def7")) a_lines = len(lines(out)) # self.rm_testdir() self.rm_zzfiles(root) self.coverage() def real_2148_show_environment_from_some_bad_parts(self): self.test_2148_show_environment_from_some_bad_parts(True) def test_2148_show_environment_from_some_bad_parts(self, real = False): """ check that the result of 'environment UNIT' can list the settings from different locations.""" testname = self.testname() testdir = self.testdir() root = self.root(testdir, real) systemctl = cover() + _systemctl_py + " --root=" + root if real: systemctl = "/usr/bin/systemctl" text_file(os_path(root, "/etc/sysconfig/zzb.conf"),""" DEF1='def1' DEF2="def2" DEF3=def3 """) text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] EnvironmentFile=/etc/sysconfig/zzb.conf EnvironmentFile=/etc/sysconfig/zz-not-existant.conf Environment=DEF5=def5 Environment=DEF6=def6 ExecStart=/usr/bin/printf $DEF1 $DEF2 \ $DEF3 $DEF4 $DEF5 [Install] WantedBy=multi-user.target""") cmd = "{systemctl} environment zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"^DEF1=def1")) self.assertTrue(greps(out, r"^DEF2=def2")) self.assertTrue(greps(out, r"^DEF3=def3")) self.assertFalse(greps(out, r"^DEF4=def4")) self.assertTrue(greps(out, r"^DEF5=def5")) self.assertTrue(greps(out, r"^DEF6=def6")) self.assertFalse(greps(out, r"^DEF7=def7")) a_lines = len(lines(out)) # self.rm_testdir() self.rm_zzfiles(root) self.coverage() def test_2150_have_environment_with_multiple_parts(self): """ check that the result of 'environment UNIT' can list the assignements that are crammed into one line.""" # https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Environment= testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/sysconfig/zzb.conf"),""" DEF1='def1' DEF2="def2" DEF3=def3 """) text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] EnvironmentFile=/etc/sysconfig/zzb.conf Environment="VAR1=word1 word2" VAR2=word3 "VAR3=$word 5 6" ExecStart=/usr/bin/printf $DEF1 $DEF2 \ $VAR1 $VAR2 $VAR3 [Install] WantedBy=multi-user.target""") cmd = "{systemctl} environment zzb.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"^DEF1=def1")) self.assertTrue(greps(out, r"^DEF2=def2")) self.assertTrue(greps(out, r"^DEF3=def3")) self.assertTrue(greps(out, r"^VAR1=word1 word2")) self.assertTrue(greps(out, r"^VAR2=word3")) self.assertTrue(greps(out, r"^VAR3=\$word 5 6")) a_lines = len(lines(out)) # self.rm_testdir() self.coverage() def test_2220_show_unit_is_parseable(self): """ check that 'show UNIT' is machine-readable """ testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A""") cmd = "{systemctl} show zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"^Id=")) self.assertTrue(greps(out, r"^Names=")) self.assertTrue(greps(out, r"^Description=")) self.assertTrue(greps(out, r"^MainPID=")) self.assertTrue(greps(out, r"^LoadState=")) self.assertTrue(greps(out, r"^ActiveState=")) self.assertTrue(greps(out, r"^SubState=")) self.assertTrue(greps(out, r"^UnitFileState=")) num_lines = len(lines(out)) # cmd = "{systemctl} --all show zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"^Id=")) self.assertTrue(greps(out, r"^Names=")) self.assertTrue(greps(out, r"^Description=")) self.assertTrue(greps(out, r"^MainPID=")) self.assertTrue(greps(out, r"^LoadState=")) self.assertTrue(greps(out, r"^ActiveState=")) self.assertTrue(greps(out, r"^SubState=")) self.assertTrue(greps(out, r"^UnitFileState=")) self.assertTrue(greps(out, r"^PIDFile=")) self.assertGreater(len(lines(out)), num_lines) # for line in lines(out): m = re.match(r"^\w+=", line) if not m: # found non-machine readable property line self.assertEqual("word=value", line) self.rm_testdir() self.coverage() def test_2221_show_unit_can_be_restricted_to_one_property(self): """ check that 'show UNIT' may return just one value if asked for""" testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A""") cmd = "{systemctl} show zza.service --property=Description" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"^Description=")) self.assertEqual(len(lines(out)), 1) # cmd = "{systemctl} show zza.service --property=Description --all" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"^Description=")) self.assertEqual(len(lines(out)), 1) # cmd = "{systemctl} show zza.service --property=PIDFile" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"^PIDFile=")) self.assertEqual(len(lines(out)), 1) # cmd = "{systemctl} show zza.service --property=PIDFile --all" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"^PIDFile=")) self.assertEqual(len(lines(out)), 1) # self.assertEqual(lines(out), [ "PIDFile=" ]) self.rm_testdir() self.coverage() def test_2225_show_unit_for_multiple_matches(self): """ check that the result of 'show UNIT' for multiple services is concatenated but still machine readable. """ testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Install] WantedBy=multi-user.target""") cmd = "{systemctl} show zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"^Id=")) self.assertTrue(greps(out, r"^Names=")) self.assertTrue(greps(out, r"^Description=")) self.assertTrue(greps(out, r"^MainPID=")) self.assertTrue(greps(out, r"^LoadState=")) self.assertTrue(greps(out, r"^ActiveState=")) self.assertTrue(greps(out, r"^SubState=")) self.assertTrue(greps(out, r"^UnitFileState=")) a_lines = len(lines(out)) # cmd = "{systemctl} show zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"^Id=")) self.assertTrue(greps(out, r"^Names=")) self.assertTrue(greps(out, r"^Description=")) self.assertTrue(greps(out, r"^MainPID=")) self.assertTrue(greps(out, r"^LoadState=")) self.assertTrue(greps(out, r"^ActiveState=")) self.assertTrue(greps(out, r"^SubState=")) self.assertTrue(greps(out, r"^UnitFileState=")) b_lines = len(lines(out)) # cmd = "{systemctl} show zza.service zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"^Id=")) self.assertTrue(greps(out, r"^Names=")) self.assertTrue(greps(out, r"^Description=")) self.assertTrue(greps(out, r"^MainPID=")) self.assertTrue(greps(out, r"^LoadState=")) self.assertTrue(greps(out, r"^ActiveState=")) self.assertTrue(greps(out, r"^SubState=")) self.assertTrue(greps(out, r"^UnitFileState=")) all_lines = len(lines(out)) # self.assertGreater(all_lines, a_lines + b_lines) # for line in lines(out): if not line.strip(): # empty lines are okay now continue m = re.match(r"^\w+=", line) if not m: # found non-machine readable property line self.assertEqual("word=value", line) self.rm_testdir() self.coverage() def test_2227_show_unit_for_oneshot_service(self): """ check that 'show UNIT' is machine-readable """ testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A [Service] Type=oneshot ExecStart=/bin/echo foo ExecStop=/bin/echo bar """) cmd = "{systemctl} show zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"^Id=")) self.assertTrue(greps(out, r"^Names=")) self.assertTrue(greps(out, r"^Description=")) self.assertTrue(greps(out, r"^MainPID=")) self.assertTrue(greps(out, r"^LoadState=")) self.assertTrue(greps(out, r"^ActiveState=")) self.assertTrue(greps(out, r"^SubState=")) self.assertTrue(greps(out, r"^UnitFileState=")) num_lines = len(lines(out)) # cmd = "{systemctl} --all show zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"^Id=")) self.assertTrue(greps(out, r"^Names=")) self.assertTrue(greps(out, r"^Description=")) self.assertTrue(greps(out, r"^MainPID=")) self.assertTrue(greps(out, r"^LoadState=")) self.assertTrue(greps(out, r"^ActiveState=")) self.assertTrue(greps(out, r"^SubState=")) self.assertTrue(greps(out, r"^UnitFileState=static")) self.assertTrue(greps(out, r"^PIDFile=")) self.assertGreater(len(lines(out)), num_lines) # for line in lines(out): m = re.match(r"^\w+=", line) if not m: # found non-machine readable property line self.assertEqual("word=value", line) self.rm_testdir() self.coverage() def test_2230_show_unit_display_parsed_timeouts(self): """ check that 'show UNIT' show parsed timeoutss """ testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A [Service] TimeoutStartSec=29 TimeoutStopSec=60 """) cmd = "{systemctl} show zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) rep = lines(out) self.assertIn("TimeoutStartUSec=29s", rep) self.assertIn("TimeoutStopUSec=1min", rep) ## text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing A [Service] TimeoutStartSec=1m TimeoutStopSec=2min """) cmd = "{systemctl} show zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) rep = lines(out) self.assertIn("TimeoutStartUSec=1min", rep) self.assertIn("TimeoutStopUSec=2min", rep) ## text_file(os_path(root, "/etc/systemd/system/zzc.service"),""" [Unit] Description=Testing C [Service] TimeoutStartSec=1s TimeoutStopSec=2000ms """) cmd = "{systemctl} show zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) rep = lines(out) self.assertIn("TimeoutStartUSec=1s", rep) self.assertIn("TimeoutStopUSec=2s", rep) # self.rm_testdir() self.coverage() ## text_file(os_path(root, "/etc/systemd/system/zzd.service"),""" [Unit] Description=Testing D [Service] TimeoutStartSec=90s TimeoutStopSec=2250ms """) cmd = "{systemctl} show zzd.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) rep = lines(out) self.assertIn("TimeoutStartUSec=1min 30s", rep) self.assertIn("TimeoutStopUSec=2s 250ms", rep) ## text_file(os_path(root, "/etc/systemd/system/zze.service"),""" [Unit] Description=Testing E [Service] TimeoutStartSec=90s 250ms TimeoutStopSec=3m 25ms """) cmd = "{systemctl} show zze.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) rep = lines(out) self.assertIn("TimeoutStartUSec=1min 30s 250ms", rep) self.assertIn("TimeoutStopUSec=3min 25ms", rep) ## text_file(os_path(root, "/etc/systemd/system/zzf.service"),""" [Unit] Description=Testing F [Service] TimeoutStartSec=180 TimeoutStopSec=182 """) cmd = "{systemctl} show zzf.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) rep = lines(out) self.assertIn("TimeoutStartUSec=3min", rep) self.assertIn("TimeoutStopUSec=3min 2s", rep) # self.rm_testdir() self.coverage() def real_2240_show_environment_from_parts(self): self.test_2240_show_environment_from_parts(True) def test_2240_show_environment_from_parts(self, real = False): """ check that the result of 'show -p Environment UNIT' can list the settings from different locations.""" testname = self.testname() testdir = self.testdir() root = self.root(testdir, real) systemctl = cover() + _systemctl_py + " --root=" + root if real: systemctl = "/usr/bin/systemctl" text_file(os_path(root, "/etc/sysconfig/zzb.conf"),""" DEF1='def1' DEF2="def2" DEF3=def3 """) text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] EnvironmentFile=/etc/sysconfig/zzb.conf Environment=DEF5=def5 Environment=DEF6=def6 ExecStart=/usr/bin/printf $DEF1 $DEF2 \ $DEF3 $DEF4 $DEF5 [Install] WantedBy=multi-user.target""") cmd = "{systemctl} show -p Environment zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertFalse(greps(out, r"DEF1=def1")) self.assertFalse(greps(out, r"DEF2=def2")) self.assertFalse(greps(out, r"DEF3=def3")) self.assertFalse(greps(out, r"DEF4=def4")) self.assertTrue(greps(out, r"DEF5=def5")) self.assertTrue(greps(out, r"DEF6=def6")) self.assertFalse(greps(out, r"DEF7=def7")) a_lines = len(lines(out)) # self.rm_testdir() self.rm_zzfiles(root) self.coverage() def real_2250_show_environment_max_depth(self): self.test_2250_show_environment_max_depth(True) def test_2250_show_environment_max_depth(self, real = False): """ check that the result of 'show -p Environment UNIT' can list the settings from different locations.""" testname = self.testname() testdir = self.testdir() root = self.root(testdir, real) systemctl = cover() + _systemctl_py + " --root=" + root if real: systemctl = "/usr/bin/systemctl" text_file(os_path(root, "/etc/sysconfig/zzb.conf"),""" DEF1='def1' DEF2="def2" DEF3=def3 """) text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] EnvironmentFile=/etc/sysconfig/zzb.conf Environment=DEF5=def5 Environment=DEF6=$DEF5 ExecStart=/usr/bin/printf x.$DEF1.$DEF2.$DEF3.$DEF4.$DEF5 [Install] WantedBy=multi-user.target""") cmd = "{systemctl} daemon-reload" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} show -p Environment zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertFalse(greps(out, r"DEF1=def1")) self.assertFalse(greps(out, r"DEF2=def2")) self.assertFalse(greps(out, r"DEF3=def3")) self.assertFalse(greps(out, r"DEF4=def4")) self.assertTrue(greps(out, r"DEF5=def5")) self.assertTrue(greps(out, r"DEF6=[$]DEF5")) self.assertFalse(greps(out, r"DEF7=def7")) a_lines = len(lines(out)) cmd = "{systemctl} show -p EnvironmentFile zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} stop zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} start zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} stop zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} status zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) # self.rm_testdir() self.rm_zzfiles(root) self.coverage() def real_2300_override_environment_extras(self): self.test_2300_override_environment_extras(True) def test_2300_override_environment_extras(self, real = False): """ check that the result of 'show -p Environment UNIT' can list the settings when using override file extras""" testname = self.testname() testdir = self.testdir() root = self.root(testdir, real) systemctl = cover() + _systemctl_py + " --root=" + root if real: systemctl = "/usr/bin/systemctl" text_file(os_path(root, "/etc/sysconfig/zzb.conf"),""" DEF1='def1' DEF2="def2" DEF3=def3 """) text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] EnvironmentFile=/etc/sysconfig/zzb.conf Environment=DEF5=def5 Environment=DEF6=$DEF5 ExecStart=/usr/bin/printf x.$DEF1.$DEF2.$DEF3.$DEF4.$DEF5 [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/etc/systemd/system/zzb.service.d/extra.conf"),""" [Service] Environment=DEF7=def7 Environment=DEF8=$DEF5 ExecStartPre=/usr/bin/printf y.$DEF7.$DEF8 """) cmd = "{systemctl} daemon-reload" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} show -p Environment zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertFalse(greps(out, r"DEF1=def1")) self.assertFalse(greps(out, r"DEF2=def2")) self.assertFalse(greps(out, r"DEF3=def3")) self.assertFalse(greps(out, r"DEF4=def4")) self.assertTrue(greps(out, r"DEF5=def5")) self.assertTrue(greps(out, r"DEF6=[$]DEF5")) self.assertTrue(greps(out, r"DEF7=def7")) self.assertTrue(greps(out, r"DEF8=[$]DEF5")) self.assertFalse(greps(out, r"DEF9=def9")) a_lines = len(lines(out)) cmd = "{systemctl} show -p EnvironmentFile zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} stop zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} start zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} stop zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} status zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) # self.rm_testdir() self.rm_zzfiles(root) self.coverage() def real_2310_override_environment_by_extras(self): self.test_2310_override_environment_by_extras(True) def test_2310_override_environment_by_extras(self, real = False): """ check that the result of 'show -p Environment UNIT' can list the settings when using override file extras""" testname = self.testname() testdir = self.testdir() root = self.root(testdir, real) systemctl = cover() + _systemctl_py + " --root=" + root if real: systemctl = "/usr/bin/systemctl" text_file(os_path(root, "/etc/sysconfig/zzb.conf"),""" DEF1='def1' DEF2="def2" DEF3=def3 """) text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] EnvironmentFile=/etc/sysconfig/zzb.conf Environment=DEF5=def5 Environment=DEF6=$DEF5 ExecStart=/usr/bin/printf x.$DEF1.$DEF2.$DEF3.$DEF4.$DEF5 [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/etc/systemd/system/zzb.service.d/extra.conf"),""" [Service] Environment=DEF5=def7 """) cmd = "{systemctl} daemon-reload" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} show -p Environment zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertFalse(greps(out, r"DEF1=def1")) self.assertFalse(greps(out, r"DEF2=def2")) self.assertFalse(greps(out, r"DEF3=def3")) self.assertFalse(greps(out, r"DEF4=def4")) self.assertTrue(greps(out, r"DEF5=def7")) self.assertTrue(greps(out, r"DEF6=[$]DEF5")) self.assertFalse(greps(out, r"DEF7=def7")) a_lines = len(lines(out)) cmd = "{systemctl} show -p EnvironmentFile zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} stop zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} start zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} stop zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} status zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) # self.rm_testdir() self.rm_zzfiles(root) self.coverage() def real_2320_override_environment_by_usrlib_extras(self): self.test_2320_override_environment_by_usrlib_extras(True) def test_2320_override_environment_by_usrlib_extras(self, real = False): """ check that the result of 'show -p Environment UNIT' can list the settings when using override file extras""" testname = self.testname() testdir = self.testdir() root = self.root(testdir, real) systemctl = cover() + _systemctl_py + " --root=" + root if real: systemctl = "/usr/bin/systemctl" text_file(os_path(root, "/etc/sysconfig/zzb.conf"),""" DEF1='def1' DEF2="def2" DEF3=def3 """) text_file(os_path(root, "/usr/lib/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] EnvironmentFile=/etc/sysconfig/zzb.conf Environment=DEF5=def5 Environment=DEF6=$DEF5 ExecStart=/usr/bin/printf x.$DEF1.$DEF2.$DEF3.$DEF4.$DEF5 [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/usr/lib/systemd/system/zzb.service.d/extra.conf"),""" [Service] Environment=DEF5=def7 """) cmd = "{systemctl} daemon-reload" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} show -p Environment zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertFalse(greps(out, r"DEF1=def1")) self.assertFalse(greps(out, r"DEF2=def2")) self.assertFalse(greps(out, r"DEF3=def3")) self.assertFalse(greps(out, r"DEF4=def4")) self.assertTrue(greps(out, r"DEF5=def7")) self.assertTrue(greps(out, r"DEF6=[$]DEF5")) self.assertFalse(greps(out, r"DEF7=def7")) a_lines = len(lines(out)) cmd = "{systemctl} show -p EnvironmentFile zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} stop zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} start zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} stop zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} status zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) # self.rm_testdir() self.rm_zzfiles(root) self.coverage() def real_2330_override_environment_by_usrlib_etc_extras(self): self.test_2330_override_environment_by_usrlib_etc_extras(True) def test_2330_override_environment_by_usrlib_etc_extras(self, real = False): """ check that the result of 'show -p Environment UNIT' can list the settings when using override file extras""" testname = self.testname() testdir = self.testdir() root = self.root(testdir, real) systemctl = cover() + _systemctl_py + " --root=" + root if real: systemctl = "/usr/bin/systemctl" text_file(os_path(root, "/etc/sysconfig/zzb.conf"),""" DEF1='def1' DEF2="def2" DEF3=def3 """) text_file(os_path(root, "/usr/lib/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] EnvironmentFile=/etc/sysconfig/zzb.conf Environment=DEF5=def5 Environment=DEF6=$DEF5 ExecStart=/usr/bin/printf x.$DEF1.$DEF2.$DEF3.$DEF4.$DEF5 [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/etc/systemd/system/zzb.service.d/extra.conf"),""" [Service] Environment=DEF5=def7 """) cmd = "{systemctl} daemon-reload" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} show -p Environment zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertFalse(greps(out, r"DEF1=def1")) self.assertFalse(greps(out, r"DEF2=def2")) self.assertFalse(greps(out, r"DEF3=def3")) self.assertFalse(greps(out, r"DEF4=def4")) self.assertTrue(greps(out, r"DEF5=def7")) self.assertTrue(greps(out, r"DEF6=[$]DEF5")) self.assertFalse(greps(out, r"DEF7=def7")) a_lines = len(lines(out)) cmd = "{systemctl} show -p EnvironmentFile zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} stop zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} start zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} stop zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} status zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) # self.rm_testdir() self.rm_zzfiles(root) self.coverage() def real_2340_override_environment_by_multiple_extras(self): self.test_2340_override_environment_by_multiple_extras(True) def test_2340_override_environment_by_multiple_extras(self, real = False): """ check that the result of 'show -p Environment UNIT' can list the settings when using override file extras""" testname = self.testname() testdir = self.testdir() root = self.root(testdir, real) systemctl = cover() + _systemctl_py + " --root=" + root if real: systemctl = "/usr/bin/systemctl" text_file(os_path(root, "/etc/sysconfig/zzb.conf"),""" DEF1='def1' DEF2="def2" DEF3=def3 """) text_file(os_path(root, "/usr/lib/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] EnvironmentFile=/etc/sysconfig/zzb.conf Environment=DEF5=def5 Environment=DEF6=$DEF5 ExecStart=/usr/bin/printf x.$DEF1.$DEF2.$DEF3.$DEF4.$DEF5 [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/usr/lib/systemd/system/zzb.service.d/base.conf"),""" [Service] Environment=DEF5=def6 Environment=DEF7=def6 """) text_file(os_path(root, "/etc/systemd/system/zzb.service.d/extra.conf"),""" [Service] Environment=DEF5=def7 """) cmd = "{systemctl} daemon-reload" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} show -p Environment zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertFalse(greps(out, r"DEF1=def1")) self.assertFalse(greps(out, r"DEF2=def2")) self.assertFalse(greps(out, r"DEF3=def3")) self.assertFalse(greps(out, r"DEF4=def4")) self.assertTrue(greps(out, r"DEF5=def7")) self.assertTrue(greps(out, r"DEF6=[$]DEF5")) self.assertTrue(greps(out, r"DEF7=def")) a_lines = len(lines(out)) cmd = "{systemctl} show -p EnvironmentFile zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} stop zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} start zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} stop zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} status zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) # self.rm_testdir() self.rm_zzfiles(root) self.coverage() def real_2350_override_environment_by_multiple_same_extras(self): self.test_2350_override_environment_by_multiple_same_extras(True) def test_2350_override_environment_by_multiple_same_extras(self, real = False): """ check that the result of 'show -p Environment UNIT' can list the settings when using override file extras""" testname = self.testname() testdir = self.testdir() root = self.root(testdir, real) systemctl = cover() + _systemctl_py + " --root=" + root if real: systemctl = "/usr/bin/systemctl" text_file(os_path(root, "/etc/sysconfig/zzb.conf"),""" DEF1='def1' DEF2="def2" DEF3=def3 """) text_file(os_path(root, "/usr/lib/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] EnvironmentFile=/etc/sysconfig/zzb.conf Environment=DEF5=def5 Environment=DEF6=$DEF5 ExecStart=/usr/bin/printf x.$DEF1.$DEF2.$DEF3.$DEF4.$DEF5 [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/usr/lib/systemd/system/zzb.service.d/extra.conf"),""" [Service] Environment=DEF5=def6 Environment=DEF7=def6 """) text_file(os_path(root, "/etc/systemd/system/zzb.service.d/extra.conf"),""" [Service] Environment=DEF5=def7 """) cmd = "{systemctl} daemon-reload" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} show -p Environment zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertFalse(greps(out, r"DEF1=def1")) self.assertFalse(greps(out, r"DEF2=def2")) self.assertFalse(greps(out, r"DEF3=def3")) self.assertFalse(greps(out, r"DEF4=def4")) self.assertTrue(greps(out, r"DEF5=def7")) self.assertTrue(greps(out, r"DEF6=[$]DEF5")) self.assertFalse(greps(out, r"DEF7=def")) # <<< difference to previous!! a_lines = len(lines(out)) cmd = "{systemctl} show -p EnvironmentFile zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} stop zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} start zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} stop zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} status zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) # self.rm_testdir() self.rm_zzfiles(root) self.coverage() def real_2360_override_environment_by_multiple_same_extras(self): self.test_2360_override_environment_by_multiple_same_extras(True) def test_2360_override_environment_by_multiple_same_extras(self, real = False): """ check that the result of 'show -p Environment UNIT' can list the settings when using override file extras""" testname = self.testname() testdir = self.testdir() root = self.root(testdir, real) systemctl = cover() + _systemctl_py + " --root=" + root if real: systemctl = "/usr/bin/systemctl" text_file(os_path(root, "/etc/sysconfig/zzb.conf"),""" DEF1='def1' DEF2="def2" DEF3=def3 """) text_file(os_path(root, "/usr/lib/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] EnvironmentFile=/etc/sysconfig/zzb.conf Environment=DEF5=def5 Environment=DEF6=$DEF5 ExecStart=/usr/bin/printf x.$DEF1.$DEF2.$DEF3.$DEF4.$DEF5 [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/usr/lib/systemd/system/zzb.service.d/extra.conf"),""" [Service] Environment=DEF5=def6 Environment=DEF7=def6 """) text_file(os_path(root, "/etc/systemd/system/zzb.service.d/base.conf"),""" [Service] Environment=DEF5=def7 """) cmd = "{systemctl} daemon-reload" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} show -p Environment zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertFalse(greps(out, r"DEF1=def1")) self.assertFalse(greps(out, r"DEF2=def2")) self.assertFalse(greps(out, r"DEF3=def3")) self.assertFalse(greps(out, r"DEF4=def4")) self.assertTrue(greps(out, r"DEF5=def6")) # <<< difference to (pre)previous self.assertTrue(greps(out, r"DEF6=[$]DEF5")) self.assertTrue(greps(out, r"DEF7=def")) a_lines = len(lines(out)) cmd = "{systemctl} show -p EnvironmentFile zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} stop zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} start zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} stop zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} status zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) # self.rm_testdir() self.rm_zzfiles(root) self.coverage() def real_2390_override_environment_with_zero_out_extras(self): self.test_2390_override_environment_with_zero_out_extras(True) def test_2390_override_environment_with_zero_out_extras(self, real = False): """ check that the result of 'show -p Environment UNIT' can list the settings when using override file extras""" testname = self.testname() testdir = self.testdir() root = self.root(testdir, real) systemctl = cover() + _systemctl_py + " --root=" + root if real: systemctl = "/usr/bin/systemctl" text_file(os_path(root, "/etc/sysconfig/zzb.conf"),""" DEF1='def1' DEF2="def2" DEF3=def3 """) text_file(os_path(root, "/usr/lib/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] EnvironmentFile=/etc/sysconfig/zzb.conf Environment=DEF5=def5 Environment=DEF6=$DEF5 ExecStart=/usr/bin/printf x.$DEF1.$DEF2.$DEF3.$DEF4.$DEF5 [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/usr/lib/systemd/system/zzb.service.d/extra.conf"),""" [Service] Environment= Environment=DEF5=def6 Environment=DEF7=def6 """) text_file(os_path(root, "/etc/systemd/system/zzb.service.d/base.conf"),""" [Service] Environment=DEF5=def7 Environment=DEF8=def8 """) cmd = "{systemctl} daemon-reload" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} show -p Environment zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertFalse(greps(out, r"DEF1=def1")) self.assertFalse(greps(out, r"DEF2=def2")) self.assertFalse(greps(out, r"DEF3=def3")) self.assertFalse(greps(out, r"DEF4=def4")) self.assertTrue(greps(out, r"DEF5=def6")) self.assertFalse(greps(out, r"DEF6=")) # <<< zero-out self.assertTrue(greps(out, r"DEF7=def")) self.assertFalse(greps(out, r"DEF8=")) # <<< zero-out a_lines = len(lines(out)) cmd = "{systemctl} show -p EnvironmentFile zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} stop zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} start zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} stop zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} status zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) # self.rm_testdir() self.rm_zzfiles(root) self.coverage() def test_2610_show_unit_not_found(self): """ check when 'show UNIT' not found """ testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A [Service] TimeoutStartSec=29 TimeoutStopSec=60 """) cmd = "{systemctl} show zz-not-existing.service" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, err, out) self.assertEqual(end, 0) rep = lines(out) self.assertIn("LoadState=not-found", rep) self.assertIn("ActiveState=inactive", rep) self.assertIn("SubState=dead", rep) self.assertIn("Id=zz-not-existing.service", rep) ## def test_2612_show_unit_property_not_found(self): """ check when 'show UNIT' not found """ testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A [Service] TimeoutStartSec=29 TimeoutStopSec=60 """) cmd = "{systemctl} show -p WeirdOption zza.service" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, err, out) self.assertEqual(end, 0) self.assertEqual(len(out.strip()), 0) ## def test_2900_class_UnitConfParser(self): """ using systemctl.py as a helper library for the UnitConfParser functions.""" python_exe = _python testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl_py_dir = os.path.dirname(realpath(_systemctl_py)) unitconfparser_py = os_path(root, "/usr/bin/unitconfparser.py") service_file = os_path(root, "/etc/systemd/system/zzb.service") defaults = {"a1": "default1"} shell_file(unitconfparser_py,""" #! {python_exe} from __future__ import print_function import sys sys.path += [ "{systemctl_py_dir}" ] import systemctl data = systemctl.UnitConfParser({defaults}) conf = systemctl.SystemctlConf(data) print("DEFAULTS", conf.data.defaults()) print("FILENAME", conf.filename()) data.read(sys.argv[1]) print("filename=", conf.filename()) print("sections=", conf.data.sections()) print("has.Foo.Bar=", conf.data.has_option("Foo", "Bar")) print("has.Unit.Foo=", conf.data.has_option("Unit", "Foo")) try: conf.data.get("Foo", "Bar") except Exception as e: print("get.Foo.Bar:", str(e)) try: conf.data.get("Unit", "Foo") except Exception as e: print("get.Unit.Foo:", str(e)) try: conf.data.getlist("Foo", "Bar") except Exception as e: print("getlist.Foo.Bar:", str(e)) try: conf.data.getlist("Unit", "Foo") except Exception as e: print("getlist.Unit.Foo:", str(e)) print("get.none.Foo.Bar=", conf.data.get("Foo", "Bar", allow_no_value = True)) print("get.none.Unit.Foo=", conf.data.get("Unit", "Foo", allow_no_value = True)) print("getlist.none.Foo.Bar=", conf.data.getlist("Foo", "Bar", allow_no_value = True)) print("getlist.none.Unit.Foo=", conf.data.getlist("Unit", "Foo", allow_no_value = True)) print("get.defs.Foo.Bar=", conf.data.get("Foo", "Bar", "def1")) print("get.defs.Unit.Foo=", conf.data.get("Unit", "Foo", "def2")) print("getlist.defs.Foo.Bar=", conf.data.getlist("Foo", "Bar", ["def3"])) print("getlist.defs.Unit.Foo=", conf.data.getlist("Unit", "Foo", ["def4"])) data.set("Unit", "After", "network.target") print("getlist.unit.after1=", conf.data.getlist("Unit", "After")) print("getitem.unit.after1=", conf.data.get("Unit", "After")) data.set("Unit", "After", "postgres.service") print("getlist.unit.after2=", conf.data.getlist("Unit", "After")) print("getitem.unit.after2=", conf.data.get("Unit", "After")) data.set("Unit", "After", None) print("getlist.unit.after0=", conf.data.getlist("Unit", "After")) print("getitem.unit.after0=", conf.data.get("Unit", "After", allow_no_value = True)) print("getlist.environment=", conf.data.getlist("Service", "Environment")) print("get.environment=", conf.data.get("Service", "Environment")) print("get.execstart=", conf.data.get("Service", "ExecStart")) """.format(**locals())) text_file(service_file,""" [Unit] Description=Testing B [Service] EnvironmentFile=/etc/sysconfig/zzb.conf Environment=DEF5=def5 Environment=DEF6=def6 ExecStart=/usr/bin/printf $DEF1 $DEF2 \\ $DEF3 $DEF4 $DEF5 \\ $DEF6 $DEF7 [Install] WantedBy=multi-user.target""") testrun = cover() + unitconfparser_py cmd = "{testrun} {service_file}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, "DEFAULTS {'a1': 'default1'}")) self.assertTrue(greps(out, "FILENAME None")) self.assertTrue(greps(out, "filename= .*"+service_file)) self.assertTrue(greps(out, "sections= \\['Unit', 'Service', 'Install'\\]")) self.assertTrue(greps(out, "has.Foo.Bar= False")) self.assertTrue(greps(out, "has.Unit.Foo= False")) self.assertTrue(greps(out, "get.Foo.Bar: section Foo does not exist")) self.assertTrue(greps(out, "get.Unit.Foo: option Foo in Unit does not exist")) self.assertTrue(greps(out, "getlist.Foo.Bar: section Foo does not exist")) self.assertTrue(greps(out, "getlist.Unit.Foo: option Foo in Unit does not exist")) self.assertTrue(greps(out, "get.none.Foo.Bar= None")) self.assertTrue(greps(out, "get.none.Unit.Foo= None")) self.assertTrue(greps(out, "getlist.none.Foo.Bar= \\[\\]")) self.assertTrue(greps(out, "getlist.none.Unit.Foo= \\[\\]")) self.assertTrue(greps(out, "get.defs.Foo.Bar= def1")) self.assertTrue(greps(out, "get.defs.Unit.Foo= def2")) self.assertTrue(greps(out, "getlist.defs.Foo.Bar= \\['def3'\\]")) self.assertTrue(greps(out, "getlist.defs.Unit.Foo= \\['def4'\\]")) self.assertTrue(greps(out, "getlist.unit.after1= \\['network.target'\\]")) self.assertTrue(greps(out, "getlist.unit.after2= \\['network.target', 'postgres.service'\\]")) self.assertTrue(greps(out, "getlist.unit.after0= \\[\\]")) self.assertTrue(greps(out, "getitem.unit.after1= network.target")) self.assertTrue(greps(out, "getitem.unit.after2= network.target")) self.assertTrue(greps(out, "getitem.unit.after0= None")) self.assertTrue(greps(out, "getlist.environment= \\['DEF5=def5', 'DEF6=def6'\\]")) self.assertTrue(greps(out, "get.environment= DEF5=def5")) self.assertTrue(greps(out, "get.execstart= /usr/bin/printf \\$DEF1 \\$DEF2 \\\\$")) self.assertTrue(greps(out, " \\$DEF3 \\$DEF4 \\$DEF5")) self.assertTrue(greps(out, " \\$DEF6 \\$DEF7")) # self.rm_testdir() self.coverage() def real_3002_enable_service_creates_a_symlink(self): self.test_3002_enable_service_creates_a_symlink(True) def test_3002_enable_service_creates_a_symlink(self, real = False): """ check that a service can be enabled """ self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir, real) systemctl = cover() + _systemctl_py + " --root=" + root if real: vv, systemctl = "", "/usr/bin/systemctl" self.rm_zzfiles(root) # text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] ExecStart=/bin/sleep 2 [Install] WantedBy=multi-user.target""") cmd = "{systemctl} daemon-reload" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} enable zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) enabled_file = os_path(root, "/etc/systemd/system/multi-user.target.wants/zzb.service") self.assertTrue(os.path.islink(enabled_file)) textB = file(enabled_file).read() self.assertTrue(greps(textB, "Testing B")) self.assertIn("\nDescription", textB) self.rm_zzfiles(root) self.rm_testdir() self.coverage() self.end() def real_3003_disable_service_removes_the_symlink(self): self.test_3003_disable_service_removes_the_symlink(True) def test_3003_disable_service_removes_the_symlink(self, real = False): """ check that a service can be enabled and disabled """ self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir, real) systemctl = cover() + _systemctl_py + " --root=" + root if real: vv, systemctl = "", "/usr/bin/systemctl" self.rm_zzfiles(root) # text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] ExecStart=/bin/sleep 2 [Install] WantedBy=multi-user.target""") cmd = "{systemctl} daemon-reload" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} enable zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) enabled_file = os_path(root, "/etc/systemd/system/multi-user.target.wants/zzb.service") self.assertTrue(os.path.islink(enabled_file)) textB = file(enabled_file).read() self.assertTrue(greps(textB, "Testing B")) self.assertIn("\nDescription", textB) # cmd = "{systemctl} enable zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) enabled_file = os_path(root, "/etc/systemd/system/multi-user.target.wants/zzb.service") self.assertTrue(os.path.islink(enabled_file)) # cmd = "{systemctl} enable zz-other.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) enabled_file = os_path(root, "/etc/systemd/system/multi-user.target.wants/zz-other.service") self.assertFalse(os.path.islink(enabled_file)) # cmd = "{systemctl} disable zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) enabled_file = os_path(root, "/etc/systemd/system/multi-user.target.wants/zzb.service") self.assertFalse(os.path.exists(enabled_file)) # cmd = "{systemctl} disable zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) enabled_file = os_path(root, "/etc/systemd/system/multi-user.target.wants/zzb.service") self.assertFalse(os.path.exists(enabled_file)) # cmd = "{systemctl} disable zz-other.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) # self.rm_zzfiles(root) self.rm_testdir() self.coverage() self.end() def real_3004_list_unit_files_when_enabled(self): self.test_3004_list_unit_files_when_enabled(True) def test_3004_list_unit_files_when_enabled(self, real = False): """ check that two unit files can be found for 'list-unit-files' with an enabled status """ self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir, real) systemctl = cover() + _systemctl_py + " --root=" + root if real: vv, systemctl = "", "/usr/bin/systemctl" self.rm_zzfiles(root) # text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] ExecStart=/bin/sleep 2 [Install] WantedBy=multi-user.target""") cmd = "{systemctl} daemon-reload" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} --no-legend --type=service list-unit-files" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"zza.service\s+static")) self.assertTrue(greps(out, r"zzb.service\s+disabled")) self.assertEqual(len(greps(out, "^zz")), 2) # cmd = "{systemctl} --no-legend enable zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) enabled_file = os_path(root, "/etc/systemd/system/multi-user.target.wants/zzb.service") self.assertTrue(os.path.islink(enabled_file)) # cmd = "{systemctl} --no-legend --type=service list-unit-files" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"zza.service\s+static")) self.assertTrue(greps(out, r"zzb.service\s+enabled")) self.assertEqual(len(greps(out, "^zz")), 2) # cmd = "{systemctl} --no-legend disable zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) enabled_file = os_path(root, "/etc/systemd/system/multi-user.target.wants/zzb.service") self.assertFalse(os.path.exists(enabled_file)) # cmd = "{systemctl} --no-legend --type=service list-unit-files" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"zza.service\s+static")) self.assertTrue(greps(out, r"zzb.service\s+disabled")) self.assertEqual(len(greps(out, "^zz")), 2) # self.rm_zzfiles(root) self.rm_testdir() self.coverage() self.end() def real_3005_is_enabled_result_when_enabled(self): self.test_3005_is_enabled_result_when_enabled(True) def test_3005_is_enabled_result_when_enabled(self, real = None): """ check that 'is-enabled' reports correctly for enabled/disabled """ self.begin() vv = "-vv" testname = self.testname() testdir = self.testdir() root = self.root(testdir, real) systemctl = cover() + _systemctl_py + " --root=" + root if real: vv, systemctl = "", "/usr/bin/systemctl" self.rm_zzfiles(root) # text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] ExecStart=/bin/sleep 2 [Install] WantedBy=multi-user.target""") # cmd = "{systemctl} daemon-reload" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} is-enabled zza.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"^static")) self.assertEqual(len(lines(out)), 1) cmd = "{systemctl} is-enabled zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 1) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) # cmd = "{systemctl} --no-legend enable zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} is-enabled zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"^enabled")) self.assertEqual(len(lines(out)), 1) # cmd = "{systemctl} --no-legend disable zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} is-enabled zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 1) # self.rm_zzfiles(root) self.rm_testdir() self.coverage() self.end() def test_3006_is_enabled_is_true_when_any_is_enabled(self): """ check that 'is-enabled' reports correctly for enabled/disabled """ self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] ExecStart=/bin/sleep 2 [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/etc/systemd/system/zzc.service"),""" [Unit] Description=Testing C [Service] ExecStart=/bin/sleep 2 [Install] WantedBy=multi-user.target""") # cmd = "{systemctl} is-enabled zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"^static")) self.assertEqual(len(lines(out)), 1) cmd = "{systemctl} is-enabled zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 1) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) cmd = "{systemctl} is-enabled zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 1) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) cmd = "{systemctl} is-enabled zzb.service zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 1) self.assertTrue(greps(out, r"^disabled")) self.assertFalse(greps(out, r"^enabled")) self.assertEqual(len(lines(out)), 2) cmd = "{systemctl} is-enabled zza.service zzb.service zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"^disabled")) self.assertFalse(greps(out, r"^enabled")) self.assertEqual(len(lines(out)), 3) # cmd = "{systemctl} --no-legend enable zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} is-enabled zzb.service zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"^disabled")) self.assertTrue(greps(out, r"^enabled")) self.assertEqual(len(lines(out)), 2) # cmd = "{systemctl} is-enabled zzb.service zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"^static")) self.assertTrue(greps(out, r"^enabled")) self.assertEqual(len(lines(out)), 2) # cmd = "{systemctl} is-enabled zzc.service zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"^static")) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 2) # self.rm_testdir() self.coverage() self.end() def test_3008_is_enabled_for_nonexistant_service(self): """ check that 'is-enabled' reports correctly for non-existant services """ self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zzc.service"),""" [Unit] Description=Testing C [Service] ExecStart=/bin/sleep 2 [Install] WantedBy=multi-user.target""") # cmd = "{systemctl} is-enabled zz-not-existing.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 1) self.assertFalse(greps(out, r"^static")) self.assertEqual(len(lines(out)), 0) cmd = "{systemctl} is-enabled zz-not-existing-service.service zzc.service" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(out, r"^disabled")) self.assertFalse(greps(out, r"^enabled")) self.assertEqual(len(lines(out)), 1) self.assertTrue(greps(err, "Unit zz-not-existing-service.service could not be found.")) # cmd = "{systemctl} --no-legend enable zz-not-existing-service.service" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zz-not-existing-service.service could not be found.")) # cmd = "{systemctl} --no-legend disable zz-not-existing-service.service" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zz-not-existing-service.service could not be found.")) # self.rm_testdir() self.coverage() self.end() def test_3009_sysv_service_enable(self): """ check that we manage SysV services in a root env with basic enable/disable commands, also being able to check its status.""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") logfile = os_path(root, "/var/log/"+testsleep+".log") bindir = os_path(root, "/usr/bin") os.makedirs(os_path(root, "/var/run")) text_file(logfile, "created\n") begin = "{" ; end = "}" shell_file(os_path(testdir, "xxx.init"), """ #! /bin/bash ### BEGIN INIT INFO # Required-Start: $local_fs $remote_fs $syslog $network # Required-Stop: $local_fs $remote_fs $syslog $network # Default-Start: 3 5 # Default-Stop: 0 1 2 6 # Short-Description: Testing Z # Description: Allows for SysV testing ### END INIT INFO """) shell_file(os_path(testdir, "zzz.init"), """ #! /bin/bash ### BEGIN INIT INFO # Required-Start: $local_fs $remote_fs $syslog $network # Required-Stop: $local_fs $remote_fs $syslog $network # Default-Start: 3 5 # Default-Stop: 0 1 2 6 # Short-Description: Testing Z # Description: Allows for SysV testing ### END INIT INFO logfile={logfile} sleeptime=111 start() {begin} [ -d /var/run ] || mkdir -p /var/run ({bindir}/{testsleep} $sleeptime 0<&- &>/dev/null & echo $! > {root}/var/run/zzz.init.pid ) & wait %1 # ps -o pid,ppid,args cat "RUNNING `cat {root}/var/run/zzz.init.pid`" {end} stop() {begin} killall {testsleep} {end} case "$1" in start) date "+START.%T" >> $logfile start >> $logfile 2>&1 date "+start.%T" >> $logfile ;; stop) date "+STOP.%T" >> $logfile stop >> $logfile 2>&1 date "+stop.%T" >> $logfile ;; restart) date "+RESTART.%T" >> $logfile stop >> $logfile 2>&1 start >> $logfile 2>&1 date "+.%T" >> $logfile ;; reload) date "+RELOAD.%T" >> $logfile echo "...." >> $logfile 2>&1 date "+reload.%T" >> $logfile ;; esac echo "done$1" >&2 exit 0 """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_tool(os_path(testdir, "xxx.init"), os_path(root, "/etc/init.d/xxx")) copy_tool(os_path(testdir, "zzz.init"), os_path(root, "/etc/init.d/zzz")) # cmd = "{systemctl} is-enabled zzz.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 1) # cmd = "{systemctl} enable xxx.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} enable xxx.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} --no-legend enable zzz.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} is-enabled zzz.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"^enabled")) self.assertEqual(len(lines(out)), 1) # cmd = "{systemctl} default-services" out, end = output2(cmd.format(**locals())) self.assertEqual(end, 0) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 2) # cmd = "{systemctl} --no-legend disable zzz.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} is-enabled zzz.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 1) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) # cmd = "{systemctl} disable xxx.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} disable xxx.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} default-services" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(len(lines(out)), 0) # self.rm_testdir() self.coverage() self.end() def test_3010_check_preset_all(self): """ check that 'is-enabled' reports correctly after 'preset-all' """ self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] ExecStart=/bin/sleep 2 [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/etc/systemd/system/zzc.service"),""" [Unit] Description=Testing C [Service] ExecStart=/bin/sleep 2 [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/etc/systemd/system-preset/our.preset"),""" enable zzb.service disable zzc.service""") # cmd = "{systemctl} is-enabled zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^static")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 0) cmd = "{systemctl} is-enabled zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 1) cmd = "{systemctl} is-enabled zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 1) # cmd = "{systemctl} preset-all" logg.info(" %s", cmd.format(**locals())) out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 0) self.assertEqual(end, 0) # cmd = "{systemctl} is-enabled zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^static")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 0) cmd = "{systemctl} is-enabled zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^enabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 0) cmd = "{systemctl} is-enabled zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 1) # self.rm_testdir() self.coverage() self.end() def test_3011_check_preset_one(self): """ check that 'is-enabled' reports correctly after 'preset service' """ self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] ExecStart=/bin/sleep 2 [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/etc/systemd/system/zzc.service"),""" [Unit] Description=Testing C [Service] ExecStart=/bin/sleep 2 [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/etc/systemd/system-preset/our.preset"),""" enable zzb.service disable zzc.service""") # cmd = "{systemctl} is-enabled zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 1) cmd = "{systemctl} is-enabled zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 1) # cmd = "{systemctl} preset zzc.service -vv" logg.info(" %s", cmd.format(**locals())) out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 0) self.assertEqual(end, 0) # cmd = "{systemctl} is-enabled zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 1) cmd = "{systemctl} is-enabled zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 1) # cmd = "{systemctl} preset zzb.service" logg.info(" %s", cmd.format(**locals())) out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 0) self.assertEqual(end, 0) # cmd = "{systemctl} is-enabled zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^enabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 0) cmd = "{systemctl} is-enabled zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 1) # self.rm_testdir() self.coverage() self.end() def test_3012_check_preset_to_reset_one(self): """ check that 'enable' and 'preset service' are counterparts """ self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] ExecStart=/bin/sleep 2 [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/etc/systemd/system/zzc.service"),""" [Unit] Description=Testing C [Service] ExecStart=/bin/sleep 2 [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/etc/systemd/system-preset/our.preset"),""" enable zzb.service disable zzc.service""") # cmd = "{systemctl} is-enabled zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 1) cmd = "{systemctl} is-enabled zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 1) self.assertEqual(end, 1) # cmd = "{systemctl} preset zzb.service" logg.info(" %s", cmd.format(**locals())) out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 0) self.assertEqual(end, 0) # cmd = "{systemctl} preset zzc.service" logg.info(" %s", cmd.format(**locals())) out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 0) self.assertEqual(end, 0) # cmd = "{systemctl} is-enabled zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^enabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 0) cmd = "{systemctl} is-enabled zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 1) # cmd = "{systemctl} disable zzb.service" logg.info(" %s", cmd.format(**locals())) out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 0) self.assertEqual(end, 0) # cmd = "{systemctl} enable zzc.service" logg.info(" %s", cmd.format(**locals())) out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 0) self.assertEqual(end, 0) # cmd = "{systemctl} is-enabled zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 1) cmd = "{systemctl} is-enabled zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^enabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 0) # cmd = "{systemctl} preset zzb.service" logg.info(" %s", cmd.format(**locals())) out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 0) self.assertEqual(end, 0) # cmd = "{systemctl} preset zzc.service" logg.info(" %s", cmd.format(**locals())) out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 0) self.assertEqual(end, 0) # cmd = "{systemctl} is-enabled zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^enabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 0) cmd = "{systemctl} is-enabled zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 1) # self.rm_testdir() self.coverage() self.end() def test_3013_check_preset_to_reset_some(self): """ check that 'enable' and 'preset services..' are counterparts """ self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] ExecStart=/bin/sleep 2 [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/etc/systemd/system/zzc.service"),""" [Unit] Description=Testing C [Service] ExecStart=/bin/sleep 2 [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/etc/systemd/system-preset/our.preset"),""" enable zzb.service disable zzc.service""") # cmd = "{systemctl} is-enabled zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 1) cmd = "{systemctl} is-enabled zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 1) self.assertEqual(end, 1) # cmd = "{systemctl} preset zzb.service zzc.service" logg.info(" %s", cmd.format(**locals())) out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 0) self.assertEqual(end, 0) # cmd = "{systemctl} is-enabled zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^enabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 0) cmd = "{systemctl} is-enabled zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 1) # cmd = "{systemctl} disable zzb.service" logg.info(" %s", cmd.format(**locals())) out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 0) self.assertEqual(end, 0) # cmd = "{systemctl} enable zzc.service" logg.info(" %s", cmd.format(**locals())) out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 0) self.assertEqual(end, 0) # cmd = "{systemctl} is-enabled zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 1) cmd = "{systemctl} is-enabled zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^enabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 0) # cmd = "{systemctl} preset zzb.service zzc.service" logg.info(" %s", cmd.format(**locals())) out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 0) self.assertEqual(end, 0) # cmd = "{systemctl} is-enabled zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^enabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 0) cmd = "{systemctl} is-enabled zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 1) # cmd = "{systemctl} preset zzb.service zzc.service other.service" logg.info(" %s", cmd.format(**locals())) out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 0) self.assertEqual(end, 1) # self.rm_testdir() self.coverage() self.end() def test_3015_check_preset_all_only_enable(self): """ check that 'preset-all' works with --preset-mode=enable """ self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] ExecStart=/bin/sleep 2 [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/etc/systemd/system/zzc.service"),""" [Unit] Description=Testing C [Service] ExecStart=/bin/sleep 2 [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/etc/systemd/system-preset/our.preset"),""" enable zzb.service disable zzc.service""") # cmd = "{systemctl} is-enabled zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^static")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 0) cmd = "{systemctl} is-enabled zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 1) cmd = "{systemctl} is-enabled zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 1) # cmd = "{systemctl} disable zzb.service" logg.info(" %s", cmd.format(**locals())) out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 0) self.assertEqual(end, 0) # cmd = "{systemctl} enable zzc.service" logg.info(" %s", cmd.format(**locals())) out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 0) self.assertEqual(end, 0) # cmd = "{systemctl} is-enabled zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^static")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 0) cmd = "{systemctl} is-enabled zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 1) cmd = "{systemctl} is-enabled zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^enabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 0) # cmd = "{systemctl} preset-all --preset-mode=enable" logg.info(" %s", cmd.format(**locals())) out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 0) self.assertEqual(end, 0) # cmd = "{systemctl} is-enabled zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^static")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 0) cmd = "{systemctl} is-enabled zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^enabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 0) cmd = "{systemctl} is-enabled zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^enabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 0) # self.rm_testdir() self.coverage() self.end() def test_3016_check_preset_all_only_disable(self): """ check that 'preset-all' works with --preset-mode=disable """ self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] ExecStart=/bin/sleep 2 [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/etc/systemd/system/zzc.service"),""" [Unit] Description=Testing C [Service] ExecStart=/bin/sleep 2 [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/etc/systemd/system-preset/our.preset"),""" enable zzb.service disable zzc.service""") # cmd = "{systemctl} is-enabled zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^static")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 0) cmd = "{systemctl} is-enabled zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 1) cmd = "{systemctl} is-enabled zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 1) # cmd = "{systemctl} disable zzb.service" logg.info(" %s", cmd.format(**locals())) out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 0) self.assertEqual(end, 0) # cmd = "{systemctl} enable zzc.service" logg.info(" %s", cmd.format(**locals())) out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 0) self.assertEqual(end, 0) # cmd = "{systemctl} is-enabled zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^static")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 0) cmd = "{systemctl} is-enabled zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 1) cmd = "{systemctl} is-enabled zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^enabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 0) # cmd = "{systemctl} preset-all --preset-mode=disable" logg.info(" %s", cmd.format(**locals())) out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 0) self.assertEqual(end, 0) # cmd = "{systemctl} is-enabled zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^static")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 0) cmd = "{systemctl} is-enabled zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 1) cmd = "{systemctl} is-enabled zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, r"^disabled")) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 1) # self.rm_testdir() self.coverage() self.end() def test_3020_default_services(self): """ check the 'default-services' to know the enabled services """ self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] ExecStart=/bin/sleep 2 [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/etc/systemd/system/zzc.service"),""" [Unit] Description=Testing C [Service] ExecStart=/bin/sleep 2 [Install] WantedBy=multi-user.target""") # cmd = "{systemctl} default-services" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 0) self.assertEqual(end, 0) # cmd = "{systemctl} --no-legend enable zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} default-services" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 0) # cmd = "{systemctl} --no-legend enable zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} default-services" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 2) self.assertEqual(end, 0) # self.assertFalse(greps(out, "a.service")) self.assertTrue(greps(out, "b.service")) self.assertTrue(greps(out, "c.service")) # self.rm_testdir() self.coverage() self.end() def test_3021_default_services(self): """ check that 'default-services' skips some known services """ self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] ExecStart=/bin/sleep 2 [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/etc/systemd/system/zzc.service"),""" [Unit] Description=Testing C [Service] ExecStart=/bin/sleep 2 [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/etc/systemd/system/mount-disks.service"),""" [Unit] Description=Testing C [Service] ExecStart=/bin/sleep 2 [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/etc/systemd/system/network.service"),""" [Unit] Description=Testing C [Service] ExecStart=/bin/sleep 2 [Install] WantedBy=multi-user.target""") # cmd = "{systemctl} default-services" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 0) self.assertEqual(end, 0) # cmd = "{systemctl} --no-legend enable zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} --no-legend enable zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} --no-legend enable mount-disks.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} --no-legend enable network.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} default-services" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 2) self.assertEqual(end, 0) # cmd = "{systemctl} default-services --all" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 3) self.assertEqual(end, 0) # cmd = "{systemctl} default-services --all --force" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 4) self.assertEqual(end, 0) # self.rm_testdir() self.coverage() self.end() def test_3025_default_user_services(self): """ check the 'default-services' to know the enabled services """ self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) user = self.user() systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] ExecStart=/bin/sleep 2 [Install] WantedBy=multi-user.target""") text_file(os_path(root, "/etc/systemd/system/zzc.service"),""" [Unit] Description=Testing C [Service] ExecStart=/bin/sleep 2 User={user} [Install] WantedBy=multi-user.target""".format(**locals())) configs = os.path.expanduser("~/.config") text_file(os_path(root, configs+"/systemd/user/zzd.service"),""" [Unit] Description=Testing D [Service] ExecStart=/bin/sleep 2 [Install] WantedBy=multi-user.target""".format(**locals())) # cmd = "{systemctl} list-unit-files --user" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertGreater(len(lines(out)), 4) self.assertEqual(end, 0) # cmd = "{systemctl} default-services --user" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 0) self.assertEqual(end, 0) # cmd = "{systemctl} --no-legend enable zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} default-services --user" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 1) self.assertEqual(end, 0) # cmd = "{systemctl} --no-legend enable zzd.service --user" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} default-services --user -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 2) self.assertEqual(end, 0) # self.assertFalse(greps(out, "a.service")) self.assertFalse(greps(out, "b.service")) self.assertTrue(greps(out, "c.service")) self.assertTrue(greps(out, "d.service")) # self.rm_testdir() self.coverage() self.end() def test_3030_systemctl_py_start_simple(self): """ check that we can start simple services with root env""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=simple ExecStart={bindir}/{testsleep} 111 ExecStop=/usr/bin/killall {testsleep} [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) # cmd = "{systemctl} enable zzz.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} --version" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} default-services -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, "zzz.service")) self.assertEqual(len(lines(out)), 1) # cmd = "{systemctl} start zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) # cmd = "{systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_3031_systemctl_py_start_extra_simple(self): """ check that we can start extra simple services with root env""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=simple ExecStart={bindir}/{testsleep} 111 [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) # cmd = "{systemctl} enable zzz.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} --version" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} default-services -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, "zzz.service")) self.assertEqual(len(lines(out)), 1) # cmd = "{systemctl} start zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) # cmd = "{systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_3032_systemctl_py_start_forking(self): """ check that we can start forking services with root env""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") os.makedirs(os_path(root, "/var/run")) shell_file(os_path(testdir, "zzz.init"), """ #! /bin/bash case "$1" in start) [ -d /var/run ] || mkdir -p /var/run ({bindir}/{testsleep} 111 0<&- &>/dev/null & echo $! > {root}/var/run/zzz.init.pid ) & wait %1 ps -o pid,ppid,args ;; stop) killall {testsleep} ;; esac echo "done$1" >&2 exit 0 """.format(**locals())) text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=forking PIDFile={root}/var/run/zzz.init.pid ExecStart={root}/usr/bin/zzz.init start ExeeStop={root}/usr/bin/zzz.init stop [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_tool(os_path(testdir, "zzz.init"), os_path(root, "/usr/bin/zzz.init")) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) # cmd = "{systemctl} enable zzz.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} --version" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} default-services -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, "zzz.service")) self.assertEqual(len(lines(out)), 1) # cmd = "{systemctl} start zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) # cmd = "{systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_3033_systemctl_py_start_forking_without_pid_file(self): """ check that we can start forking services with root env without PIDFile""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") os.makedirs(os_path(root, "/var/run")) shell_file(os_path(testdir, "zzz.init"), """ #! /bin/bash case "$1" in start) ({bindir}/{testsleep} 111 0<&- &>/dev/null &) & wait %1 # ps -o pid,ppid,args >&2 ;; stop) killall {testsleep} echo killed all {testsleep} >&2 sleep 1 ;; esac echo "done$1" >&2 exit 0 """.format(**locals())) text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=forking ExecStart={root}/usr/bin/zzz.init start ExecStop={root}/usr/bin/zzz.init stop [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_tool(os_path(testdir, "zzz.init"), os_path(root, "/usr/bin/zzz.init")) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) # cmd = "{systemctl} enable zzz.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} --version" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} default-services -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, "zzz.service")) self.assertEqual(len(lines(out)), 1) # cmd = "{systemctl} start zzz.service -vvvv 2>&1" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) # cmd = "{systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_3040_systemctl_py_start_simple_bad_stop(self): """ check that we can start simple services with root env""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=simple ExecStart={bindir}/{testsleep} 111 ExecStop=/usr/bin/killall -q z-not-existing TimeoutStopSec=4 [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) # cmd = "{systemctl} enable zzz.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} --version" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} default-services -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, "zzz.service")) self.assertEqual(len(lines(out)), 1) # cmd = "{systemctl} start zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) # cmd = "{systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 1) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_3041_systemctl_py_start_extra_simple_bad_start(self): """ check that we can start extra simple services with root env""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=simple ExecStart={bindir}/{testsleep} foo [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) # cmd = "{systemctl} enable zzz.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} --version" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} default-services -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, "zzz.service")) self.assertEqual(len(lines(out)), 1) # cmd = "{systemctl} start zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 1) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) # cmd = "{systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_3042_systemctl_py_start_forking_bad_stop(self): """ check that we can start forking services with root env""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") os.makedirs(os_path(root, "/var/run")) shell_file(os_path(testdir, "zzz.init"), """ #! /bin/bash case "$1" in start) [ -d /var/run ] || mkdir -p /var/run ({bindir}/{testsleep} 111 0<&- &>/dev/null & echo $! > {root}/var/run/zzz.init.pid ) & wait %1 ps -o pid,ppid,args ;; stop) killall {testsleep} exit 1 ;; esac echo "done$1" >&2 exit 0 """.format(**locals())) text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=forking PIDFile={root}/var/run/zzz.init.pid ExecStart={root}/usr/bin/zzz.init start ExeeStop={root}/usr/bin/zzz.init stop [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_tool(os_path(testdir, "zzz.init"), os_path(root, "/usr/bin/zzz.init")) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) # cmd = "{systemctl} enable zzz.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} --version" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} default-services -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, "zzz.service")) self.assertEqual(len(lines(out)), 1) # cmd = "{systemctl} start zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) # cmd = "{systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_3043_systemctl_py_start_forking_bad_start(self): """ check that we can start forking services with root env without PIDFile""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") os.makedirs(os_path(root, "/var/run")) shell_file(os_path(testdir, "zzz.init"), """ #! /bin/bash case "$1" in start) ({bindir}/{testsleep} 111 0<&- &>/dev/null &) & wait %1 # ps -o pid,ppid,args >&2 ;; stop) killall {testsleep} echo killed all {testsleep} >&2 sleep 1 ;; esac echo "done$1" >&2 exit 1 """.format(**locals())) text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=forking ExecStart={root}/usr/bin/zzz.init start ExecStop={root}/usr/bin/zzz.init stop [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_tool(os_path(testdir, "zzz.init"), os_path(root, "/usr/bin/zzz.init")) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) # cmd = "{systemctl} enable zzz.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} --version" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} default-services -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, "zzz.service")) self.assertEqual(len(lines(out)), 1) # cmd = "{systemctl} start zzz.service -vvvv 2>&1" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 1) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) # cmd = "{systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 1) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_3049_systemctl_py_run_default_services_in_testenv(self): """ check that we can enable services in a test env to be run as default-services""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStart={bindir}/{testsleep} 99 [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C [Service] Type=simple ExecStart={bindir}/{testsleep} 111 [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zza.service"), os_path(root, "/etc/systemd/system/zza.service")) copy_file(os_path(testdir, "zzb.service"), os_path(root, "/etc/systemd/system/zzb.service")) copy_file(os_path(testdir, "zzc.service"), os_path(root, "/etc/systemd/system/zzc.service")) # cmd = "{systemctl} enable zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} enable zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} --version" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} default-services -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, "zzb.service")) self.assertEqual(len(lines(out)), 2) # cmd = "{systemctl} default -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep+" 99")) self.assertTrue(greps(top, testsleep+" 111")) # cmd = "{systemctl} halt -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def real_3050_systemctl_py_check_is_active(self): self.test_3050_systemctl_py_check_is_active(True) def test_3050_systemctl_py_check_is_active(self, real = None): """ check is_active behaviour""" self.begin() vv = "-vv" testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir, real) systemctl = cover() + _systemctl_py + " --root=" + root if real: vv, systemctl = "", "/usr/bin/systemctl" testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStart={bindir}/{testsleep} 99 [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C [Service] Type=simple ExecStart={bindir}/{testsleep} 111 [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zza.service"), os_path(root, "/etc/systemd/system/zza.service")) copy_file(os_path(testdir, "zzb.service"), os_path(root, "/etc/systemd/system/zzb.service")) copy_file(os_path(testdir, "zzc.service"), os_path(root, "/etc/systemd/system/zzc.service")) sh____("{systemctl} daemon-reload".format(**locals())) # enable_A = "{systemctl} enable zza.service" enable_B = "{systemctl} enable zzb.service" enable_C = "{systemctl} enable zzc.service" enable_D = "{systemctl} enable zzd.service" doneA, exitA = output2(enable_A.format(**locals())) doneB, exitB = output2(enable_B.format(**locals())) doneC, exitC = output2(enable_C.format(**locals())) doneD, exitD = output2(enable_D.format(**locals())) if TODO or real: self.assertEqual(exitA, 0) else: self.assertEqual(exitA, 1) self.assertEqual(exitB, 0) self.assertEqual(exitC, 0) self.assertEqual(exitD, 1) # is_active_A = "{systemctl} is-active zza.service" is_active_B = "{systemctl} is-active zzb.service" is_active_C = "{systemctl} is-active zzc.service" is_active_D = "{systemctl} is-active zzd.service" actA, exitA = output2(is_active_A.format(**locals())) actB, exitB = output2(is_active_B.format(**locals())) actC, exitC = output2(is_active_C.format(**locals())) actD, exitD = output2(is_active_D.format(**locals())) self.assertEqual(actA.strip(), "unknown") self.assertEqual(actB.strip(), "inactive") self.assertEqual(actC.strip(), "inactive") self.assertEqual(exitA, 3) self.assertEqual(exitB, 3) self.assertEqual(exitC, 3) self.assertEqual(exitD, 3) # cmd = "{systemctl} start zzb.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # is_active_A = "{systemctl} is-active zza.service" is_active_B = "{systemctl} is-active zzb.service" is_active_C = "{systemctl} is-active zzc.service" is_active_D = "{systemctl} is-active zzd.service" actA, exitA = output2(is_active_A.format(**locals())) actB, exitB = output2(is_active_B.format(**locals())) actC, exitC = output2(is_active_C.format(**locals())) actD, exitD = output2(is_active_D.format(**locals())) self.assertEqual(actA.strip(), "unknown") self.assertEqual(actB.strip(), "active") self.assertEqual(actC.strip(), "inactive") self.assertEqual(actD.strip(), "unknown") self.assertNotEqual(exitA, 0) self.assertEqual(exitB, 0) self.assertNotEqual(exitC, 0) self.assertNotEqual(exitD, 0) # logg.info("== checking with --quiet") is_active_A = "{systemctl} is-active zza.service --quiet" is_active_B = "{systemctl} is-active zzb.service --quiet" actA, exitA = output2(is_active_A.format(**locals())) actB, exitB = output2(is_active_B.format(**locals())) self.assertEqual(actA, "") self.assertEqual(actB, "") self.assertNotEqual(exitA, 0) self.assertEqual(exitB, 0) # logg.info("== checking combinations of arguments") is_active_BC = "{systemctl} is-active zzb.service zzc.service " is_active_CD = "{systemctl} is-active zzc.service zzd.service" is_active_BD = "{systemctl} is-active zzb.service zzd.service" is_active_BCD = "{systemctl} is-active zzb.service zzc.service zzd.service" actBC, exitBC = output2(is_active_BC.format(**locals())) actCD, exitCD = output2(is_active_CD.format(**locals())) actBD, exitBD = output2(is_active_BD.format(**locals())) actBCD, exitBCD = output2(is_active_BCD.format(**locals())) self.assertEqual(actBC.split("\n"), ["active", "inactive", ""]) self.assertEqual(actCD.split("\n"), [ "inactive", "unknown",""]) self.assertEqual(actBD.split("\n"), [ "active", "unknown", ""]) self.assertEqual(actBCD.split("\n"), ["active", "inactive", "unknown", ""]) self.assertNotEqual(exitBC, 0) ## this is how the original systemctl self.assertNotEqual(exitCD, 0) ## works. The documentation however self.assertNotEqual(exitBD, 0) ## says to return 0 if any service self.assertNotEqual(exitBCD, 0) ## is found to be 'active' top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep+" 99")) # cmd = "{systemctl} start zzc.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # actBC, exitBC = output2(is_active_BC.format(**locals())) self.assertEqual(actBC.split("\n"), ["active", "active", ""]) self.assertEqual(exitBC, 0) ## all is-active => return 0 # cmd = "{systemctl} stop zzb.service zzc.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # actBC, exitBC = output2(is_active_BC.format(**locals())) self.assertEqual(actBC.split("\n"), ["inactive", "inactive", ""]) self.assertNotEqual(exitBC, 0) # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.rm_zzfiles(root) self.coverage() self.end() def real_3051_systemctl_py_check_is_failed(self): self.test_3051_systemctl_py_check_is_failed(True) def test_3051_systemctl_py_check_is_failed(self, real = None): """ check is_failed behaviour""" self.begin() vv = "-vv" testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir, real) systemctl = cover() + _systemctl_py + " --root=" + root if real: vv, systemctl = "", "/usr/bin/systemctl" testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStart={bindir}/{testsleep} 99 [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C [Service] Type=simple ExecStart={bindir}/{testsleep} 111 [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zza.service"), os_path(root, "/etc/systemd/system/zza.service")) copy_file(os_path(testdir, "zzb.service"), os_path(root, "/etc/systemd/system/zzb.service")) copy_file(os_path(testdir, "zzc.service"), os_path(root, "/etc/systemd/system/zzc.service")) sh____("{systemctl} daemon-reload".format(**locals())) # enable_A = "{systemctl} enable zza.service" enable_B = "{systemctl} enable zzb.service" enable_C = "{systemctl} enable zzc.service" enable_D = "{systemctl} enable zzd.service" doneA, exitA = output2(enable_A.format(**locals())) doneB, exitB = output2(enable_B.format(**locals())) doneC, exitC = output2(enable_C.format(**locals())) doneD, exitD = output2(enable_D.format(**locals())) if TODO or real: self.assertEqual(exitA, 0) else: self.assertEqual(exitA, 1) self.assertEqual(exitB, 0) self.assertEqual(exitC, 0) self.assertEqual(exitD, 1) # is_active_A = "{systemctl} is-failed zza.service" is_active_B = "{systemctl} is-failed zzb.service" is_active_C = "{systemctl} is-failed zzc.service" is_active_D = "{systemctl} is-failed zzd.service" actA, exitA = output2(is_active_A.format(**locals())) actB, exitB = output2(is_active_B.format(**locals())) actC, exitC = output2(is_active_C.format(**locals())) actD, exitD = output2(is_active_D.format(**locals())) self.assertEqual(actA.strip(), "unknown") self.assertEqual(actB.strip(), "inactive") self.assertEqual(actC.strip(), "inactive") self.assertEqual(actD.strip(), "unknown") self.assertEqual(exitA, 1) self.assertEqual(exitB, 1) self.assertEqual(exitC, 1) self.assertEqual(exitD, 1) # cmd = "{systemctl} start zzb.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # is_active_A = "{systemctl} is-failed zza.service" is_active_B = "{systemctl} is-failed zzb.service" is_active_C = "{systemctl} is-failed zzc.service" is_active_D = "{systemctl} is-failed zzd.service" actA, exitA = output2(is_active_A.format(**locals())) actB, exitB = output2(is_active_B.format(**locals())) actC, exitC = output2(is_active_C.format(**locals())) actD, exitD = output2(is_active_D.format(**locals())) self.assertEqual(actA.strip(), "unknown") self.assertEqual(actB.strip(), "active") self.assertEqual(actC.strip(), "inactive") self.assertEqual(actD.strip(), "unknown") self.assertEqual(exitA, 1) self.assertEqual(exitB, 1) self.assertEqual(exitC, 1) self.assertEqual(exitD, 1) # logg.info("== checking with --quiet") is_active_A = "{systemctl} is-failed zza.service --quiet" is_active_B = "{systemctl} is-failed zzb.service --quiet" actA, exitA = output2(is_active_A.format(**locals())) actB, exitB = output2(is_active_B.format(**locals())) self.assertEqual(actA, "") self.assertEqual(actB, "") self.assertEqual(exitA, 1) self.assertEqual(exitB, 1) # logg.info("== checking combinations of arguments") is_active_BC = "{systemctl} is-failed zzb.service zzc.service {vv}" is_active_CD = "{systemctl} is-failed zzc.service zzd.service {vv}" is_active_BD = "{systemctl} is-failed zzb.service zzd.service {vv}" is_active_BCD = "{systemctl} is-failed zzb.service zzc.service zzd.service {vv}" is_active_BCDX = "{systemctl} is-failed zzb.service zzc.service zzd.service --quiet {vv}" actBC, exitBC = output2(is_active_BC.format(**locals())) actCD, exitCD = output2(is_active_CD.format(**locals())) actBD, exitBD = output2(is_active_BD.format(**locals())) actBCD, exitBCD = output2(is_active_BCD.format(**locals())) actBCDX, exitBCDX = output2(is_active_BCDX.format(**locals())) self.assertEqual(actBC.split("\n"), ["active", "inactive", ""]) self.assertEqual(actCD.split("\n"), [ "inactive", "unknown",""]) self.assertEqual(actBD.split("\n"), [ "active", "unknown", ""]) self.assertEqual(actBCD.split("\n"), ["active", "inactive", "unknown", ""]) self.assertEqual(actBCDX.split("\n"), [""]) self.assertNotEqual(exitBC, 0) ## this is how the original systemctl self.assertNotEqual(exitCD, 0) ## works. The documentation however self.assertNotEqual(exitBD, 0) ## says to return 0 if any service self.assertNotEqual(exitBCD, 0) ## is found to be 'active' top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep+" 99")) # cmd = "{systemctl} start zzc.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # actBC, exitBC = output2(is_active_BC.format(**locals())) self.assertEqual(actBC.split("\n"), ["active", "active", ""]) self.assertNotEqual(exitBC, 0) # actBC, exitBC = output2(is_active_BC.format(**locals())) self.assertEqual(actBC.split("\n"), ["active", "active", ""]) self.assertNotEqual(exitBC, 0) # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) # actBC, exitBC = output2(is_active_BC.format(**locals())) self.assertEqual(exitBC, 0) if TODO or real: self.assertEqual(actBC.split("\n"), ["inactive", "inactive", ""]) else: self.assertEqual(actBC.split("\n"), ["failed", "failed", ""]) # cmd = "{systemctl} stop zzb.service zzc.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # actBC, exitBC = output2(is_active_BC.format(**locals())) self.assertEqual(actBC.split("\n"), ["inactive", "inactive", ""]) self.assertNotEqual(exitBC, 0) # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def real_3060_is_active_for_forking(self): self.test_3060_is_active_for_forking(True) def test_3060_is_active_for_forking(self, real = None): """ check that we can start forking services and have them is-active""" self.begin() vv = "-vv" testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir, real) systemctl = cover() + _systemctl_py + " --root=" + root bindir = os_path(root, "/usr/bin") if real: vv, systemctl = "", "/usr/bin/systemctl" testsleep = self.testname("sleep") self.makedirs(os_path(root, "/var/run")) shell_file(os_path(testdir, "zzz.init"), """ #! /bin/bash case "$1" in start) [ -d /var/run ] || mkdir -p /var/run ({bindir}/{testsleep} 111 0<&- &>/dev/null & echo $! > {root}/var/run/zzz.init.pid ) & wait %1 ps -o pid,ppid,args ;; stop) killall {testsleep} ;; esac echo "done$1" >&2 exit 0 """.format(**locals())) text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=forking PIDFile={root}/var/run/zzz.init.pid ExecStart={root}/usr/bin/zzz.init start ExecStop={root}/usr/bin/zzz.init stop [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_tool(os_path(testdir, "zzz.init"), os_path(root, "/usr/bin/zzz.init")) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) cmd = "{systemctl} daemon-reload" sh____(cmd.format(**locals())) # cmd = "{systemctl} is-active zzz.service {vv}" actZX, exitZX = output2(cmd.format(**locals())) self.assertEqual(actZX.split("\n"), ["unknown", ""]) self.assertEqual(exitZX, 3) # cmd = "{systemctl} enable zzz.service {vv}" sh____(cmd.format(**locals())) cmd = "{systemctl} is-active zzz.service {vv}" actZX, exitZX = output2(cmd.format(**locals())) self.assertEqual(actZX.split("\n"), ["inactive", ""]) self.assertEqual(exitZX, 3) # cmd = "{systemctl} start zzz.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) # is_active_ZX = "{systemctl} is-active zzz.service {vv}" actZX, exitZX = output2(is_active_ZX.format(**locals())) self.assertEqual(actZX.split("\n"), ["active", ""]) self.assertEqual(exitZX, 0) # cmd = "{systemctl} stop zzz.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) # is_active_ZX = "{systemctl} is-active zzz.service {vv}" actZX, exitZX = output2(is_active_ZX.format(**locals())) self.assertEqual(actZX.split("\n"), ["inactive", ""]) self.assertEqual(exitZX, 3) # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.rm_zzfiles(root) self.coverage() self.end() def real_3061_is_failed_for_forking(self): self.test_3061_is_failed_for_forking(True) def test_3061_is_failed_for_forking(self, real = None): """ check that we can start forking services and have them is-failed""" self.begin() vv = "-vv" testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir, real) systemctl = cover() + _systemctl_py + " --root=" + root if real: vv, systemctl = "", "/usr/bin/systemctl" bindir = os_path(root, "/usr/bin") testsleep = self.testname("sleep") self.makedirs(os_path(root, "/var/run")) shell_file(os_path(testdir, "zzz.init"), """ #! /bin/bash case "$1" in start) [ -d /var/run ] || mkdir -p /var/run ({bindir}/{testsleep} 111 0<&- &>/dev/null & echo $! > {root}/var/run/zzz.init.pid ) & wait %1 ps -o pid,ppid,args ;; stop) killall {testsleep} ;; esac echo "done$1" >&2 exit 0 """.format(**locals())) text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=forking PIDFile={root}/var/run/zzz.init.pid ExecStart={root}/usr/bin/zzz.init start ExecStop={root}/usr/bin/zzz.init stop [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_tool(os_path(testdir, "zzz.init"), os_path(root, "/usr/bin/zzz.init")) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) sh____("{systemctl} daemon-reload".format(**locals())) # cmd = "{systemctl} is-failed zzz.service {vv}" actZX, exitZX = output2(cmd.format(**locals())) self.assertEqual(actZX.split("\n"), ["unknown", ""]) self.assertEqual(exitZX, 1) # cmd = "{systemctl} enable zzz.service" sh____(cmd.format(**locals())) cmd = "{systemctl} is-failed zzz.service {vv}" actZX, exitZX = output2(cmd.format(**locals())) self.assertEqual(actZX.split("\n"), ["inactive", ""]) self.assertEqual(exitZX, 1) # cmd = "{systemctl} start zzz.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) # is_active_ZX = "{systemctl} is-failed zzz.service {vv}" actZX, exitZX = output2(is_active_ZX.format(**locals())) self.assertEqual(actZX.split("\n"), ["active", ""]) self.assertEqual(exitZX, 1) # cmd = "{systemctl} stop zzz.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) # is_active_ZX = "{systemctl} is-failed zzz.service {vv}" actZX, exitZX = output2(is_active_ZX.format(**locals())) self.assertEqual(actZX.split("\n"), ["inactive", ""]) self.assertEqual(exitZX, 1) # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.rm_zzfiles(root) self.coverage() self.end() def real_3063_is_active_for_forking_delayed(self): self.test_3063_is_active_for_forking_delayed(True) def test_3063_is_active_for_forking_delayed(self, real = None): """ check that we can start forking services and have them is-active, even when the pid-file is created later because startup waits for its existance.""" self.begin() vv = "-vv" testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir, real) systemctl = cover() + _systemctl_py + " --root=" + root if real: vv, systemctl = "", "/usr/bin/systemctl" testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") self.makedirs(os_path(root, "/var/run")) shell_file(os_path(testdir, "zzz.init"), """ #! /bin/bash case "$1" in start) [ -d /var/run ] || mkdir -p /var/run ({bindir}/{testsleep} 111 0<&- &>/dev/null & sleep 4 echo $! > {root}/var/run/zzz.init.pid ) & sleep 1 ps -o pid,ppid,args ;; stop) killall {testsleep} ;; esac echo "done$1" >&2 exit 0 """.format(**locals())) text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=forking PIDFile={root}/var/run/zzz.init.pid ExecStart={root}/usr/bin/zzz.init start ExecStop={root}/usr/bin/zzz.init stop [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_tool(os_path(testdir, "zzz.init"), os_path(root, "/usr/bin/zzz.init")) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) sh____("{systemctl} daemon-reload".format(**locals())) # cmd = "{systemctl} is-active zzz.service {vv}" actZX, exitZX = output2(cmd.format(**locals())) self.assertEqual(actZX.split("\n"), ["unknown", ""]) self.assertEqual(exitZX, 3) # cmd = "{systemctl} enable zzz.service" sh____(cmd.format(**locals())) cmd = "{systemctl} is-active zzz.service {vv}" actZX, exitZX = output2(cmd.format(**locals())) self.assertEqual(actZX.split("\n"), ["inactive", ""]) self.assertEqual(exitZX, 3) # cmd = "{systemctl} start zzz.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) # is_active_ZX = "{systemctl} is-active zzz.service {vv}" actZX, exitZX = output2(is_active_ZX.format(**locals())) self.assertEqual(actZX.split("\n"), ["active", ""]) self.assertEqual(exitZX, 0) self.assertTrue(os.path.exists(os_path(root, "/var/run/zzz.init.pid"))) time.sleep(4) self.assertTrue(os.path.exists(os_path(root, "/var/run/zzz.init.pid"))) # cmd = "{systemctl} stop zzz.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) # is_active_ZX = "{systemctl} is-active zzz.service {vv}" actZX, exitZX = output2(is_active_ZX.format(**locals())) self.assertEqual(actZX.split("\n"), ["inactive", ""]) self.assertEqual(exitZX, 3) # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.rm_zzfiles(root) self.coverage() self.end() def real_3070_check_prestart_is_activating(self): self.test_3063_check_prestart_is_activating(True) def test_3070_check_prestart_is_activating(self, real = None): """ consider a situation where a 'systemctl start ' is taking a bit longer to start. Especially some pre-start must be blocking while being in state 'activating'""" self.begin() vv = "-vv" testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir, real) systemctl = cover() + realpath(_systemctl_py) + " --root=" + root if real: vv, systemctl = "", "/usr/bin/systemctl" testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") logfile = os.path.join(os.path.abspath(testdir), "zzz.log") self.makedirs(os_path(root, "/var/run")) shell_file(os_path(testdir, "zzz.init"), """ #! /bin/bash case "$1" in start) [ -d /var/run ] || mkdir -p /var/run ( mkdir -p {root}/var/log echo `date +%M:%S` starting pid >{logfile} {bindir}/{testsleep} 111 0<&- &>/dev/null & echo $! > {root}/var/run/zzz.init.pid echo `date +%M:%S` started pid >>{logfile} sleep 1 echo `date +%M:%S` starting zza >>{logfile} {systemctl} start zza.service {vv} >>{logfile} 2>&1 echo `date +%M:%S` started zza >>{logfile} ) & sleep 1 ps -o pid,ppid,args ;; stop) killall {testsleep} ;; esac echo "done$1" >&2 exit 0 """.format(**locals())) text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=forking PIDFile={root}/var/run/zzz.init.pid ExecStart={root}/usr/bin/zzz.init start ExecStop={root}/usr/bin/zzz.init stop [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zza.service"),""" [Unit] Description=Testing A [Service] Type=simple ExecStartPre={bindir}/{testsleep}pre 5 ExecStart={bindir}/{testsleep}now 10 [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep+"pre")) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep+"now")) copy_tool(os_path(testdir, "zzz.init"), os_path(root, "/usr/bin/zzz.init")) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) copy_file(os_path(testdir, "zza.service"), os_path(root, "/etc/systemd/system/zza.service")) sh____("{systemctl} daemon-reload".format(**locals())) # cmd = "{systemctl} enable zza.service zzz.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} start zzz.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) logg.info("===== [0] just started") top = greps(_recent(output(_top_list)), "sleep") logg.info("top>>>\n| %s", "\n| ".join(top)) self.assertFalse(greps(top, testsleep+"pre")) self.assertFalse(greps(top, testsleep+"now")) log0 = lines(open(logfile)) logg.info("zzz.log>\n\t%s", "\n\t".join(log0)) time.sleep(2) logg.info("===== [1] after start") cmd = "{systemctl} is-active zza.service zzz.service {vv}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, err, out) top = greps(_recent(output(_top_list)), "sleep") logg.info("top>>>\n| %s", "\n| ".join(top)) self.assertTrue(greps(top, testsleep+"pre")) self.assertFalse(greps(top, testsleep+"now")) log1 = lines(open(logfile)) logg.info("zzz.log>\n\t%s", "\n\t".join(log1)) time.sleep(2) # logg.info("===== [2] some later") cmd = "{systemctl} is-active zza.service zzz.service {vv}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, err, out) top = greps(_recent(output(_top_list)), "sleep") logg.info("top>>>\n| %s", "\n| ".join(top)) self.assertTrue(greps(top, testsleep+"pre")) self.assertFalse(greps(top, testsleep+"now")) log2 = lines(open(logfile)) logg.info("zzz.log>\n\t%s", "\n\t".join(log2)) time.sleep(2) # logg.info("===== [3] some more later") cmd = "{systemctl} is-active zza.service zzz.service {vv}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, err, out) top = greps(_recent(output(_top_list)), "sleep") logg.info("top>>>\n| %s", "\n| ".join(top)) self.assertFalse(greps(top, testsleep+"pre")) self.assertTrue(greps(top, testsleep+"now")) log3 = lines(open(logfile)) logg.info("zzz.log>\n\t%s", "\n\t".join(log3)) time.sleep(2) logg.info("===== [4] even more later") cmd = "{systemctl} is-active zza.service zzz.service {vv}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, err, out) top = greps(_recent(output(_top_list)), "sleep") logg.info("top>>>\n| %s", "\n| ".join(top)) self.assertFalse(greps(top, testsleep+"pre")) self.assertTrue(greps(top, testsleep+"now")) log4 = lines(open(logfile)) logg.info("zzz.log>\n\t%s", "\n\t".join(log4)) time.sleep(2) # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.rm_zzfiles(root) self.coverage() self.end() def real_3080_two_service_starts_in_parallel(self): self.test_3063_two_service_starts_in_parallel(True) def test_3080_two_service_starts_in_parallel(self, real = None): """ consider a situation where a 'systemctl start ' is done from two programs at the same time. Ensure that there is a locking that disallow then to run in parallel.""" self.begin() vv = "-vv" testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir, real) systemctl = cover() + realpath(_systemctl_py) + " --root=" + root if real: vv, systemctl = "", "/usr/bin/systemctl" testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") logfile = os.path.join(os.path.abspath(testdir), "zzz.log") self.makedirs(os_path(root, "/var/run")) shell_file(os_path(testdir, "zzz.init"), """ #! /bin/bash case "$1" in start) [ -d /var/run ] || mkdir -p /var/run ( mkdir -p {root}/var/log echo `date +%M:%S` starting pid >{logfile} {bindir}/{testsleep} 111 0<&- &>/dev/null & echo $! > {root}/var/run/zzz.init.pid echo `date +%M:%S` started pid >>{logfile} sleep 2 echo `date +%M:%S` starting zza >>{logfile} {systemctl} start zza.service {vv} >>{logfile} 2>&1 echo `date +%M:%S` started zza >>{logfile} ) & sleep 1 ps -o pid,ppid,args ;; stop) killall {testsleep} ;; esac echo "done$1" >&2 exit 0 """.format(**locals())) text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=forking PIDFile={root}/var/run/zzz.init.pid ExecStart={root}/usr/bin/zzz.init start ExecStop={root}/usr/bin/zzz.init stop [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zza.service"),""" [Unit] Description=Testing A [Service] Type=simple ExecStartPre={bindir}/{testsleep}pre 5 ExecStart={bindir}/{testsleep}now 10 [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep+"pre")) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep+"now")) copy_tool(os_path(testdir, "zzz.init"), os_path(root, "/usr/bin/zzz.init")) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) copy_file(os_path(testdir, "zza.service"), os_path(root, "/etc/systemd/system/zza.service")) sh____("{systemctl} daemon-reload".format(**locals())) # cmd = "{systemctl} enable zza.service zzz.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} start zzz.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) logg.info("===== [0] just started") top = greps(_recent(output(_top_list)), "sleep") logg.info("top>>>\n| %s", "\n| ".join(top)) self.assertTrue(greps(top, testsleep)) log1 = lines(open(logfile)) logg.info("zzz.log>\n\t%s", "\n\t".join(log1)) time.sleep(3) logg.info("===== [1] after start") top = greps(_recent(output(_top_list)), "sleep") logg.info("top>>>\n| %s", "\n| ".join(top)) self.assertTrue(greps(top, testsleep)) log1 = lines(open(logfile)) logg.info("zzz.log>\n\t%s", "\n\t".join(log1)) # logg.info("====== [2] start next") cmd = "{systemctl} is-active zza.service zzz.service {vv}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, err, out) cmd = "{systemctl} start zza.service {vv}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, out, err) self.assertEqual(end, 0) top = greps(_recent(output(_top_list)), "sleep") logg.info("top>>>\n| %s", "\n| ".join(top)) self.assertTrue(greps(top, testsleep)) log1 = lines(open(logfile)) logg.info("zzz.log>\n\t%s", "\n\t".join(log1)) # self.assertTrue(greps(err, "1. systemctl locked by")) self.assertTrue(greps(err, "the service is already running on PID")) # FIXME: may not be? # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.rm_zzfiles(root) self.coverage() self.end() def test_3081_two_service_starts_in_parallel_with_lockfile_remove(self, real = None): """ consider a situation where a 'systemctl start ' is done from two programs at the same time. Ensure that there is a locking that disallows them to run in parallel. In this scenario we test what happens if the lockfile is deleted in between.""" self.begin() vv = "-vv" removelockfile="--coverage=removelockfile,sleep" testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir, real) systemctl = cover() + realpath(_systemctl_py) + " --root=" + root if real: vv, removelockfile, systemctl = "", "", "/usr/bin/systemctl" testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") logfile = os.path.join(os.path.abspath(testdir), "zzz.log") self.makedirs(os_path(root, "/var/run")) if os.path.exists(logfile): os.remove(logfile) shell_file(os_path(testdir, "zzz.init"), """ #! /bin/bash case "$1" in start) [ -d /var/run ] || mkdir -p /var/run ( mkdir -p {root}/var/log echo zzz `date +%M:%S` "[$$]" starting pid >>{logfile} {bindir}/{testsleep} 111 0<&- &>/dev/null & echo $! > {root}/var/run/zzz.init.pid echo zzz `date +%M:%S` "[$$]" started pid >>{logfile} sleep 2 echo zzz `date +%M:%S` "[$$]" starting zza >>{logfile} {systemctl} start zza.service {vv} {vv} {removelockfile} >>{logfile} 2>&1 echo zzz `date +%M:%S` "[$$]" started zza >>{logfile} ) & sleep 1 ps -o pid,ppid,args ;; stop) killall {testsleep} ;; esac echo "done$1" >&2 exit 0 """.format(**locals())) text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=forking PIDFile={root}/var/run/zzz.init.pid ExecStart={root}/usr/bin/zzz.init start ExecStop={root}/usr/bin/zzz.init stop [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zza.service"),""" [Unit] Description=Testing A [Service] Type=simple ExecStartPre={bindir}/{testsleep}pre 5 ExecStart={bindir}/{testsleep}now 10 [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep+"pre")) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep+"now")) copy_tool(os_path(testdir, "zzz.init"), os_path(root, "/usr/bin/zzz.init")) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) copy_file(os_path(testdir, "zza.service"), os_path(root, "/etc/systemd/system/zza.service")) sh____("{systemctl} daemon-reload".format(**locals())) # cmd = "{systemctl} enable zza.service zzz.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} start zzz.service {vv} {removelockfile}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) logg.info("===== [0] just started") top = greps(_recent(output(_top_list)), "sleep") logg.info("top>>>\n| %s", "\n| ".join(top)) self.assertTrue(greps(top, testsleep)) log1 = lines(open(logfile)) logg.info("zzz.log>\n\t%s", "\n\t".join(log1)) time.sleep(2) logg.info("===== [1] after start") top = greps(_recent(output(_top_list)), "sleep") logg.info("top>>>\n| %s", "\n| ".join(top)) self.assertTrue(greps(top, testsleep)) log1 = lines(open(logfile)) logg.info("zzz.log>\n\t%s", "\n\t".join(log1)) # logg.info("====== start next") # cmd = "{systemctl} is-active zza.service zzz.service {vv}" # out, err, end = output3(cmd.format(**locals())) # logg.info(" %s =>%s\n%s\n%s", cmd, end, err, out) cmd = "{systemctl} start zza.service {vv} {vv} {removelockfile}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, err, out) self.assertEqual(end, 0) top = greps(_recent(output(_top_list)), "sleep") logg.info("top>>>\n| %s", "\n| ".join(top)) self.assertTrue(greps(top, testsleep)) # self.assertTrue(greps(err, "1. systemctl locked by")) self.assertTrue(greps(err, "the service is already running on PID")) self.assertTrue(greps(err, "lock got deleted, trying again")) self.assertTrue(greps(err, "lock got deleted, trying again")) # log1 = lines(open(logfile)) logg.info("zzz.log>\n\t%s", "\n\t".join(log1)) # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.rm_zzfiles(root) self.coverage() self.end() def real_3102_mask_service_creates_empty_file(self): self.test_3102_mask_service_creates_empty_file(True) def test_3102_mask_service_creates_empty_file(self, real = False): """ check that a service can be masked """ self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir, real) vv = "-vv" systemctl = cover() + _systemctl_py + " --root=" + root if real: vv, systemctl = "", "/usr/bin/systemctl" self.rm_zzfiles(root) # text_file(os_path(root, "/usr/lib/systemd/system/zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(root, "/usr/lib/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] ExecStart=/bin/sleep 2 [Install] WantedBy=multi-user.target""") cmd = "{systemctl} daemon-reload" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} enable zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) enabled_file = os_path(root, "/etc/systemd/system/multi-user.target.wants/zzb.service") self.assertTrue(os.path.islink(enabled_file)) textB = file(enabled_file).read() self.assertTrue(greps(textB, "Testing B")) self.assertIn("\nDescription", textB) cmd = "{systemctl} status zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, "enabled")) # ......................................... cmd = "{systemctl} mask zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} status zzb.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertFalse(greps(out, "enabled")) self.assertTrue(greps(out, "masked")) if real: self.assertTrue(greps(out, "/dev/null")) else: self.assertTrue(greps(out, "None, ")) mask_file = os_path(root, "/etc/systemd/system/zzb.service") self.assertTrue(os.path.islink(mask_file)) target = os.readlink(mask_file) self.assertEqual(target, "/dev/null") cmd = "{systemctl} show zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, "LoadState=masked")) self.assertTrue(greps(out, "UnitFileState=masked")) self.assertTrue(greps(out, "Id=zzb.service")) self.assertTrue(greps(out, "Names=zzb.service")) cmd = "{systemctl} is-active zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) if real: self.assertTrue(greps(out, "inactive")) cmd = "{systemctl} is-enabled zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, "masked")) self.rm_zzfiles(root) self.rm_testdir() self.coverage() self.end() def real_3104_unmask_service_removes_empty_file(self): self.test_3104_unmask_service_removes_empty_file(True) def test_3104_unmask_service_removes_empty_file(self, real = False): """ check that a service can be unmasked """ self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir, real) vv = "-vv" systemctl = cover() + _systemctl_py + " --root=" + root if real: vv, systemctl = "", "/usr/bin/systemctl" self.rm_zzfiles(root) # text_file(os_path(root, "/usr/lib/systemd/system/zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(root, "/usr/lib/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] ExecStart=/bin/sleep 2 [Install] WantedBy=multi-user.target""") cmd = "{systemctl} daemon-reload" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} enable zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) enabled_file = os_path(root, "/etc/systemd/system/multi-user.target.wants/zzb.service") self.assertTrue(os.path.islink(enabled_file)) textB = file(enabled_file).read() self.assertTrue(greps(textB, "Testing B")) self.assertIn("\nDescription", textB) cmd = "{systemctl} status zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, "enabled")) # ......................................... cmd = "{systemctl} mask zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} status zzb.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertFalse(greps(out, "enabled")) self.assertTrue(greps(out, "masked")) if real: self.assertTrue(greps(out, "/dev/null")) else: self.assertTrue(greps(out, "None, ")) mask_file = os_path(root, "/etc/systemd/system/zzb.service") self.assertTrue(os.path.islink(mask_file)) target = os.readlink(mask_file) self.assertEqual(target, "/dev/null") cmd = "{systemctl} show zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, "LoadState=masked")) self.assertTrue(greps(out, "UnitFileState=masked")) self.assertTrue(greps(out, "Id=zzb.service")) self.assertTrue(greps(out, "Names=zzb.service")) # ................................................. cmd = "{systemctl} unmask zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} status zzb.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, "enabled")) self.assertFalse(greps(out, "masked")) mask_file = os_path(root, "/etc/systemd/system/zzb.service") self.assertFalse(os.path.exists(mask_file)) cmd = "{systemctl} show zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertTrue(greps(out, "LoadState=loaded")) self.assertTrue(greps(out, "Id=zzb.service")) self.assertTrue(greps(out, "Names=zzb.service")) self.rm_zzfiles(root) self.rm_testdir() self.coverage() self.end() def test_3108_is_masked_for_nonexistant_service(self): """ check that mask/unmask reports correctly for non-existant services """ self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/etc/systemd/system/zzc.service"),""" [Unit] Description=Testing C [Service] ExecStart=/bin/sleep 2 [Install] WantedBy=multi-user.target""") # cmd = "{systemctl} is-enabled zz-not-existing.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 1) self.assertFalse(greps(out, r"^static")) self.assertEqual(len(lines(out)), 0) cmd = "{systemctl} is-enabled zz-not-existing-service.service zzc.service" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 1) self.assertTrue(greps(out, r"^disabled")) self.assertFalse(greps(out, r"^enabled")) self.assertEqual(len(lines(out)), 1) self.assertTrue(greps(err, "Unit zz-not-existing-service.service could not be found.")) # cmd = "{systemctl} --no-legend mask zz-not-existing-service.service" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zz-not-existing-service.service could not be found.")) # cmd = "{systemctl} --no-legend unmask zz-not-existing-service.service" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zz-not-existing-service.service could not be found.")) # self.rm_testdir() self.coverage() self.end() def test_3201_missing_environment_file_makes_service_ignored(self): """ check that a missing EnvironmentFile spec makes the service to be ignored""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=simple EnvironmentFile=/foo.conf ExecStart={bindir}/{testsleep} 111 [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) # start_service = "{systemctl} start zzz.service -vv" end = sx____(start_service.format(**locals())) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) self.assertGreater(end, 0) # stop_service = "{systemctl} stop zzz.service -vv" end = sx____(stop_service.format(**locals())) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) self.assertGreater(end, 0) # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_3211_environment_files_are_included(self): """ check that environment specs are read correctly""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) logfile = os_path(root, "/var/log/test.log") systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=simple EnvironmentFile=/etc/sysconfig/zzz.conf Environment=CONF4=dd4 ExecStart={bindir}/zzz.sh ExecStop=/usr/bin/killall {testsleep} [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zzz.sh"),""" #! /bin/sh echo "WITH CONF1=$CONF1" >> {logfile} echo "WITH CONF2=$CONF2" >> {logfile} echo "WITH CONF3=$CONF3" >> {logfile} echo "WITH CONF4=$CONF4" >> {logfile} {bindir}/{testsleep} 4 """.format(**locals())) text_file(os_path(testdir, "zzz.conf"),""" CONF1=aa1 CONF2="bb2" CONF3='cc3' """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_tool(os_path(testdir, "zzz.sh"), os_path(bindir, "zzz.sh")) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) copy_file(os_path(testdir, "zzz.conf"), os_path(root, "/etc/sysconfig/zzz.conf")) # start_service = "{systemctl} start zzz.service -vv" end = sx____(start_service.format(**locals())) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) self.assertEqual(end, 0) # log = lines(open(logfile)) logg.info("LOG \n| %s", "\n| ".join(log)) self.assertTrue(greps(log, "WITH CONF1=aa1")) self.assertTrue(greps(log, "WITH CONF2=bb2")) self.assertTrue(greps(log, "WITH CONF3=cc3")) self.assertTrue(greps(log, "WITH CONF4=dd4")) os.remove(logfile) # stop_service = "{systemctl} stop zzz.service -vv" end = sx____(stop_service.format(**locals())) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) # self.rm_testdir() self.coverage() self.end() def test_3240_may_expand_environment_variables(self): """ check that different styles of environment variables get expanded.""" self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root print_sh = os_path(root, "/usr/bin/print.sh") logfile = os_path(root, "/var/log/print_sh.log") text_file(os_path(root, "/etc/sysconfig/b.conf"),""" DEF1='def1' DEF2="def2 def3" DEF4="$DEF1 ${DEF2}" DEF5="$DEF1111 def5 ${DEF2222}" """) text_file(os_path(root, "/etc/systemd/system/zzb.service"),""" [Unit] Description=Testing B [Service] Environment=DEF2=foo EnvironmentFile=/etc/sysconfig/b.conf ExecStart=/bin/sleep 3 ExecStartPost=%s A $DEF1 $DEF2 ExecStartPost=%s B ${DEF1} ${DEF2} ExecStartPost=%s C $DEF1$DEF2 ExecStartPost=%s D ${DEF1}${DEF2} ExecStartPost=%s E ${DEF4} ExecStartPost=%s F ${DEF5} [Install] WantedBy=multi-user.target""" % (print_sh, print_sh, print_sh, print_sh, print_sh, print_sh,)) text_file(logfile, "") shell_file(print_sh, """ #! /bin/sh logfile='{logfile}' echo "'$1' '$2' '$3' '$4' '$5'" >> "$logfile" """.format(**locals())) cmd = "{systemctl} environment zzb.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, r"^DEF1=def1")) self.assertTrue(greps(out, r"^DEF2=def2 def3")) # cmd = "{systemctl} start zzb.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) log = lines(open(logfile)) logg.info("LOG \n%s", log) A="'A' 'def1' 'def2' 'def3' ''" # A $DEF1 $DEF2 B="'B' 'def1' 'def2 def3' '' ''" # B ${DEF1} ${DEF2} C="'C' 'def1def2' 'def3' '' ''" # C $DEF1$DEF2 D="'D' 'def1def2 def3' '' '' ''" # D ${DEF1}${DEF2} ??TODO?? E="'E' 'def1 def2 def3' '' '' ''" # E ${DEF4} F="'F' ' def5 ' '' '' ''" # F ${DEF5} self.assertIn(A, log) self.assertIn(B, log) self.assertIn(C, log) self.assertIn(D, log) self.assertIn(E, log) self.assertIn(F, log) # self.rm_testdir() self.coverage() self.end() def test_3250_env_may_expand_special_variables(self): """ check that different flavours for special variables get expanded.""" self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root print_sh = os_path(root, "/usr/bin/print.sh") logfile = os_path(root, "/var/log/print_sh.log") service_file = os_path(root, "/etc/systemd/system/zzb zzc.service") text_file(service_file,""" [Unit] Description=Testing B [Service] Environment=X=x1 Environment="Y=y2 y3" ExecStart=/bin/sleep 3 ExecStartPost=%s A %%N $X ${Y} ExecStartPost=%s B %%n $X ${Y} ExecStartPost=%s C %%f $X ${Y} ExecStartPost=%s D %%t $X ${Y} ExecStartPost=%s E %%P $X ${Y} ExecStartPost=%s F %%p $X ${Y} ExecStartPost=%s G %%I $X ${Y} ExecStartPost=%s H %%i $X ${Y} $FOO ExecStartPost=%s T %%T $X ${Y} ExecStartPost=%s V %%V $X ${Y} ExecStartPost=%s Z %%Z $X ${Y} ${FOO} [Install] WantedBy=multi-user.target""" % (print_sh, print_sh, print_sh, print_sh, print_sh, print_sh, print_sh, print_sh, print_sh, print_sh, print_sh)) text_file(logfile, "") shell_file(print_sh, """ #! /bin/sh logfile='{logfile}' echo "'$1' '$2' '$3' '$4' '$5'" >> "$logfile" """.format(**locals())) # RUN = "/run" # for system-mode cmd = "{systemctl} start 'zzb zzc.service' -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) log = lines(open(logfile)) logg.info("LOG \n%s", log) A="'A' 'zzb' 'zzc.service' 'x1' 'y2 y3'" # A %%N B="'B' 'zzb zzc.service' 'x1' 'y2 y3' ''" # B %%n C="'C' '%s' 'x1' 'y2 y3' ''" % service_file # C %%f D="'D' '%s' 'x1' 'y2 y3' ''" % os_path(root, RUN) # D %%t E="'E' 'zzb' 'zzc' 'x1' 'y2 y3'" # E %%P F="'F' 'zzb zzc' 'x1' 'y2 y3' ''" # F %%p G="'G' 'x1' 'y2 y3' '' ''" # G %%I H="'H' '' 'x1' 'y2 y3' ''" # H %%i T="'T' '%s' 'x1' 'y2 y3' ''" % os_path(root, "/tmp") # T %%T V="'V' '%s' 'x1' 'y2 y3' ''" % os_path(root, "/var/tmp") # V %%V Z="'Z' '' 'x1' 'y2 y3' ''" # Z %%Z self.assertIn(A, log) self.assertIn(B, log) self.assertIn(C, log) self.assertIn(D, log) self.assertIn(E, log) self.assertIn(F, log) self.assertIn(G, log) self.assertIn(H, log) self.assertIn(T, log) self.assertIn(V, log) self.assertIn(Z, log) # self.rm_testdir() self.coverage() self.end() def test_3260_user_mode_env_may_expand_special_variables(self): """ check that different flavours for special variables get expanded. Differently in --user mode.""" self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root print_sh = os_path(root, "/usr/bin/print.sh") logfile = os_path(root, "/var/log/print_sh.log") service_file = os_path(root, "/etc/systemd/user/zzb zzc.service") text_file(service_file,""" [Unit] Description=Testing B [Service] Environment=X=x1 Environment="Y=y2 y3" ExecStart=/bin/sleep 3 ExecStartPost=%s A %%N $X ${Y} ExecStartPost=%s B %%n $X ${Y} ExecStartPost=%s C %%f $X ${Y} ExecStartPost=%s D %%t $X ${Y} ExecStartPost=%s E %%P $X ${Y} ExecStartPost=%s F %%p $X ${Y} ExecStartPost=%s G %%I $X ${Y} ExecStartPost=%s H %%i $X ${Y} $FOO ExecStartPost=%s T %%T $X ${Y} ExecStartPost=%s V %%V $X ${Y} ExecStartPost=%s Z %%Z $X ${Y} ${FOO} [Install] WantedBy=multi-user.target""" % (print_sh, print_sh, print_sh, print_sh, print_sh, print_sh, print_sh, print_sh, print_sh, print_sh, print_sh)) text_file(logfile, "") shell_file(print_sh, """ #! /bin/sh logfile='{logfile}' echo "'$1' '$2' '$3' '$4' '$5'" >> "$logfile" """.format(**locals())) # RUN = "/run" # for system-mode RUN = os.environ.get("XDG_RUNTIME_DIR") or "/tmp/run-"+os_getlogin() cmd = "{systemctl} --user start 'zzb zzc.service' -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) log = lines(open(logfile)) logg.info("LOG \n%s", log) A="'A' 'zzb' 'zzc.service' 'x1' 'y2 y3'" # A %%N B="'B' 'zzb zzc.service' 'x1' 'y2 y3' ''" # B %%n C="'C' '%s' 'x1' 'y2 y3' ''" % service_file # C %%f D="'D' '%s' 'x1' 'y2 y3' ''" % os_path(root, RUN) # D %%t E="'E' 'zzb' 'zzc' 'x1' 'y2 y3'" # E %%P F="'F' 'zzb zzc' 'x1' 'y2 y3' ''" # F %%p G="'G' 'x1' 'y2 y3' '' ''" # G %%I H="'H' '' 'x1' 'y2 y3' ''" # H %%i T="'T' '%s' 'x1' 'y2 y3' ''" % os_path(root, "/tmp") # T %%T V="'V' '%s' 'x1' 'y2 y3' ''" % os_path(root, "/var/tmp") # V %%V Z="'Z' '' 'x1' 'y2 y3' ''" # Z %%Z self.assertIn(A, log) self.assertIn(B, log) self.assertIn(C, log) self.assertIn(D, log) self.assertIn(E, log) self.assertIn(F, log) self.assertIn(G, log) self.assertIn(H, log) self.assertIn(T, log) self.assertIn(V, log) self.assertIn(Z, log) # self.rm_testdir() self.coverage() self.end() def test_3270_may_override_environment_from_commandline(self): """ check that --extra-vars can be given on the commandline to override settings in Environment= and EnvironmentFile=.""" self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root print_sh = os_path(root, "/usr/bin/print.sh") logfile = os_path(root, "/var/log/print_sh.log") service_file = os_path(root, "/etc/systemd/system/zzb zzc.service") env_file = "/etc/sysconfig/my.conf" extra_vars_file = "/etc/sysconfig/extra.conf" env_text_file = os_path(root, env_file) extra_vars_text_file = os_path(root, extra_vars_file) text_file(env_text_file,""" M="emm a" N='enn i' """) text_file(extra_vars_text_file,""" R="rob o" Y='knew it' """) text_file(service_file,""" [Unit] Description=Testing B [Service] Environment=X=x1 Environment="Y=y2 y3" EnvironmentFile=%s ExecStart=/bin/sleep 3 ExecStartPost=%s X: $X ${X} ExecStartPost=%s Y: $Y ${Y} ExecStartPost=%s M: $M ${M} ExecStartPost=%s N: $N ${N} ExecStartPost=%s R: $R ${R} ExecStartPost=%s S: $S ${S} ExecStartPost=%s T: $T ${T} [Install] WantedBy=multi-user.target""" % (env_file, print_sh, print_sh, print_sh, print_sh, print_sh, print_sh, print_sh, )) text_file(logfile, "") shell_file(print_sh, """ #! /bin/sh logfile='{logfile}' echo "'$1' '$2' '$3' '$4' '$5'" >> "$logfile" """.format(**locals())) # cmd = "{systemctl} start 'zzb zzc.service' -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) log = lines(open(logfile)) logg.info("LOG \n%s", log) X="'X:' 'x1' 'x1' '' ''" # Y="'Y:' 'y2' 'y3' 'y2 y3' ''" M="'M:' 'emm' 'a' 'emm a' ''" N="'N:' 'enn' 'i' 'enn i' ''" R="'R:' '' '' '' ''" S="'S:' '' '' '' ''" T="'T:' '' '' '' ''" self.assertIn(X, log) self.assertIn(Y, log) self.assertIn(M, log) self.assertIn(N, log) self.assertIn(R, log) self.assertIn(S, log) self.assertIn(T, log) # cmd = "{systemctl} stop 'zzb zzc.service'" out, end = output2(cmd.format(**locals())) time.sleep(1) cmd = "{systemctl} start 'zzb zzc.service' -vv -e X=now --environment 'M=more N=from' --extra-vars @" + extra_vars_file out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) log = lines(open(logfile)) logg.info("LOG \n%s", log) X="'X:' 'now' 'now' '' ''" # Y="'Y:' 'knew' 'it' 'knew it' ''" M="'M:' 'more' 'more' '' ''" N="'N:' 'from' 'from' '' ''" R="'R:' 'rob' 'o' 'rob o' ''" S="'S:' '' '' '' ''" T="'T:' '' '' '' ''" self.assertIn(X, log) self.assertIn(Y, log) self.assertIn(M, log) self.assertIn(N, log) self.assertIn(R, log) self.assertIn(S, log) self.assertIn(T, log) # self.rm_testdir() self.coverage() self.end() def test_3301_service_config_show(self): """ check that a named service config can show its properties""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) logfile = os_path(root, "/var/log/test.log") systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zzs.service"),""" [Unit] Description=Testing S After=foo.service [Service] Type=simple ExecStart={bindir}/{testsleep} 99 ExecStop=/usr/bin/killall {testsleep} [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zzs.service"), os_path(root, "/etc/systemd/system/zzs.service")) # cmd = "{systemctl} show zzs.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) data = lines(out) self.assertTrue(greps(data, "Id=zzs.service")) self.assertTrue(greps(data, "Names=zzs.service")) self.assertTrue(greps(data, "Description=Testing")) self.assertTrue(greps(data, "MainPID=0")) self.assertTrue(greps(data, "SubState=dead")) self.assertTrue(greps(data, "ActiveState=inactive")) self.assertTrue(greps(data, "LoadState=loaded")) self.assertTrue(greps(data, "UnitFileState=disabled")) self.assertEqual(end, 0) # cmd = "{systemctl} enable zzs.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} show zzs.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) data = lines(out) self.assertTrue(greps(data, "Id=zzs.service")) self.assertTrue(greps(data, "Names=zzs.service")) self.assertTrue(greps(data, "Description=Testing")) self.assertTrue(greps(data, "MainPID=0")) self.assertTrue(greps(data, "SubState=dead")) self.assertTrue(greps(data, "ActiveState=inactive")) self.assertTrue(greps(data, "LoadState=loaded")) self.assertTrue(greps(data, "UnitFileState=enabled")) # <<< self.assertEqual(end, 0) # cmd = "{systemctl} start zzs.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} show zzs.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) data = lines(out) self.assertTrue(greps(data, "Id=zzs.service")) self.assertTrue(greps(data, "Names=zzs.service")) self.assertTrue(greps(data, "Description=Testing")) self.assertTrue(greps(data, "MainPID=[123456789][1234567890]*")) # <<<< self.assertTrue(greps(data, "SubState=running")) # <<< self.assertTrue(greps(data, "ActiveState=active")) # <<<< self.assertTrue(greps(data, "LoadState=loaded")) self.assertTrue(greps(data, "UnitFileState=enabled")) self.assertEqual(end, 0) # # cleanup kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_3302_service_config_show_single_properties(self): """ check that a named service config can show a single properties""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) logfile = os_path(root, "/var/log/test.log") systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zzs.service"),""" [Unit] Description=Testing S After=foo.service [Service] Type=simple ExecStart={bindir}/{testsleep} 99 ExecStop=/usr/bin/killall {testsleep} [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zzs.service"), os_path(root, "/etc/systemd/system/zzs.service")) # cmd = "{systemctl} show zzs.service -vv -p ActiveState" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) data = lines(out) self.assertTrue(greps(data, "ActiveState=inactive")) self.assertEqual(len(data), 1) self.assertEqual(end, 0) # cmd = "{systemctl} start zzs.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} show zzs.service -vv -p ActiveState" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) data = lines(out) self.assertTrue(greps(data, "ActiveState=active")) self.assertEqual(len(data), 1) self.assertEqual(end, 0) # cmd = "{systemctl} show zzs.service -vv -p 'MainPID'" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) data = lines(out) self.assertTrue(greps(data, "MainPID=[123456789][1234567890]*")) # <<<< self.assertEqual(len(data), 1) self.assertEqual(end, 0) # # cleanup kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_3303_service_config_show_single_properties_plus_unknown(self): """ check that a named service config can show a single properties""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) logfile = os_path(root, "/var/log/test.log") systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zzs.service"),""" [Unit] Description=Testing S After=foo.service [Service] Type=simple ExecStart={bindir}/{testsleep} 99 ExecStop=/usr/bin/killall {testsleep} [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zzs.service"), os_path(root, "/etc/systemd/system/zzs.service")) # cmd = "{systemctl} show zzs.service -vv -p ActiveState" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) data = lines(out) self.assertTrue(greps(data, "ActiveState=inactive")) self.assertEqual(len(data), 1) self.assertEqual(end, 0) # cmd = "{systemctl} start zzs.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} show zzs.service other.service -vv -p ActiveState" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) data = lines(out) self.assertTrue(greps(data, "ActiveState=active")) self.assertEqual(len(data), 3) # cmd = "{systemctl} show zzs.service other.service -vv -p 'MainPID'" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) data = lines(out) self.assertTrue(greps(data, "MainPID=[123456789][1234567890]*")) # <<<< self.assertEqual(len(data), 3) # # cleanup kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_3401_service_status_show(self): """ check that a named service config can show its status""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) logfile = os_path(root, "/var/log/test.log") systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zzs.service"),""" [Unit] Description=Testing S After=foo.service [Service] Type=simple ExecStart={bindir}/{testsleep} 99 ExecStop=/usr/bin/killall {testsleep} [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zzs.service"), os_path(root, "/etc/systemd/system/zzs.service")) # cmd = "{systemctl} status zzs.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertNotEqual(end, 0) data = lines(out) self.assertTrue(greps(data, "zzs.service - Testing")) self.assertTrue(greps(data, "Loaded: loaded")) self.assertTrue(greps(data, "Active: inactive")) self.assertTrue(greps(data, "[(]dead[)]")) self.assertTrue(greps(data, "disabled[)]")) # cmd = "{systemctl} enable zzs.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} start zzs.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} status zzs.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) data = lines(out) self.assertTrue(greps(data, "zzs.service - Testing")) self.assertTrue(greps(data, "Loaded: loaded")) self.assertTrue(greps(data, "Active: active")) self.assertTrue(greps(data, "[(]running[)]")) self.assertTrue(greps(data, "enabled[)]")) # # cleanup kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_3403_service_status_show_plus_unknown(self): """ check that a named service config can show its status""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) logfile = os_path(root, "/var/log/test.log") systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zzs.service"),""" [Unit] Description=Testing S After=foo.service [Service] Type=simple ExecStart={bindir}/{testsleep} 99 ExecStop=/usr/bin/killall {testsleep} [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zzs.service"), os_path(root, "/etc/systemd/system/zzs.service")) # cmd = "{systemctl} status zzs.service other.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertNotEqual(end, 0) data = lines(out) self.assertTrue(greps(data, "zzs.service - Testing")) self.assertTrue(greps(data, "Loaded: loaded")) self.assertTrue(greps(data, "Active: inactive")) self.assertTrue(greps(data, "[(]dead[)]")) self.assertTrue(greps(data, "disabled[)]")) # cmd = "{systemctl} enable zzs.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} start zzs.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} status zzs.service other.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertNotEqual(end, 0) data = lines(out) self.assertTrue(greps(data, "zzs.service - Testing")) self.assertTrue(greps(data, "Loaded: loaded")) self.assertTrue(greps(data, "Active: active")) self.assertTrue(greps(data, "[(]running[)]")) self.assertTrue(greps(data, "enabled[)]")) # # cleanup kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_3530_systemctl_py_default_workingdirectory_is_root(self): """ check that services without WorkingDirectory start in / """ self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) logfile = os_path(root, "/var/log/test.log") systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=simple ExecStart={bindir}/zzz.sh ExecStop=/usr/bin/killall {testsleep} [Install] WantedBy=multi-user.target """.format(**locals())) shell_file(os_path(testdir, "zzz.sh"),""" #! /bin/sh log={logfile} date > "$log" pwd >> "$log" exec {bindir}/{testsleep} 111 """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) copy_tool(os_path(testdir, "zzz.sh"), os_path(root, "/usr/bin/zzz.sh")) # cmd = "{systemctl} start zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) # log = lines(open(logfile).read()) logg.info("LOG %s\n| %s", logfile, "\n| ".join(log)) self.assertIn(root, log) # <<<<<<<<<< CHECK # cmd = "{systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_3531_systemctl_py_simple_in_workingdirectory(self): """ check that we can start simple services with a WorkingDirectory""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) logfile = os_path(root, "/var/log/test.log") systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") workingdir = "/var/testsleep" text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=simple WorkingDirectory={workingdir} ExecStart={bindir}/zzz.sh ExecStop=/usr/bin/killall {testsleep} [Install] WantedBy=multi-user.target """.format(**locals())) shell_file(os_path(testdir, "zzz.sh"),""" #! /bin/sh log={logfile} date > "$log" pwd >> "$log" exec {bindir}/{testsleep} 111 """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) copy_tool(os_path(testdir, "zzz.sh"), os_path(root, "/usr/bin/zzz.sh")) os.makedirs(os_path(root, workingdir)) # cmd = "{systemctl} start zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) # log = lines(open(logfile).read()) logg.info("LOG %s\n| %s", logfile, "\n| ".join(log)) self.assertIn(os_path(root,workingdir), log) # <<<<<<<<<< CHECK # cmd = "{systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_3532_systemctl_py_with_bad_workingdirectory(self): """ check that we can start simple services with a bad WorkingDirectory""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) logfile = os_path(root, "/var/log/test.log") systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") workingdir = "/var/testsleep" text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=simple WorkingDirectory={workingdir} ExecStart={bindir}/zzz.sh ExecStop=/usr/bin/killall {testsleep} [Install] WantedBy=multi-user.target """.format(**locals())) shell_file(os_path(testdir, "zzz.sh"),""" #! /bin/sh log={logfile} date > "$log" pwd >> "$log" exec {bindir}/{testsleep} 111 """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) copy_tool(os_path(testdir, "zzz.sh"), os_path(root, "/usr/bin/zzz.sh")) # os.makedirs(os_path(root, workingdir)) <<< # cmd = "{systemctl} start zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 1) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) # cmd = "{systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 1) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_3533_systemctl_py_with_bad_workingdirectory(self): """ check that we can start simple services with a bad WorkingDirectory with '-'""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) logfile = os_path(root, "/var/log/test.log") systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") workingdir = "/var/testsleep" text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=simple WorkingDirectory=-{workingdir} ExecStart={bindir}/zzz.sh ExecStop=/usr/bin/killall {testsleep} [Install] WantedBy=multi-user.target """.format(**locals())) shell_file(os_path(testdir, "zzz.sh"),""" #! /bin/sh log={logfile} date > "$log" pwd >> "$log" exec {bindir}/{testsleep} 111 """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) copy_tool(os_path(testdir, "zzz.sh"), os_path(root, "/usr/bin/zzz.sh")) # os.makedirs(os_path(root, workingdir)) <<< # cmd = "{systemctl} start zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) # log = lines(open(logfile).read()) logg.info("LOG %s\n| %s", logfile, "\n| ".join(log)) self.assertNotIn(os_path(root,workingdir), log) # <<<<<<<<<< CHECK self.assertIn(root, log) # cmd = "{systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_3601_non_absolute_ExecStopPost(self): """ check that we get a strong warning when not using absolute paths in ExecCommands""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=simple ExecStart={bindir}/{testsleep} 111 ExecStop=/usr/bin/killall {testsleep} ExecStopPost=killall {testsleep} [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) # cmd = "{systemctl} start zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} stop zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, err, out) self.assertTrue(greps(err, "Exec is not an absolute")) # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_3602_non_absolute_ExecStop(self): """ check that we get a strong warning when not using absolute paths in ExecCommands""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) quick = "--coverage=quick" systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=simple ExecStart={bindir}/{testsleep} 111 ExecStop=killall {testsleep} [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) # cmd = "{systemctl} start zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} stop zzz.service -vv {quick}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, err, out) self.assertTrue(greps(err, "Exec is not an absolute")) # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_3603_non_absolute_ExecReload(self): """ check that we get a strong warning when not using absolute paths in ExecCommands""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=simple ExecStart={bindir}/{testsleep} 111 ExecReload=killall {testsleep} [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) # cmd = "{systemctl} start zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} reload zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, err, out) self.assertTrue(greps(err, "Exec is not an absolute")) # cmd = "{systemctl} stop zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, err, out) # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_3604_non_absolute_ExecStartPost(self): """ check that we get a strong warning when not using absolute paths in ExecCommands""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=simple ExecStart={bindir}/{testsleep} 111 ExecStartPost=echo OK [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) # cmd = "{systemctl} start zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, err, out) self.assertEqual(end, 0) self.assertTrue(greps(err, "Exec is not an absolute")) # cmd = "{systemctl} stop zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, err, out) # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_3605_non_absolute_ExecStartPre(self): """ check that we get a strong warning when not using absolute paths in ExecCommands""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=simple ExecStartPre=echo OK ExecStart={bindir}/{testsleep} 111 [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) # cmd = "{systemctl} start zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Exec is not an absolute")) # cmd = "{systemctl} stop zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, err, out) # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_3606_non_absolute_ExecStart(self): """ check that we get a strong warning when not using absolute paths in ExecCommands""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=simple ExecStart=sleep 111 TimeoutSec=10 [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) # cmd = "{systemctl} start zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Exec is not an absolute")) # cmd = "{systemctl} stop zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, err, out) # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def real_3609_exitcode_from_ExecReload(self): self.test_3609_exitcode_from_ExecReload(True) def test_3609_exitcode_from_ExecReload(self, real = False): """ check that we get a warning when ExecReload has an error""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir, real) vv = "-vv" systemctl = cover() + _systemctl_py + " --root=" + root if real: vv, systemctl = "", "/usr/bin/systemctl" testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=simple ExecStart={bindir}/{testsleep} 111 ExecReload=/usr/bin/killall -q some-unknown-program [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) cmd = "{systemctl} start zzz.service {vv}" sx____("{systemctl} daemon-reload".format(**locals())) # cmd = "{systemctl} start zzz.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} reload zzz.service {vv}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, err, out) self.assertTrue(greps(err, "Job for zzz.service failed because the control process exited with error code.")) # cmd = "{systemctl} is-active zzz.service {vv}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, err, out) # cmd = "{systemctl} stop zzz.service {vv}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, err, out) # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.rm_zzfiles(root) self.coverage() self.end() def test_3700_systemctl_py_default_init_loop_in_testenv(self): """ check that we can enable services in a test env to be run by an init-loop. We expect here that the init-loop ends when all services are dead. """ self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStartPre=/bin/echo starting B ExecStart={bindir}/{testsleep} 10 ExecStartPost=/bin/echo running B ExecStopPost=/bin/echo stopping B [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C [Service] Type=simple ExecStartPre=/bin/echo starting C ExecStart={bindir}/{testsleep} 15 ExecStartPost=/bin/echo running C ExecStopPost=/bin/echo stopping C [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zza.service"), os_path(root, "/etc/systemd/system/zza.service")) copy_file(os_path(testdir, "zzb.service"), os_path(root, "/etc/systemd/system/zzb.service")) copy_file(os_path(testdir, "zzc.service"), os_path(root, "/etc/systemd/system/zzc.service")) # cmd = "{systemctl} enable zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} enable zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} --version" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} default-services -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, "zzb.service")) self.assertEqual(len(lines(out)), 2) # log_stdout = os.path.join(root, "systemctl.stdout.log") log_stderr = os.path.join(root, "systemctl.stderr.log") pid = os.fork() if not pid: new_stdout = os.open(log_stdout, os.O_WRONLY|os.O_CREAT|os.O_TRUNC) new_stderr = os.open(log_stderr, os.O_WRONLY|os.O_CREAT|os.O_TRUNC) os.dup2(new_stdout, 1) os.dup2(new_stderr, 2) systemctl_cmd = [ _systemctl_py, "--root="+root, "--init", "default", "-vv" ] env = os.environ.copy() env["SYSTEMCTL_EXIT_WHEN_NO_MORE_SERVICES"] = "yes" env["SYSTEMCTL_INITLOOP"] = "2" os.execve(_systemctl_py, systemctl_cmd, env) time.sleep(2) logg.info("all services running [systemctl.py PID %s]", pid) txt_stdout = lines(open(log_stdout)) txt_stderr = lines(open(log_stderr)) logg.info("-- %s>\n\t%s", log_stdout, "\n\t".join(txt_stdout)) logg.info("-- %s>\n\t%s", log_stderr, "\n\t".join(txt_stderr)) # top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzb.service zzc.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, "^active")) self.assertFalse(greps(out, "inactive")) self.assertFalse(greps(out, "failed")) for check in xrange(9): time.sleep(3) top = _recent(output(_top_list)) logg.info("[%s] checking for testsleep procs: \n>>>\n%s", check, greps(top, testsleep)) if not greps(top, testsleep): break time.sleep(2) logg.info("all services dead [systemctl.py PID %s]", pid) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zzb.service zzc.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 3) self.assertFalse(greps(out, "^active")) self.assertTrue(greps(out, "inactive")) self.assertFalse(greps(out, "failed")) # os.kill(pid, 2) # SIGINT (clean up zombie?) txt_stdout = lines(open(log_stdout)) txt_stderr = lines(open(log_stderr)) logg.info("-- %s>\n\t%s", log_stdout, "\n\t".join(txt_stdout)) logg.info("-- %s>\n\t%s", log_stderr, "\n\t".join(txt_stderr)) self.assertTrue(greps(txt_stderr, "no more services - exit init-loop")) self.assertTrue(greps(txt_stderr, "system is down")) self.assertTrue(greps(txt_stdout, "starting B")) self.assertTrue(greps(txt_stdout, "starting C")) # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_3710_systemctl_py_init_explicit_loop_in_testenv(self): """ check that we can init services in a test env to be run by an init-loop. We expect here that the init-loop ends when those services are dead. """ self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStartPre=/bin/echo starting B ExecStart={bindir}/{testsleep} 10 ExecStartPost=/bin/echo running B ExecStopPost=/bin/echo stopping B [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C [Service] Type=simple ExecStartPre=/bin/echo starting C ExecStart={bindir}/{testsleep} 15 ExecStartPost=/bin/echo running C ExecStopPost=/bin/echo stopping C [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zza.service"), os_path(root, "/etc/systemd/system/zza.service")) copy_file(os_path(testdir, "zzb.service"), os_path(root, "/etc/systemd/system/zzb.service")) copy_file(os_path(testdir, "zzc.service"), os_path(root, "/etc/systemd/system/zzc.service")) # cmd = "{systemctl} enable zzb.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} enable zzc.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} --version" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} default-services -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, "zzb.service")) self.assertEqual(len(lines(out)), 2) # log_stdout = os.path.join(root, "systemctl.stdout.log") log_stderr = os.path.join(root, "systemctl.stderr.log") pid = os.fork() if not pid: new_stdout = os.open(log_stdout, os.O_WRONLY|os.O_CREAT|os.O_TRUNC) new_stderr = os.open(log_stderr, os.O_WRONLY|os.O_CREAT|os.O_TRUNC) os.dup2(new_stdout, 1) os.dup2(new_stderr, 2) systemctl_cmd = [ _systemctl_py, "--root="+root, "init", "zzb.service", "zzc.service", "-vv" ] env = os.environ.copy() env["SYSTEMCTL_INITLOOP"] = "2" os.execve(_systemctl_py, systemctl_cmd, env) time.sleep(3) logg.info("all services running [systemctl.py PID %s]", pid) txt_stdout = lines(open(log_stdout)) txt_stderr = lines(open(log_stderr)) logg.info("-- %s>\n\t%s", log_stdout, "\n\t".join(txt_stdout)) logg.info("-- %s>\n\t%s", log_stderr, "\n\t".join(txt_stderr)) # top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzb.service zzc.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) self.assertTrue(greps(out, "^active")) self.assertFalse(greps(out, "inactive")) self.assertFalse(greps(out, "failed")) for check in xrange(9): time.sleep(3) top = _recent(output(_top_list)) logg.info("[%s] checking for testsleep procs: \n>>>\n%s", check, greps(top, testsleep)) if not greps(top, testsleep): break time.sleep(2) logg.info("all services dead [systemctl.py PID %s]", pid) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zzb.service zzc.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 3) self.assertFalse(greps(out, "^active")) self.assertTrue(greps(out, "inactive")) self.assertFalse(greps(out, "failed")) # os.kill(pid, 2) # SIGINT (clean up zombie?) txt_stdout = lines(open(log_stdout)) txt_stderr = lines(open(log_stderr)) logg.info("-- %s>\n\t%s", log_stdout, "\n\t".join(txt_stdout)) logg.info("-- %s>\n\t%s", log_stderr, "\n\t".join(txt_stderr)) self.assertTrue(greps(txt_stderr, "no more services - exit init-loop")) self.assertTrue(greps(txt_stderr, "init is done")) self.assertTrue(greps(txt_stdout, "starting B")) self.assertTrue(greps(txt_stdout, "starting C")) # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_3801_start_some_unknown(self): """ check start some unknown unit fails okay""" self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root # cmd = "{systemctl} start zz-unknown.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zz-unknown.service could not be found.")) # self.rm_testdir() self.coverage() self.end() def test_3802_stop_some_unknown(self): """ check stop some unknown unit fails okay""" self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root # cmd = "{systemctl} stop zz-unknown.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zz-unknown.service could not be found.")) # self.rm_testdir() self.coverage() self.end() def test_3803_restart_some_unknown(self): """ check restart some unknown unit fails okay""" self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root # cmd = "{systemctl} restart zz-unknown.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zz-unknown.service could not be found.")) # self.rm_testdir() self.coverage() self.end() def test_3804_reload_some_unknown(self): """ check reload some unknown unit fails okay""" self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root # cmd = "{systemctl} reload zz-unknown.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zz-unknown.service could not be found.")) # self.rm_testdir() self.coverage() self.end() def test_3805_reload_or_restart_some_unknown(self): """ check reload-or-restart some unknown unit fails okay""" self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root # cmd = "{systemctl} reload-or-restart zz-unknown.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zz-unknown.service could not be found.")) # self.rm_testdir() self.coverage() self.end() def test_3806_reload_or_try_restart_some_unknown(self): """ check reload-or-try-restart some unknown unit fails okay""" self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root # cmd = "{systemctl} reload-or-try-restart zz-unknown.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zz-unknown.service could not be found.")) # self.rm_testdir() self.coverage() self.end() def test_3807_try_restart_some_unknown(self): """ check try-restart some unknown unit fails okay""" self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root # cmd = "{systemctl} try-restart zz-unknown.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zz-unknown.service could not be found.")) # self.rm_testdir() self.coverage() self.end() def test_3808_kill_some_unknown(self): """ check kill some unknown unit fails okay""" self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root # cmd = "{systemctl} kill zz-unknown.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zz-unknown.service could not be found.")) # self.rm_testdir() self.coverage() self.end() def test_3809_reset_failed_some_unknown(self): """ check reset-failed some unknown unit fails okay""" self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root # cmd = "{systemctl} reset-failed zz-unknown.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zz-unknown.service could not be found.")) # self.rm_testdir() self.coverage() self.end() def test_3811_mask_some_unknown(self): """ check mask some unknown unit fails okay""" self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root # cmd = "{systemctl} mask zz-unknown.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zz-unknown.service could not be found.")) # self.rm_testdir() self.coverage() self.end() def test_3812_unmask_some_unknown(self): """ check unmask some unknown unit fails okay""" self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root # cmd = "{systemctl} unmask zz-unknown.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zz-unknown.service could not be found.")) # self.rm_testdir() self.coverage() self.end() def test_3813_enable_some_unknown(self): """ check enable some unknown unit fails okay""" self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root # cmd = "{systemctl} enable zz-unknown.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zz-unknown.service could not be found.")) # self.rm_testdir() self.coverage() self.end() def test_3814_disable_some_unknown(self): """ check disable some unknown unit fails okay""" self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root # cmd = "{systemctl} disable zz-unknown.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zz-unknown.service could not be found.")) # self.rm_testdir() self.coverage() self.end() def test_3815_is_enabled_some_unknown(self): """ check is-enabled some unknown unit fails okay""" self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root # cmd = "{systemctl} is-enabled zz-unknown.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zz-unknown.service could not be found.")) # self.rm_testdir() self.coverage() self.end() def test_3816_is_failed_some_unknown(self): """ check is-failed some unknown unit fails okay""" self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root # cmd = "{systemctl} is-failed zz-unknown.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zz-unknown.service could not be found.")) # self.rm_testdir() self.coverage() self.end() def test_3817_is_active_some_unknown(self): """ check is-active some unknown unit fails okay""" self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root # cmd = "{systemctl} is-active zz-unknown.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 3) self.assertTrue(greps(err, "Unit zz-unknown.service could not be found.")) # self.rm_testdir() self.coverage() self.end() def test_3818_get_preset_some_unknown(self): """ check get-preset some unknown unit fails okay""" self.skipTest("get-preset currently not exported") self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root # cmd = "{systemctl} get-preset zz-unknown.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zz-unknown.service could not be found.")) # self.rm_testdir() self.coverage() self.end() def test_3819_status_some_unknown(self): """ check get status some unknown unit fails okay""" self.begin() testname = self.testname() testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root # cmd = "{systemctl} status zz-unknown.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 3) self.assertTrue(greps(err, "Unit zz-unknown.service could not be found.")) # self.rm_testdir() self.coverage() self.end() def test_3901_service_config_cat(self): """ check that a name service config can be printed as-is""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) logfile = os_path(root, "/var/log/test.log") systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zzs.service"),""" [Unit] Description=Testing S After=foo.service [Service] Type=simple ExecStart={bindir}{testsleep} 99 ExecStop=/usr/bin/killall {testsleep} [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zzs.service"), os_path(root, "/etc/systemd/system/zzs.service")) # cmd = "{systemctl} cat zzs.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) orig = lines(open(os_path(root, "/etc/systemd/system/zzs.service"))) data = lines(out) self.assertEqual(orig + [""], data) # self.rm_testdir() self.coverage() self.end() def test_3903_service_config_cat_plus_unknown(self): """ check that a name service config can be printed as-is""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) logfile = os_path(root, "/var/log/test.log") systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zzs.service"),""" [Unit] Description=Testing S After=foo.service [Service] Type=simple ExecStart={bindir}{testsleep} 99 ExecStop=/usr/bin/killall {testsleep} [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zzs.service"), os_path(root, "/etc/systemd/system/zzs.service")) # cmd = "{systemctl} cat zzs.service unknown.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 1) orig = lines(open(os_path(root, "/etc/systemd/system/zzs.service"))) data = lines(out) self.assertEqual(orig + [""], data) # self.rm_testdir() self.coverage() self.end() def real_3900_start_false_execpre(self): self.test_3900_start_false_execpre(True) def test_3900_start_false_execpre(self, real = None): """ check that a failed execpre is handled""" vv = self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir, real) logfile = os_path(root, "/var/log/test.log") systemctl = cover() + _systemctl_py + " --root=" + root if real: vv, systemctl = "", "/usr/bin/systemctl" testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") self.rm_testdir() self.rm_zzfiles(root) text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z After=foo.service [Service] Type=simple ExecStartPre="/bin/false" ExecStart={bindir}/{testsleep} 9 ExecStop=/usr/bin/killall {testsleep} [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) sx____("{systemctl} reset-failed zzz.service".format(**locals())) sh____("{systemctl} daemon-reload".format(**locals())) # cmd = "{systemctl} enable zzz.service {vv}" sh____(cmd.format(**locals())) cmd = "{systemctl} is-active zzz.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== try to 'start' the service ") cmd = "{systemctl} start zzz.service {vv}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s\n%s\n%s", cmd, end, out, err) self.assertEqual(end, 1) self.assertTrue(greps(err, "control process exited with error code")) # top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "failed") # sx____("{systemctl} reset-failed zzz.service".format(**locals())) self.rm_testdir() self.rm_zzfiles(root) self.coverage() self.end() def test_4030_simple_service_functions_system(self): """ check that we manage simple services in a root env with commands like start, restart, stop, etc""" self.begin() testname = self.testname() testdir = self.testdir() self.simple_service_functions("system", testname, testdir) self.rm_testdir() self.coverage() self.end() def test_4031_simple_service_functions_user(self): """ check that we manage simple services in a root env with commands like start, restart, stop, etc""" self.begin() testname = self.testname() testdir = self.testdir() self.simple_service_functions("user", testname, testdir) self.rm_testdir() self.coverage() self.end() def simple_service_functions(self, system, testname, testdir): """ check that we manage simple services in a root env with commands like start, restart, stop, etc""" user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root systemctl += " --{system}".format(**locals()) testsleep = testname+"_testsleep" testscript = testname+"_testscript.sh" logfile = os_path(root, "/var/log/test.log") bindir = os_path(root, "/usr/bin") begin = "{" end = "}" text_file(logfile, "") text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=simple ExecStartPre=/bin/echo %n ExecStart={bindir}/{testscript} 111 ExecStartPost=/bin/echo started $MAINPID ExecStop=/bin/kill -3 $MAINPID ExecStopPost=/bin/echo stopped $MAINPID ExecStopPost=/bin/sleep 2 ExecReload=/bin/kill -10 $MAINPID KillSignal=SIGQUIT [Install] WantedBy=multi-user.target """.format(**locals())) shell_file(os_path(bindir, testscript),""" #! /bin/sh date +%T,enter > {logfile} stops () {begin} date +%T,stopping >> {logfile} killall {testsleep} date +%T,stopped >> {logfile} {end} reload () {begin} date +%T,reloading >> {logfile} date +%T,reloaded >> {logfile} {end} trap "stops" 3 # SIGQUIT trap "reload" 10 # SIGUSR1 date +%T,starting >> {logfile} {bindir}/{testsleep} $1 >> {logfile} 2>&1 & while kill -0 $!; do # use 'kill -0' to check the existance of the child date +%T,waiting >> {logfile} # use 'wait' for children AND external signals wait done date +%T,leaving >> {logfile} trap - 3 10 # SIGQUIT SIGUSR1 date +%T,leave >> {logfile} """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) zzz_service = "/etc/systemd/{system}/zzz.service".format(**locals()) copy_file(os_path(testdir, "zzz.service"), os_path(root, zzz_service)) # cmd = "{systemctl} enable zzz.service -vv" sh____(cmd.format(**locals())) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'start' shall start a service that is NOT is-active ") cmd = "{systemctl} start zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") # logg.info("== 'stop' shall stop a service that is-active") cmd = "{systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # # inspect the service's log log = lines(open(logfile)) logg.info("LOG\n %s", "\n ".join(log)) self.assertTrue(greps(log, "enter")) self.assertTrue(greps(log, "leave")) self.assertTrue(greps(log, "starting")) self.assertTrue(greps(log, "stopped")) self.assertFalse(greps(log, "reload")) os.remove(logfile) # logg.info("== 'restart' shall start a service that NOT is-active") cmd = "{systemctl} restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top1= top # # inspect the service's log log = lines(open(logfile)) logg.info("LOG\n %s", "\n ".join(log)) self.assertTrue(greps(log, "enter")) self.assertFalse(greps(log, "leave")) self.assertTrue(greps(log, "starting")) self.assertFalse(greps(log, "stopped")) self.assertFalse(greps(log, "reload")) os.remove(logfile) # logg.info("== 'restart' shall restart a service that is-active") cmd = "{systemctl} restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top2 = top # logg.info("-- and we check that there is a new PID for the service process") def find_pids(ps_output, command): pids = [] for line in _lines(ps_output): if command not in line: continue m = re.match(r"\s*[\d:]*\s+(\S+)\s+(\S+)\s+(.*)", line) pid, ppid, args = m.groups() # logg.info(" %s | %s | %s", pid, ppid, args) pids.append(pid) return pids ps1 = find_pids(top1, testsleep) ps2 = find_pids(top2, testsleep) logg.info("found PIDs %s and %s", ps1, ps2) self.assertTrue(len(ps1), 1) self.assertTrue(len(ps2), 1) self.assertNotEqual(ps1[0], ps2[0]) # # inspect the service's log log = lines(open(logfile)) logg.info("LOG\n %s", "\n ".join(log)) self.assertTrue(greps(log, "enter")) self.assertTrue(greps(log, "starting")) self.assertFalse(greps(log, "reload")) os.remove(logfile) # # logg.info("== 'reload' will NOT restart a service that is-active") cmd = "{systemctl} reload zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top3 = top # logg.info("-- and we check that there is NO new PID for the service process") ps3 = find_pids(top3, testsleep) logg.info("found PIDs %s and %s", ps2, ps3) self.assertTrue(len(ps2), 1) self.assertTrue(len(ps3), 1) self.assertEqual(ps2[0], ps3[0]) # # inspect the service's log log = lines(open(logfile)) logg.info("LOG\n %s", "\n ".join(log)) self.assertFalse(greps(log, "enter")) self.assertFalse(greps(log, "leave")) self.assertFalse(greps(log, "starting")) self.assertFalse(greps(log, "stopped")) self.assertTrue(greps(log, "reload")) os.remove(logfile) # logg.info("== 'reload-or-restart' will restart a service that is-active (if ExecReload)") cmd = "{systemctl} reload-or-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top4 = top # logg.info("-- and we check that there is NO new PID for the service process (if ExecReload)") ps4 = find_pids(top4, testsleep) logg.info("found PIDs %s and %s", ps3, ps4) self.assertTrue(len(ps3), 1) self.assertTrue(len(ps4), 1) self.assertEqual(ps3[0], ps4[0]) # logg.info("== 'kill' will bring is-active non-active as well (when the PID is known)") cmd = "{systemctl} kill zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'stop' will turn 'failed' to 'inactive' (when the PID is known)") cmd = "{systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) # no PID known so 'kill $MAINPID' fails cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'reload-or-try-restart' will not start a not-active service") cmd = "{systemctl} reload-or-try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'try-restart' will not start a not-active service") cmd = "{systemctl} try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'reload-or-restart' will start a not-active service") cmd = "{systemctl} reload-or-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top5 = top # logg.info("== 'reload-or-try-restart' will NOT restart an is-active service (with ExecReload)") cmd = "{systemctl} reload-or-try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top6 = top # logg.info("-- and we check that there is NO new PID for the service process (if ExecReload)") ps5 = find_pids(top5, testsleep) ps6 = find_pids(top6, testsleep) logg.info("found PIDs %s and %s", ps5, ps6) self.assertTrue(len(ps5), 1) self.assertTrue(len(ps6), 1) self.assertEqual(ps5[0], ps6[0]) # logg.info("== 'try-restart' will restart an is-active service") cmd = "{systemctl} try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top7 = top # logg.info("-- and we check that there is a new PID for the service process") ps7 = find_pids(top7, testsleep) logg.info("found PIDs %s and %s", ps6, ps7) self.assertTrue(len(ps6), 1) self.assertTrue(len(ps7), 1) self.assertNotEqual(ps6[0], ps7[0]) # # cleanup kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) time.sleep(1) def test_4032_forking_service_functions_system(self): """ check that we manage forking services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart.""" self.begin() testname = self.testname() testdir = self.testdir() self.forking_service_functions("system", testname, testdir) self.rm_testdir() self.coverage() self.end() def test_4033_forking_service_functions_user(self): """ check that we manage forking services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart.""" self.begin() testname = self.testname() testdir = self.testdir() self.forking_service_functions("user", testname, testdir) self.rm_testdir() self.coverage() self.end() def forking_service_functions(self, system, testname, testdir): user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root systemctl += " --{system}".format(**locals()) testsleep = testname+"_sleep" logfile = os_path(root, "/var/log/"+testsleep+".log") bindir = os_path(root, "/usr/bin") os.makedirs(os_path(root, "/var/run")) text_file(logfile, "created\n") begin = "{" ; end = "}" shell_file(os_path(testdir, "zzz.init"), """ #! /bin/bash logfile={logfile} start() {begin} [ -d /var/run ] || mkdir -p /var/run ({bindir}/{testsleep} 111 0<&- &>/dev/null & echo $! > {root}/var/run/zzz.init.pid ) & wait %1 # ps -o pid,ppid,args {end} stop() {begin} killall {testsleep} {end} case "$1" in start) date "+START.%T" >> $logfile start >> $logfile 2>&1 date "+start.%T" >> $logfile ;; stop) date "+STOP.%T" >> $logfile stop >> $logfile 2>&1 date "+stop.%T" >> $logfile ;; restart) date "+RESTART.%T" >> $logfile stop >> $logfile 2>&1 start >> $logfile 2>&1 date "+.%T" >> $logfile ;; reload) date "+RELOAD.%T" >> $logfile echo "...." >> $logfile 2>&1 date "+reload.%T" >> $logfile ;; esac echo "done$1" >&2 exit 0 """.format(**locals())) text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=forking PIDFile={root}/var/run/zzz.init.pid ExecStart={root}/usr/bin/zzz.init start ExecStop={root}/usr/bin/zzz.init stop [Install] WantedBy=multi-user.target """.format(**locals())) zzz_service = "/etc/systemd/{system}/zzz.service".format(**locals()) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_tool(os_path(testdir, "zzz.init"), os_path(root, "/usr/bin/zzz.init")) copy_file(os_path(testdir, "zzz.service"), os_path(root, zzz_service)) # cmd = "{systemctl} enable zzz.service -vv" sh____(cmd.format(**locals())) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'start' shall start a service that is NOT is-active ") cmd = "{systemctl} start zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") # logg.info("== 'stop' shall stop a service that is-active") cmd = "{systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'restart' shall start a service that NOT is-active") cmd = "{systemctl} restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top1= top # logg.info("== 'restart' shall restart a service that is-active") cmd = "{systemctl} restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top2 = top # logg.info("-- and we check that there is a new PID for the service process") def find_pids(ps_output, command): pids = [] for line in _lines(ps_output): if command not in line: continue m = re.match(r"\s*[\d:]*\s+(\S+)\s+(\S+)\s+(.*)", line) pid, ppid, args = m.groups() # logg.info(" %s | %s | %s", pid, ppid, args) pids.append(pid) return pids ps1 = find_pids(top1, testsleep) ps2 = find_pids(top2, testsleep) logg.info("found PIDs %s and %s", ps1, ps2) self.assertTrue(len(ps1), 1) self.assertTrue(len(ps2), 1) self.assertNotEqual(ps1[0], ps2[0]) # logg.info("== 'reload' will NOT restart a service that is-active") cmd = "{systemctl} reload zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top3 = top # logg.info("-- and we check that there is NO new PID for the service process") ps3 = find_pids(top3, testsleep) logg.info("found PIDs %s and %s", ps2, ps3) self.assertTrue(len(ps2), 1) self.assertTrue(len(ps3), 1) self.assertEqual(ps2[0], ps3[0]) # logg.info("== 'reload-or-restart' will restart a service that is-active (if no ExecReload)") cmd = "{systemctl} reload-or-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top4 = top # logg.info("-- and we check that there is a new PID for the service process (if no ExecReload)") ps4 = find_pids(top4, testsleep) logg.info("found PIDs %s and %s", ps3, ps4) self.assertTrue(len(ps3), 1) self.assertTrue(len(ps4), 1) self.assertNotEqual(ps3[0], ps4[0]) # logg.info("== 'kill' will bring is-active non-active as well (when the PID is known)") cmd = "{systemctl} kill zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "failed") # logg.info("== 'stop' will turn 'failed' to 'inactive' (when the PID is known)") cmd = "{systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'reload-or-try-restart' will not start a not-active service") cmd = "{systemctl} reload-or-try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'try-restart' will not start a not-active service") cmd = "{systemctl} try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'reload-or-restart' will start a not-active service") cmd = "{systemctl} reload-or-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top5 = top # logg.info("== 'reload-or-try-restart' will restart an is-active service (with no ExecReload)") cmd = "{systemctl} try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top6 = top # logg.info("-- and we check that there is a new PID for the service process (if no ExecReload)") ps5 = find_pids(top5, testsleep) ps6 = find_pids(top6, testsleep) logg.info("found PIDs %s and %s", ps5, ps6) self.assertTrue(len(ps5), 1) self.assertTrue(len(ps6), 1) self.assertNotEqual(ps5[0], ps6[0]) # logg.info("== 'try-restart' will restart an is-active service") cmd = "{systemctl} try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top7 = top # logg.info("-- and we check that there is a new PID for the service process") ps7 = find_pids(top7, testsleep) logg.info("found PIDs %s and %s", ps6, ps7) self.assertTrue(len(ps6), 1) self.assertTrue(len(ps7), 1) self.assertNotEqual(ps6[0], ps7[0]) # logg.info("LOG\n%s", " "+open(logfile).read().replace("\n","\n ")) def test_4034_notify_service_functions_system(self): """ check that we manage notify services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart.""" self.begin() if not os.path.exists("/usr/bin/socat"): self.skipTest("missing /usr/bin/socat") testname = self.testname() testdir = self.testdir() self.notify_service_functions("system", testname, testdir) self.rm_testdir() self.coverage() self.end() def test_4035_notify_service_functions_user(self): """ check that we manage notify services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart.""" self.begin() if not os.path.exists("/usr/bin/socat"): self.skipTest("missing /usr/bin/socat") testname = self.testname() testdir = self.testdir() self.notify_service_functions("user", testname, testdir) self.rm_testdir() self.coverage() self.end() def notify_service_functions(self, system, testname, testdir): user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root systemctl += " --{system}".format(**locals()) testsleep = testname+"_sleep" logfile = os_path(root, "/var/log/"+testsleep+".log") bindir = os_path(root, "/usr/bin") os.makedirs(os_path(root, "/var/run")) text_file(logfile, "created\n") begin = "{" ; end = "}" shell_file(os_path(testdir, "zzz.init"), """ #! /bin/bash logfile={logfile} start() {begin} ls -l $NOTIFY_SOCKET {bindir}/{testsleep} 111 0<&- &>/dev/null & echo "MAINPID=$!" | socat -v -d - UNIX-CLIENT:$NOTIFY_SOCKET echo "READY=1" | socat -v -d - UNIX-CLIENT:$NOTIFY_SOCKET wait %1 # ps -o pid,ppid,args {end} stop() {begin} killall {testsleep} {end} case "$1" in start) date "+START.%T" >> $logfile start >> $logfile 2>&1 date "+start.%T" >> $logfile ;; stop) date "+STOP.%T" >> $logfile stop >> $logfile 2>&1 date "+stop.%T" >> $logfile ;; restart) date "+RESTART.%T" >> $logfile stop >> $logfile 2>&1 start >> $logfile 2>&1 date "+.%T" >> $logfile ;; reload) date "+RELOAD.%T" >> $logfile echo "...." >> $logfile 2>&1 date "+reload.%T" >> $logfile ;; esac echo "done$1" >&2 exit 0 """.format(**locals())) text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=notify # PIDFile={root}/var/run/zzz.init.pid ExecStart={root}/usr/bin/zzz.init start ExecStop={root}/usr/bin/zzz.init stop [Install] WantedBy=multi-user.target """.format(**locals())) zzz_service = "/etc/systemd/{system}/zzz.service".format(**locals()) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_tool(os_path(testdir, "zzz.init"), os_path(root, "/usr/bin/zzz.init")) copy_file(os_path(testdir, "zzz.service"), os_path(root, zzz_service)) # cmd = "{systemctl} enable zzz.service -vv" sh____(cmd.format(**locals())) # cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'start' shall start a service that is NOT is-active ") cmd = "{systemctl} start zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") # logg.info("== 'stop' shall stop a service that is-active") cmd = "{systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'restart' shall start a service that NOT is-active") cmd = "{systemctl} restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top1= top # logg.info("== 'restart' shall restart a service that is-active") cmd = "{systemctl} restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top2 = top # logg.info("-- and we check that there is a new PID for the service process") def find_pids(ps_output, command): pids = [] for line in _lines(ps_output): if command not in line: continue m = re.match(r"\s*[\d:]*\s+(\S+)\s+(\S+)\s+(.*)", line) pid, ppid, args = m.groups() # logg.info(" %s | %s | %s", pid, ppid, args) pids.append(pid) return pids ps1 = find_pids(top1, testsleep) ps2 = find_pids(top2, testsleep) logg.info("found PIDs %s and %s", ps1, ps2) self.assertTrue(len(ps1), 1) self.assertTrue(len(ps2), 1) self.assertNotEqual(ps1[0], ps2[0]) # logg.info("== 'reload' will NOT restart a service that is-active") cmd = "{systemctl} reload zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top3 = top # logg.info("-- and we check that there is NO new PID for the service process") ps3 = find_pids(top3, testsleep) logg.info("found PIDs %s and %s", ps2, ps3) self.assertTrue(len(ps2), 1) self.assertTrue(len(ps3), 1) self.assertEqual(ps2[0], ps3[0]) # logg.info("== 'reload-or-restart' will restart a service that is-active (if no ExecReload)") cmd = "{systemctl} reload-or-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top4 = top # logg.info("-- and we check that there is a new PID for the service process (if no ExecReload)") ps4 = find_pids(top4, testsleep) logg.info("found PIDs %s and %s", ps3, ps4) self.assertTrue(len(ps3), 1) self.assertTrue(len(ps4), 1) self.assertNotEqual(ps3[0], ps4[0]) # logg.info("== 'kill' will bring is-active non-active as well (when the PID is known)") cmd = "{systemctl} kill zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'stop' will turn 'failed' to 'inactive' (when the PID is known)") cmd = "{systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'reload-or-try-restart' will not start a not-active service") cmd = "{systemctl} reload-or-try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'try-restart' will not start a not-active service") cmd = "{systemctl} try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'reload-or-restart' will start a not-active service") cmd = "{systemctl} reload-or-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top5 = top # logg.info("== 'reload-or-try-restart' will restart an is-active service (with no ExecReload)") cmd = "{systemctl} try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top6 = top # logg.info("-- and we check that there is a new PID for the service process (if no ExecReload)") ps5 = find_pids(top5, testsleep) ps6 = find_pids(top6, testsleep) logg.info("found PIDs %s and %s", ps5, ps6) self.assertTrue(len(ps5), 1) self.assertTrue(len(ps6), 1) self.assertNotEqual(ps5[0], ps6[0]) # logg.info("== 'try-restart' will restart an is-active service") cmd = "{systemctl} try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top7 = top # logg.info("-- and we check that there is a new PID for the service process") ps7 = find_pids(top7, testsleep) logg.info("found PIDs %s and %s", ps6, ps7) self.assertTrue(len(ps6), 1) self.assertTrue(len(ps7), 1) self.assertNotEqual(ps6[0], ps7[0]) # logg.info("LOG\n%s", " "+open(logfile).read().replace("\n","\n ")) def test_4036_notify_service_functions_with_reload(self): """ check that we manage notify services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart. (with ExecReload)""" self.begin() if not os.path.exists("/usr/bin/socat"): self.skipTest("missing /usr/bin/socat") testname = self.testname() testdir = self.testdir() self.notify_service_functions_with_reload("system", testname, testdir) self.rm_testdir() self.coverage() self.end() def test_4037_notify_service_functions_with_reload_user(self): """ check that we manage notify services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart. (with ExecReload)""" # test_4037 is triggering len(socketfile) > 100 | "new notify socketfile" self.begin() if not os.path.exists("/usr/bin/socat"): self.skipTest("missing /usr/bin/socat") testname = self.testname() testdir = self.testdir() self.notify_service_functions_with_reload("user", testname, testdir) self.rm_testdir() self.coverage() self.end() def notify_service_functions_with_reload(self, system, testname, testdir): user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root systemctl += " --{system}".format(**locals()) testsleep = self.testname("sleep") logfile = os_path(root, "/var/log/"+testsleep+".log") bindir = os_path(root, "/usr/bin") os.makedirs(os_path(root, "/var/run")) text_file(logfile, "created\n") begin = "{" ; end = "}" shell_file(os_path(testdir, "zzz.init"), """ #! /bin/bash logfile={logfile} start() {begin} ls -l $NOTIFY_SOCKET {bindir}/{testsleep} 111 0<&- &>/dev/null & echo "MAINPID=$!" | socat -v -d - UNIX-CLIENT:$NOTIFY_SOCKET echo "READY=1" | socat -v -d - UNIX-CLIENT:$NOTIFY_SOCKET wait %1 # ps -o pid,ppid,args {end} stop() {begin} killall {testsleep} {end} case "$1" in start) date "+START.%T" >> $logfile start >> $logfile 2>&1 date "+start.%T" >> $logfile ;; stop) date "+STOP.%T" >> $logfile stop >> $logfile 2>&1 date "+stop.%T" >> $logfile ;; restart) date "+RESTART.%T" >> $logfile stop >> $logfile 2>&1 start >> $logfile 2>&1 date "+.%T" >> $logfile ;; reload) date "+RELOAD.%T" >> $logfile echo "...." >> $logfile 2>&1 date "+reload.%T" >> $logfile ;; esac echo "done$1" >&2 exit 0 """.format(**locals())) text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=notify # PIDFile={root}/var/run/zzz.init.pid ExecStart={root}/usr/bin/zzz.init start ExecReload={root}/usr/bin/zzz.init reload ExecStop={root}/usr/bin/zzz.init stop TimeoutRestartSec=4 TimeoutReloadSec=4 [Install] WantedBy=multi-user.target """.format(**locals())) zzz_service = "/etc/systemd/{system}/zzz.service".format(**locals()) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_tool(os_path(testdir, "zzz.init"), os_path(root, "/usr/bin/zzz.init")) copy_file(os_path(testdir, "zzz.service"), os_path(root, zzz_service)) # cmd = "{systemctl} enable zzz.service -vv" sh____(cmd.format(**locals())) # cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'start' shall start a service that is NOT is-active ") cmd = "{systemctl} start zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") # logg.info("== 'stop' shall stop a service that is-active") cmd = "{systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'restart' shall start a service that NOT is-active") cmd = "{systemctl} restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top1= top # logg.info("== 'restart' shall restart a service that is-active") cmd = "{systemctl} restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top2 = top # logg.info("-- and we check that there is a new PID for the service process") def find_pids(ps_output, command): pids = [] for line in _lines(ps_output): if command not in line: continue m = re.match(r"\s*[\d:]*\s+(\S+)\s+(\S+)\s+(.*)", line) pid, ppid, args = m.groups() # logg.info(" %s | %s | %s", pid, ppid, args) pids.append(pid) return pids ps1 = find_pids(top1, testsleep) ps2 = find_pids(top2, testsleep) logg.info("found PIDs %s and %s", ps1, ps2) self.assertTrue(len(ps1), 1) self.assertTrue(len(ps2), 1) self.assertNotEqual(ps1[0], ps2[0]) # logg.info("== 'reload' will NOT restart a service that is-active") cmd = "{systemctl} reload zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top3 = top # logg.info("-- and we check that there is NO new PID for the service process") ps3 = find_pids(top3, testsleep) logg.info("found PIDs %s and %s", ps2, ps3) self.assertTrue(len(ps2), 1) self.assertTrue(len(ps3), 1) self.assertEqual(ps2[0], ps3[0]) # logg.info("== 'reload-or-restart' will restart a service that is-active (if no ExecReload)") cmd = "{systemctl} reload-or-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top4 = top # logg.info("-- and we check that there is the same PID for the service process (if ExecReload)") ps4 = find_pids(top4, testsleep) logg.info("found PIDs %s and %s", ps3, ps4) self.assertTrue(len(ps3), 1) self.assertTrue(len(ps4), 1) self.assertEqual(ps3[0], ps4[0]) # logg.info("== 'kill' will bring is-active non-active as well (when the PID is known)") cmd = "{systemctl} kill zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'stop' will turn 'failed' to 'inactive' (when the PID is known)") #TODO# cmd = "{systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'reload-or-try-restart' will not start a not-active service") cmd = "{systemctl} reload-or-try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'try-restart' will not start a not-active service") cmd = "{systemctl} try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'reload-or-restart' will start a not-active service") cmd = "{systemctl} reload-or-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top5 = top # logg.info("== 'reload-or-try-restart' will restart an is-active service (with no ExecReload)") cmd = "{systemctl} try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top6 = top # logg.info("-- and we check that there is a new PID for the service process (if no ExecReload)") ps5 = find_pids(top5, testsleep) ps6 = find_pids(top6, testsleep) logg.info("found PIDs %s and %s", ps5, ps6) self.assertTrue(len(ps5), 1) self.assertTrue(len(ps6), 1) self.assertNotEqual(ps5[0], ps6[0]) # logg.info("== 'try-restart' will restart an is-active service") cmd = "{systemctl} try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top7 = top # logg.info("-- and we check that there is a new PID for the service process") ps7 = find_pids(top7, testsleep) logg.info("found PIDs %s and %s", ps6, ps7) self.assertTrue(len(ps6), 1) self.assertTrue(len(ps7), 1) self.assertNotEqual(ps6[0], ps7[0]) # logg.info("LOG\n%s", " "+open(logfile).read().replace("\n","\n ")) def test_4040_oneshot_service_functions(self): """ check that we manage oneshot services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart.""" self.begin() testname = self.testname() testdir = self.testdir() self.oneshot_service_functions("system", testname, testdir) def test_4041_oneshot_service_functions_user(self): """ check that we manage oneshot services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart.""" self.begin() testname = self.testname() testdir = self.testdir() self.oneshot_service_functions("user", testname, testdir) def oneshot_service_functions(self, system, testname, testdir): user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root systemctl += " --{system}".format(**locals()) testsleep = self.testname("sleep") logfile = os_path(root, "/var/log/"+testsleep+".log") bindir = os_path(root, "/usr/bin") os.makedirs(os_path(root, "/var/run")) text_file(logfile, "created\n") begin = "{" ; end = "}" text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=oneshot ExecStartPre={bindir}/backup {root}/var/tmp/test.1 {root}/var/tmp/test.2 ExecStart=/usr/bin/touch {root}/var/tmp/test.1 ExecStop=/bin/rm {root}/var/tmp/test.1 ExecStopPost=/bin/rm -f {root}/var/tmp/test.2 [Install] WantedBy=multi-user.target """.format(**locals())) shell_file(os_path(testdir, "backup"), """ #! /bin/sh set -x test ! -f "$1" || mv -v "$1" "$2" """) zzz_service = "/etc/systemd/{system}/zzz.service".format(**locals()) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zzz.service"), os_path(root, zzz_service)) copy_tool(os_path(testdir, "backup"), os_path(root, "/usr/bin/backup")) text_file(os_path(root, "/var/tmp/test.0"), """..""") # cmd = "{systemctl} enable zzz.service -vv" sh____(cmd.format(**locals())) # is_active = "{systemctl} is-active zzz.service -vv" act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive") self.assertEqual(end, 3) # logg.info("== 'start' shall start a service that is NOT is-active ") cmd = "{systemctl} start zzz.service -vvvv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active") self.assertEqual(end, 0) self.assertTrue(os.path.exists(os_path(root, "/var/tmp/test.1"))) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.2"))) # logg.info("== 'stop' shall stop a service that is-active") cmd = "{systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive") self.assertEqual(end, 3) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.1"))) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.2"))) # logg.info("== 'restart' shall start a service that NOT is-active") cmd = "{systemctl} restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active") self.assertEqual(end, 0) self.assertTrue(os.path.exists(os_path(root, "/var/tmp/test.1"))) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.2"))) # logg.info("== 'restart' shall restart a service that is-active") cmd = "{systemctl} restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active") self.assertEqual(end, 0) self.assertTrue(os.path.exists(os_path(root, "/var/tmp/test.1"))) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.2"))) # logg.info("== 'reload' will NOT restart a service that is-active") cmd = "{systemctl} reload zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active") self.assertEqual(end, 0) self.assertTrue(os.path.exists(os_path(root, "/var/tmp/test.1"))) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.2"))) # logg.info("== 'reload-or-restart' will restart a service that is-active") cmd = "{systemctl} reload-or-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active") self.assertEqual(end, 0) # logg.info("== 'stop' will brings it back to 'inactive'") cmd = "{systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive") self.assertEqual(end, 3) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.1"))) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.2"))) # logg.info("== 'reload-or-try-restart' will not start a not-active service") cmd = "{systemctl} reload-or-try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive") self.assertEqual(end, 3) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.1"))) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.2"))) # logg.info("== 'try-restart' will not start a not-active service") cmd = "{systemctl} try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive") self.assertEqual(end, 3) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.1"))) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.2"))) # logg.info("== 'reload-or-restart' will start a not-active service") cmd = "{systemctl} reload-or-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active") self.assertEqual(end, 0) self.assertTrue(os.path.exists(os_path(root, "/var/tmp/test.1"))) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.2"))) # logg.info("== 'reload-or-try-restart' will restart an is-active service") cmd = "{systemctl} reload-or-try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active") self.assertEqual(end, 0) self.assertTrue(os.path.exists(os_path(root, "/var/tmp/test.1"))) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.2"))) # logg.info("== 'try-restart' will restart an is-active service") cmd = "{systemctl} try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active") self.assertEqual(end, 0) self.assertTrue(os.path.exists(os_path(root, "/var/tmp/test.1"))) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.2"))) # logg.info("== 'stop' will brings it back to 'inactive'") cmd = "{systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive") self.assertEqual(end, 3) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.1"))) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.2"))) # logg.info("LOG\n%s", " "+open(logfile).read().replace("\n","\n ")) def test_4042_oneshot_and_unknown_service_functions(self): """ check that we manage multiple services even when some services are not actually known. Along with oneshot serivce with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart / we have only different exit-code.""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") logfile = os_path(root, "/var/log/"+testsleep+".log") bindir = os_path(root, "/usr/bin") os.makedirs(os_path(root, "/var/run")) text_file(logfile, "created\n") begin = "{" ; end = "}" text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=oneshot ExecStartPre={bindir}/backup {root}/var/tmp/test.1 {root}/var/tmp/test.2 ExecStart=/usr/bin/touch {root}/var/tmp/test.1 ExecStop=/bin/rm {root}/var/tmp/test.1 ExecStopPost=/bin/rm -f {root}/var/tmp/test.2 [Install] WantedBy=multi-user.target """.format(**locals())) shell_file(os_path(testdir, "backup"), """ #! /bin/sh set -x test ! -f "$1" || mv -v "$1" "$2" """) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) copy_tool(os_path(testdir, "backup"), os_path(root, "/usr/bin/backup")) text_file(os_path(root, "/var/tmp/test.0"), """..""") # cmd = "{systemctl} enable zzz.service -vv" sh____(cmd.format(**locals())) is_active = "{systemctl} is-active zzz.service other.service -vv" act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive\nunknown") self.assertEqual(end, 3) # logg.info("== 'start' shall start a service that is NOT is-active ") cmd = "{systemctl} start zzz.service other.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) is_active = "{systemctl} is-active zzz.service other.service -vv" act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active\nunknown") self.assertEqual(end, 3) self.assertTrue(os.path.exists(os_path(root, "/var/tmp/test.1"))) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.2"))) # logg.info("== 'stop' shall stop a service that is-active") cmd = "{systemctl} stop zzz.service other.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive\nunknown") self.assertEqual(end, 3) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.1"))) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.2"))) # logg.info("== 'restart' shall start a service that NOT is-active") cmd = "{systemctl} restart zzz.service other.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active\nunknown") self.assertEqual(end, 3) self.assertTrue(os.path.exists(os_path(root, "/var/tmp/test.1"))) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.2"))) # logg.info("== 'restart' shall restart a service that is-active") cmd = "{systemctl} restart zzz.service other.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active\nunknown") self.assertEqual(end, 3) self.assertTrue(os.path.exists(os_path(root, "/var/tmp/test.1"))) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.2"))) # logg.info("== 'reload' will NOT restart a service that is-active") cmd = "{systemctl} reload zzz.service other.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active\nunknown") self.assertEqual(end, 3) self.assertTrue(os.path.exists(os_path(root, "/var/tmp/test.1"))) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.2"))) # logg.info("== 'reload-or-restart' will restart a service that is-active") cmd = "{systemctl} reload-or-restart zzz.service other.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active\nunknown") self.assertEqual(end, 3) # logg.info("== 'stop' will brings it back to 'inactive'") cmd = "{systemctl} stop zzz.service other.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive\nunknown") self.assertEqual(end, 3) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.1"))) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.2"))) # logg.info("== 'reload-or-try-restart' will not start a not-active service") cmd = "{systemctl} reload-or-try-restart zzz.service other.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive\nunknown") self.assertEqual(end, 3) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.1"))) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.2"))) # logg.info("== 'try-restart' will not start a not-active service") cmd = "{systemctl} try-restart zzz.service other.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive\nunknown") self.assertEqual(end, 3) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.1"))) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.2"))) # logg.info("== 'reload-or-restart' will start a not-active service") cmd = "{systemctl} reload-or-restart zzz.service other.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active\nunknown") self.assertEqual(end, 3) self.assertTrue(os.path.exists(os_path(root, "/var/tmp/test.1"))) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.2"))) # logg.info("== 'reload-or-try-restart' will restart an is-active service") cmd = "{systemctl} reload-or-try-restart zzz.service other.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active\nunknown") self.assertEqual(end, 3) self.assertTrue(os.path.exists(os_path(root, "/var/tmp/test.1"))) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.2"))) # logg.info("== 'try-restart' will restart an is-active service") cmd = "{systemctl} try-restart zzz.service other.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active\nunknown") self.assertEqual(end, 3) self.assertTrue(os.path.exists(os_path(root, "/var/tmp/test.1"))) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.2"))) # logg.info("== 'stop' will brings it back to 'inactive'") cmd = "{systemctl} stop zzz.service other.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive\nunknown") self.assertEqual(end, 3) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.1"))) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.2"))) # logg.info("LOG\n%s", " "+open(logfile).read().replace("\n","\n ")) self.rm_testdir() self.coverage() self.end() def test_4044_sysv_service_functions(self): """ check that we manage SysV services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart.""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") logfile = os_path(root, "/var/log/"+testsleep+".log") bindir = os_path(root, "/usr/bin") os.makedirs(os_path(root, "/var/run")) text_file(logfile, "created\n") begin = "{" ; end = "}" shell_file(os_path(testdir, "zzz.init"), """ #! /bin/bash ### BEGIN INIT INFO # Required-Start: $local_fs $remote_fs $syslog $network # Required-Stop: $local_fs $remote_fs $syslog $network # Default-Start: 3 5 # Default-Stop: 0 1 2 6 # Short-Description: Testing Z # Description: Allows for SysV testing ### END INIT INFO logfile={logfile} sleeptime=111 start() {begin} [ -d /var/run ] || mkdir -p /var/run ({bindir}/{testsleep} $sleeptime 0<&- &>/dev/null & echo $! > {root}/var/run/zzz.init.pid ) & wait %1 # ps -o pid,ppid,args cat "RUNNING `cat {root}/var/run/zzz.init.pid`" {end} stop() {begin} killall {testsleep} {end} case "$1" in start) date "+START.%T" >> $logfile start >> $logfile 2>&1 date "+start.%T" >> $logfile ;; stop) date "+STOP.%T" >> $logfile stop >> $logfile 2>&1 date "+stop.%T" >> $logfile ;; restart) date "+RESTART.%T" >> $logfile stop >> $logfile 2>&1 start >> $logfile 2>&1 date "+.%T" >> $logfile ;; reload) date "+RELOAD.%T" >> $logfile echo "...." >> $logfile 2>&1 date "+reload.%T" >> $logfile ;; esac echo "done$1" >&2 exit 0 """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_tool(os_path(testdir, "zzz.init"), os_path(root, "/etc/init.d/zzz")) # cmd = "{systemctl} enable zzz.service -vv" sh____(cmd.format(**locals())) # cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'start' shall start a service that is NOT is-active ") cmd = "{systemctl} start zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") # logg.info("== 'stop' shall stop a service that is-active") cmd = "{systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'restart' shall start a service that NOT is-active") cmd = "{systemctl} restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top1= top # logg.info("== 'restart' shall restart a service that is-active") cmd = "{systemctl} restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top2 = top # logg.info("-- and we check that there is a new PID for the service process") def find_pids(ps_output, command): pids = [] for line in _lines(ps_output): if command not in line: continue m = re.match(r"\s*[\d:]*\s+(\S+)\s+(\S+)\s+(.*)", line) pid, ppid, args = m.groups() # logg.info(" %s | %s | %s", pid, ppid, args) pids.append(pid) return pids ps1 = find_pids(top1, testsleep) ps2 = find_pids(top2, testsleep) logg.info("found PIDs %s and %s", ps1, ps2) self.assertTrue(len(ps1), 1) self.assertTrue(len(ps2), 1) self.assertNotEqual(ps1[0], ps2[0]) # logg.info("== 'reload' will NOT restart a service that is-active") cmd = "{systemctl} reload zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top3 = top # logg.info("-- and we check that there is NO new PID for the service process") ps3 = find_pids(top3, testsleep) logg.info("found PIDs %s and %s", ps2, ps3) self.assertTrue(len(ps2), 1) self.assertTrue(len(ps3), 1) self.assertEqual(ps2[0], ps3[0]) # logg.info("== 'reload-or-restart' may restart a service that is-active") cmd = "{systemctl} reload-or-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") # logg.info("== 'stop' will turn 'failed' to 'inactive' (when the PID is known)") cmd = "{systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'reload-or-try-restart' will not start a not-active service") cmd = "{systemctl} reload-or-try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'try-restart' will not start a not-active service") cmd = "{systemctl} try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'reload-or-restart' will start a not-active service") cmd = "{systemctl} reload-or-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top5 = top # logg.info("== 'reload-or-try-restart' will restart an is-active service (with no ExecReload)") cmd = "{systemctl} reload-or-try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top6 = top # logg.info("== 'try-restart' will restart an is-active service") cmd = "{systemctl} try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top7 = top # logg.info("-- and we check that there is a new PID for the service process") ps6 = find_pids(top6, testsleep) ps7 = find_pids(top7, testsleep) logg.info("found PIDs %s and %s", ps6, ps7) self.assertTrue(len(ps6), 1) self.assertTrue(len(ps7), 1) self.assertNotEqual(ps6[0], ps7[0]) # logg.info("LOG\n%s", " "+open(logfile).read().replace("\n","\n ")) self.rm_testdir() self.coverage() self.end() def test_4050_forking_service_failed_functions(self): """ check that we manage forking services in a root env with basic run-service commands: start, stop, restart, checking the executions when some part fails.""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) quick = "--coverage=quick" systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") logfile = os_path(root, "/var/log/"+testsleep+".log") bindir = os_path(root, "/usr/bin") fail = os_path(root, "/tmp/fail") os.makedirs(os_path(root, "/var/run")) text_file(logfile, "created\n") begin = "{" ; end = "}" shell_file(os_path(testdir, "zzz.init"), """ #! /bin/bash logfile={logfile} start() {begin} [ -d /var/run ] || mkdir -p /var/run ({bindir}/{testsleep} 111 0<&- &>/dev/null & echo $! > {root}/var/run/zzz.init.pid ) & wait %1 # ps -o pid,ppid,args {end} stop() {begin} killall {testsleep} {end} echo "run-$1" >> $logfile if test -f {fail}$1; then echo "fail-$1" >> $logfile exit 1 fi case "$1" in start) echo "START-IT" >> $logfile start >> $logfile 2>&1 echo "started" >> $logfile ;; stop) echo "STOP-IT" >> $logfile stop >> $logfile 2>&1 echo "stopped" >> $logfile ;; restart) echo "RESTART-IT" >> $logfile stop >> $logfile 2>&1 start >> $logfile 2>&1 echo "restarted" >> $logfile ;; reload) echo "RELOAD-IT" >> $logfile echo "...." >> $logfile 2>&1 echo "reloaded" >> $logfile ;; start-pre) echo "START-PRE" >> $logfile ;; start-post) echo "START-POST" >> $logfile ;; stop-post) echo "STOP-POST" >> $logfile ;; esac echo "done$1" >&2 if test -f {fail}after$1; then echo "fail-after-$1" >> $logfile exit 1 fi exit 0 """.format(**locals())) text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=forking PIDFile={root}/var/run/zzz.init.pid ExecStartPre={root}/usr/bin/zzz.init start-pre ExecStart={root}/usr/bin/zzz.init start ExecStartPost={root}/usr/bin/zzz.init start-post ExecStop={root}/usr/bin/zzz.init stop ExecStopPost={root}/usr/bin/zzz.init stop-post [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_tool(os_path(testdir, "zzz.init"), os_path(root, "/usr/bin/zzz.init")) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) # cmd = "{systemctl} enable zzz.service -vv" sh____(cmd.format(**locals())) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # log = lines(open(logfile)) logg.info("LOG\n %s", "\n ".join(log)) os.remove(logfile) self.assertEqual(log, ["created"]) # logg.info("== 'start' shall start a service that is NOT is-active ") cmd = "{systemctl} start zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") # log = lines(open(logfile)) logg.info("LOG\n %s", "\n ".join(log)) os.remove(logfile) self.assertEqual(log, [ "run-start-pre", "START-PRE", "run-start", "START-IT", "started", "run-start-post", "START-POST"]) # logg.info("== 'stop' shall stop a service that is-active") cmd = "{systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # log = lines(open(logfile)) logg.info("LOG\n %s", "\n ".join(log)) os.remove(logfile) self.assertEqual(log, [ "run-stop", "STOP-IT", "stopped", "run-stop-post", "STOP-POST"]) # text_file(fail+"start", "") # logg.info("== 'start' returns to stopped if the main call fails ") cmd = "{systemctl} start zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertNotEqual(end, 0) self.assertEqual(out.strip(), "inactive") # log = lines(open(logfile)) logg.info("LOG\n %s", "\n ".join(log)) os.remove(logfile) self.assertEqual(log, [ "run-start-pre", "START-PRE", "run-start", "fail-start", "run-stop-post", "STOP-POST"]) # logg.info("== 'stop' on stopped service does not do much ") cmd = "{systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertNotEqual(end, 0) self.assertEqual(out.strip(), "inactive") # log = lines(open(logfile)) logg.info("LOG\n %s", "\n ".join(log)) os.remove(logfile) self.assertEqual(log[:2], [ "run-stop", "STOP-IT" ]) self.assertEqual(log[-2:], [ "run-stop-post", "STOP-POST"]) # logg.info("== 'restart' on a stopped item remains stopped if the main call fails ") cmd = "{systemctl} restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertNotEqual(end, 0) self.assertEqual(out.strip(), "inactive") # log = lines(open(logfile)) logg.info("LOG\n %s", "\n ".join(log)) os.remove(logfile) self.assertEqual(log, [ "run-start-pre", "START-PRE", "run-start", "fail-start", "run-stop-post", "STOP-POST"]) # os.remove(fail+"start") text_file(fail+"stop", "") # logg.info("== 'start' that service ") cmd = "{systemctl} start zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # logg.info("== 'stop' may have a failed item ") cmd = "{systemctl} stop zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") # 'active' because the PIDFile process was not killed # log = lines(open(logfile)) logg.info("LOG\n %s", "\n ".join(log)) os.remove(logfile) self.assertEqual(log, [ "run-start-pre", "START-PRE", "run-start", "START-IT", "started", "run-start-post", "START-POST", "run-stop", "fail-stop"]) # os.remove(fail+"stop") text_file(fail+"afterstop", "") # logg.info("== 'start' that service ") cmd = "{systemctl} start zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # logg.info("== 'stop' may have a failed item ") cmd = "{systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertNotEqual(end, 0) self.assertEqual(out.strip(), "inactive") # log = lines(open(logfile)) logg.info("LOG\n %s", "\n ".join(log)) os.remove(logfile) self.assertEqual(log, [ "run-start-pre", "START-PRE", "run-start", "START-IT", "started", "run-start-post", "START-POST", "run-stop", "STOP-IT", "stopped", "fail-after-stop", "run-stop-post", "STOP-POST"]) # os.remove(fail+"afterstop") text_file(fail+"afterstart", "") # logg.info("== 'start' shall start a service that is NOT is-active ") cmd = "{systemctl} start zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 1) cmd = "{systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "failed") # log = lines(open(logfile)) logg.info("LOG\n %s", "\n ".join(log)) os.remove(logfile) self.assertEqual(log, [ "run-start-pre", "START-PRE", "run-start", "START-IT", "started", "fail-after-start", "run-stop-post", "STOP-POST"]) # self.rm_testdir() self.coverage() self.end() def test_4060_oneshot_truncate_old_status(self): """ check that we manage a service that has some old .status file being around. That is a reboot has occurred and the information is not relevant to the current system state.""" self.begin() self.rm_testdir() vv = "-vv" testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") logfile = os_path(root, "/var/log/"+testsleep+".log") bindir = os_path(root, "/usr/bin") os.makedirs(os_path(root, "/var/run")) text_file(logfile, "created\n") begin = "{" ; end = "}" text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=oneshot ExecStart=/usr/bin/touch {root}/var/tmp/test.1 ExecStopPost=/bin/rm {root}/var/tmp/test.1 [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) text_file(os_path(root, "/var/tmp/test.0"), """..""") # cmd = "{systemctl} enable zzz.service -vv" sh____(cmd.format(**locals())) is_active = "{systemctl} is-active zzz.service other.service {vv}" act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive\nunknown") self.assertEqual(end, 3) # logg.info("== 'start' shall start a service that is NOT is-active ") cmd = "{systemctl} start zzz.service other.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) is_active = "{systemctl} is-active zzz.service other.service -vv" act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active\nunknown") self.assertEqual(end, 3) self.assertTrue(os.path.exists(os_path(root, "/var/tmp/test.1"))) # logg.info("== 'stop' shall stop a service that is-active") cmd = "{systemctl} stop zzz.service other.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive\nunknown") self.assertEqual(end, 3) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.1"))) # logg.info("== 'restart' shall start a service that NOT is-active\n") cmd = "{systemctl} restart zzz.service other.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active\nunknown") self.assertEqual(end, 3) self.assertTrue(os.path.exists(os_path(root, "/var/tmp/test.1"))) # logg.info("== mark the status file as being too old") status_file = os_path(root, "/var/run/zzz.service.status") self.assertTrue(os.path.exists(status_file)) sh____("LANG=C stat {status_file} | grep Modify:".format(**locals())) sh____("LANG=C stat /proc/1/status | grep Modify:".format(**locals())) sh____("touch -r /proc/1/status {status_file}".format(**locals())) sh____("LANG=C stat {status_file} | grep Modify:".format(**locals())) # logg.info("== the next is-active shall then truncate it") old_size = os.path.getsize(status_file) is_activeXX = "{systemctl} is-active zzz.service other.service {vv} {vv}" act, end = output2(is_activeXX.format(**locals())) self.assertEqual(act.strip(), "inactive\nunknown") self.assertEqual(end, 3) self.assertTrue(os.path.exists(os_path(root, "/var/tmp/test.1"))) new_size = os.path.getsize(status_file) logg.info("status-file size: old %s new %s", old_size, new_size) self.assertGreater(old_size, 0) self.assertEqual(new_size, 0) # logg.info("== 'stop' shall cleanup a service that was not inactive") cmd = "{systemctl} stop zzz.service other.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive\nunknown") self.assertEqual(end, 3) self.assertFalse(os.path.exists(os_path(root, "/var/tmp/test.1"))) # and the status_file is also cleaned away self.assertFalse(os.path.exists(status_file)) # logg.info("LOG\n%s", " "+open(logfile).read().replace("\n","\n ")) self.rm_testdir() self.coverage() self.end() def test_4065_simple_truncate_old_pid(self): """ check that we manage a service that has some old .pid file being around. That is a reboot has occurred and the information is not relevant to the current system state.""" self.begin() vv = "-vv" testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) logfile = os_path(root, "/var/log/test.log") systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z After=foo.service [Service] Type=simple ExecStart={bindir}/{testsleep} 99 ExecStop=/usr/bin/killall {testsleep} [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) # cmd = "{systemctl} enable zzz.service -vv" sh____(cmd.format(**locals())) is_active = "{systemctl} is-active zzz.service other.service {vv}" act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive\nunknown") self.assertEqual(end, 3) # logg.info("== 'start' shall start a service that is NOT is-active ") cmd = "{systemctl} start zzz.service other.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) is_active = "{systemctl} is-active zzz.service other.service -vv" act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active\nunknown") self.assertEqual(end, 3) # logg.info("== 'stop' shall stop a service that is-active") cmd = "{systemctl} stop zzz.service other.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive\nunknown") self.assertEqual(end, 3) # logg.info("== 'restart' shall start a service that NOT is-active\n") cmd = "{systemctl} restart zzz.service other.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active\nunknown") self.assertEqual(end, 3) # time.sleep(2) logg.info("== mark the status file as being too old") status_file = os_path(root, "/var/run/zzz.service.status") self.assertTrue(os.path.exists(status_file)) sh____("LANG=C stat {status_file} | grep Modify:".format(**locals())) sh____("LANG=C stat /proc/1/status | grep Modify:".format(**locals())) sh____("touch -r /proc/1/status {status_file}".format(**locals())) sh____("LANG=C stat {status_file} | grep Modify:".format(**locals())) # pid_file = os_path(root, "/var/run/zzz.service.pid") #+ self.assertTrue(os.path.exists(pid_file)) #+ sh____("LANG=C stat {pid_file} | grep Modify:".format(**locals())) #+ sh____("LANG=C stat /proc/1/status | grep Modify:".format(**locals())) #+ sh____("touch -r /proc/1/status {pid_file}".format(**locals())) #+ sh____("LANG=C stat {pid_file} | grep Modify:".format(**locals())) # logg.info("== the next is-active shall then truncate it") old_status = os.path.getsize(status_file) #+ old_pid = os.path.getsize(pid_file) is_activeXX = "{systemctl} is-active zzz.service other.service {vv} {vv}" act, end = output2(is_activeXX.format(**locals())) self.assertEqual(act.strip(), "inactive\nunknown") self.assertEqual(end, 3) new_status = os.path.getsize(status_file) #+ new_pid = os.path.getsize(pid_file) logg.info("status-file size: old %s new %s", old_status, new_status) self.assertGreater(old_status, 0) self.assertEqual(new_status, 0) #+ logg.info("pid-file size: old %s new %s", old_pid, new_pid) #+ self.assertGreater(old_pid, 0) #+ self.assertEqual(new_pid, 0) # logg.info("== 'stop' shall cleanup a service that was not inactive") cmd = "{systemctl} stop zzz.service other.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive\nunknown") self.assertEqual(end, 3) logg.info("== and the status_file / pid_file is also cleaned away") self.assertFalse(os.path.exists(status_file)) self.assertFalse(os.path.exists(pid_file)) # self.rm_testdir() self.coverage() self.end() def real_4090_simple_service_RemainAfterExit(self): self.test_4090_simple_service_RemainAfterExit(True) def test_4090_simple_service_RemainAfterExit(self, real = None): """ check that we manage simple services in a root env with commands like start, restart, stop, etc where RemainAfterExit=yes says the service is okay even when ExecStart has finished.""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir, real) vv = "-vv" systemctl = cover() + _systemctl_py + " --root=" + root if real: vv, systemctl = "", "/usr/bin/systemctl" testsleep = self.testname("testsleep") testfail = self.testname("testfail.sh") bindir = os_path(root, "/usr/bin") begin = "{" end = "}" text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=simple ExecStartPre=/bin/echo %n ExecStart={bindir}/{testsleep} 20 ExecStartPost=/bin/echo started $MAINPID ExecStop=/bin/kill $MAINPID ExecStopPost=/bin/echo stopped $MAINPID ExecStopPost=/bin/sleep 2 [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zze.service"),""" [Unit] Description=Testing E [Service] Type=simple ExecStartPre=/bin/echo %n ExecStart={bindir}/{testsleep} 3 ExecStartPost=/bin/echo started $MAINPID ExecStop=/bin/kill $MAINPID ExecStopPost=/bin/echo stopped $MAINPID ExecStopPost=/bin/sleep 2 [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zzf.service"),""" [Unit] Description=Testing F [Service] Type=simple ExecStartPre=/bin/echo %n ExecStart={bindir}/{testfail} 3 ExecStartPost=/bin/echo started $MAINPID ExecStop=/bin/kill $MAINPID ExecStopPost=/bin/echo stopped $MAINPID ExecStopPost=/bin/sleep 2 [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zzr.service"),""" [Unit] Description=Testing R [Service] Type=simple RemainAfterExit=yes ExecStartPre=/bin/echo %n ExecStart={bindir}/{testsleep} 3 ExecStartPost=/bin/echo started $MAINPID ExecStop=/bin/kill $MAINPID ExecStopPost=/bin/echo stopped $MAINPID ExecStopPost=/bin/sleep 2 [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zzx.service"),""" [Unit] Description=Testing X [Service] Type=simple RemainAfterExit=yes ExecStartPre=/bin/echo %n ExecStart={bindir}/{testfail} 3 ExecStartPost=/bin/echo started $MAINPID ExecStop=/bin/kill $MAINPID ExecStopPost=/bin/echo stopped $MAINPID ExecStopPost=/bin/sleep 2 [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "testfail.sh"),""" #! /bin/sh {bindir}/{testsleep} $1 exit 2 """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_tool(os_path(testdir, "testfail.sh"), os_path(bindir, testfail)) copy_file(os_path(testdir, "zzz.service"), os_path(root, "/etc/systemd/system/zzz.service")) copy_file(os_path(testdir, "zze.service"), os_path(root, "/etc/systemd/system/zze.service")) copy_file(os_path(testdir, "zzf.service"), os_path(root, "/etc/systemd/system/zzf.service")) copy_file(os_path(testdir, "zzr.service"), os_path(root, "/etc/systemd/system/zzr.service")) copy_file(os_path(testdir, "zzx.service"), os_path(root, "/etc/systemd/system/zzx.service")) sh____("{systemctl} daemon-reload".format(**locals())) # cmd = "{systemctl} is-active zzz.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "unknown") # cmd = "{systemctl} enable zzz.service {vv}" sh____(cmd.format(**locals())) cmd = "{systemctl} is-active zzz.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'start' shall start a normal service ") cmd = "{systemctl} start zzz.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) time.sleep(4) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") # logg.info("== 'stop' shall stop a normal service") cmd = "{systemctl} stop zzz.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zzz.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # cmd = "{systemctl} enable zze.service {vv}" sh____(cmd.format(**locals())) # logg.info("== 'start' will run a later exiting service ") cmd = "{systemctl} start zze.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) time.sleep(4) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zze.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "failed") # logg.info("== 'stop' shall clean an already exited service") cmd = "{systemctl} stop zze.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) if TODO or real: self.assertEqual(end, 0) else: self.assertEqual(end, 1) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zze.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) if TODO or real: self.assertEqual(out.strip(), "failed") else: self.assertEqual(out.strip(), "inactive") # cmd = "{systemctl} enable zzf.service {vv}" sh____(cmd.format(**locals())) # logg.info("== 'start' will run a later failing service ") cmd = "{systemctl} start zzf.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) time.sleep(4) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zzf.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "failed") # logg.info("== 'reset-failed' shall clean an already failed service") cmd = "{systemctl} reset-failed zzf.service {vv} {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} is-active zzf.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'stop' shall clean an already failed service") cmd = "{systemctl} stop zzf.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) if TODO or real: self.assertEqual(end, 0) else: self.assertEqual(end, 1) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zzf.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # # cmd = "{systemctl} enable zzr.service {vv}" sh____(cmd.format(**locals())) # logg.info("== 'start' will have a later exiting service as remaining active") cmd = "{systemctl} start zzr.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) time.sleep(4) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zzr.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") # <<<<<<<<<<< here's the new functionality # logg.info("== 'stop' shall clean an exited but remaining service") cmd = "{systemctl} stop zzr.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) if TODO or real: self.assertEqual(end, 0) else: self.assertEqual(end, 1) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zzr.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) if TODO or real: self.assertEqual(end, 0) else: self.assertEqual(end, 3) if TODO or real: self.assertEqual(out.strip(), "failed") else: self.assertEqual(out.strip(), "inactive") # cmd = "{systemctl} enable zzx.service {vv}" sh____(cmd.format(**locals())) # # logg.info("== 'start' will have a later failing service remaining but failed") cmd = "{systemctl} start zzx.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) time.sleep(4) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zzx.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") # logg.info("== 'stop' shall clean an already failed remaining service") cmd = "{systemctl} stop zzx.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) if TODO or real: self.assertEqual(end, 0) else: self.assertEqual(end, 1) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep)) cmd = "{systemctl} is-active zzx.service {vv}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) if TODO or real: self.assertEqual(out.strip(), "failed") else: self.assertEqual(out.strip(), "inactive") # # cleanup kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.rm_zzfiles(root) self.coverage() self.end() def test_4101_systemctl_py_kill_basic_behaviour(self): """ check systemctl_py kill basic behaviour""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") testsleepB = testsleep+"B" testsleepC = testsleep+"C" bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStart={bindir}/{testsleepB} 99 [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C [Service] Type=simple ExecStart={bindir}/{testsleepC} 111 [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleepB)) copy_tool("/usr/bin/sleep", os_path(bindir, testsleepC)) copy_file(os_path(testdir, "zzb.service"), os_path(root, "/etc/systemd/system/zzb.service")) copy_file(os_path(testdir, "zzc.service"), os_path(root, "/etc/systemd/system/zzc.service")) # cmd = "{systemctl} start zzb.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} start zzc.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleepB)) self.assertTrue(greps(top, testsleepC)) # cmd = "{systemctl} stop zzb.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} kill zzc.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # time.sleep(1) # kill is asynchronous top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleepB)) self.assertFalse(greps(top, testsleepC)) # cmd = "{systemctl} start zzb.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} start zzc.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleepB)) self.assertTrue(greps(top, testsleepC)) # cmd = "killall {testsleepB}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "killall {testsleepC}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} stop zzb.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # already down cmd = "{systemctl} kill zzc.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # nothing to kill # time.sleep(1) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleepB)) self.assertFalse(greps(top, testsleepC)) # self.rm_testdir() self.coverage() self.end() def test_4105_systemctl_py_kill_in_stop(self): """ check systemctl_py kill from ExecStop""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("sleep") testsleepB = testsleep+"B" testsleepC = testsleep+"C" bindir = os_path(root, "/usr/bin") rundir = os_path(root, "/var/run") begin="{" end="}" text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStart={bindir}/{testsleepB} 99 [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C [Service] Type=simple ExecStart={bindir}/{testsleepC} 111 ExecStop=/bin/kill ${begin}MAINPID{end} [Install] WantedBy=multi-user.target """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleepB)) copy_tool("/usr/bin/sleep", os_path(bindir, testsleepC)) copy_file(os_path(testdir, "zzb.service"), os_path(root, "/etc/systemd/system/zzb.service")) copy_file(os_path(testdir, "zzc.service"), os_path(root, "/etc/systemd/system/zzc.service")) os.makedirs(rundir) # cmd = "{systemctl} stop zzb.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} stop zzc.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) # self.assertEqual(end, 0) # cmd = "{systemctl} start zzb.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} start zzc.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleepB)) self.assertTrue(greps(top, testsleepC)) # cmd = "ls -l {rundir}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) if TODO: self.assertTrue(greps(out, "zzb.service.pid")) if TODO: self.assertTrue(greps(out, "zzc.service.pid")) # cmd = "{systemctl} stop zzb.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} kill zzc.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # time.sleep(1) # kill is asynchronous top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleepB)) self.assertFalse(greps(top, testsleepC)) # cmd = "ls -l {rundir}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) if TODO: self.assertFalse(greps(out, "zzb.service.pid")) if TODO: self.assertTrue(greps(out, "zzc.service.pid")) # cmd = "{systemctl} start zzb.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "{systemctl} start zzc.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleepB)) self.assertTrue(greps(top, testsleepC)) # cmd = "ls -l {rundir}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) if TODO: self.assertTrue(greps(out, "zzb.service.pid")) if TODO: self.assertTrue(greps(out, "zzc.service.pid")) # cmd = "killall {testsleepB}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "killall {testsleepC}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # cmd = "{systemctl} stop zzb.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # already down cmd = "{systemctl} kill zzc.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # nothing to kill # cmd = "ls -l {rundir}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) if TODO: self.assertFalse(greps(out, "zzb.service.pid")) # issue #13 if TODO: self.assertTrue(greps(out, "zzc.service.pid")) # TODO ? # time.sleep(1) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleepB)) self.assertFalse(greps(top, testsleepC)) # self.rm_testdir() self.coverage() self.end() def test_4120_systemctl_kill_ignore_behaviour(self): """ systemctl kill ignore behaviour""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) quick = "--coverage=quick" systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("testsleep") testsleepB = testsleep+"B" testsleepC = testsleep+"C" testscriptB = self.testname("testscriptB.sh") testscriptC = self.testname("testscriptC.sh") logfile = os_path(root, "/var/log/test.log") bindir = os_path(root, "/usr/bin") begin = "{" end = "}" text_file(logfile, "") text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStartPre=/bin/echo %n ExecStart={bindir}/{testscriptB} 111 ExecStartPost=/bin/echo started $MAINPID ExecStop=/bin/kill -3 $MAINPID ExecStopPost=/bin/echo stopped $MAINPID ExecStopPost=/bin/sleep 2 ExecReload=/bin/kill -10 $MAINPID # KillSignal=SIGQUIT [Install] WantedBy=multi-user.target """.format(**locals())) shell_file(os_path(bindir, testscriptB),""" #! /bin/sh date +%T,enter > {logfile} stops () {begin} date +%T,stopping >> {logfile} killall {testsleep} date +%T,stopped >> {logfile} {end} reload () {begin} date +%T,reloading >> {logfile} date +%T,reloaded >> {logfile} {end} ignored () {begin} date +%T,ignored >> {logfile} {end} sighup () {begin} date +%T,sighup >> {logfile} {end} trap "stops" 3 # SIGQUIT trap "reload" 10 # SIGUSR1 trap "ignored" 15 # SIGTERM trap "sighup" 1 # SIGHUP date +%T,starting >> {logfile} {bindir}/{testsleepB} $1 >> {logfile} 2>&1 & while kill -0 $!; do # use 'kill -0' to check the existance of the child date +%T,waiting >> {logfile} # use 'wait' for children AND external signals wait done date +%T,leaving >> {logfile} trap - 3 10 15 # SIGQUIT SIGUSR1 SIGTERM date +%T,leave >> {logfile} """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleepB)) copy_tool("/usr/bin/sleep", os_path(bindir, testsleepC)) copy_file(os_path(testdir, "zzb.service"), os_path(root, "/etc/systemd/system/zzb.service")) # cmd = "{systemctl} start zzb.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleepB)) # cmd = "{systemctl} stop zzb.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testscriptB)) self.assertTrue(greps(top, testsleepB)) # cmd = "{systemctl} kill zzb.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # time.sleep(1) # kill is asynchronous top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testscriptB)) self.assertFalse(greps(top, testsleepB)) # kills children as well # log = lines(open(logfile).read()) logg.info("LOG %s\n| %s", logfile, "\n| ".join(log)) self.assertTrue(greps(log, "ignored")) self.assertFalse(greps(log, "sighup")) # top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) cmd = "killall {testsleepB}" sx____(cmd.format(**locals())) # cleanup before check self.assertFalse(greps(top, testsleepB)) # self.rm_testdir() self.coverage() self.end() def test_4121_systemctl_kill_ignore_nokill_behaviour(self): """ systemctl kill ignore and nokill behaviour""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) quick = "--coverage=quick" systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("testsleep") testsleepB = testsleep+"B" testsleepC = testsleep+"C" testscriptB = self.testname("testscriptB.sh") testscriptC = self.testname("testscriptC.sh") logfile = os_path(root, "/var/log/test.log") bindir = os_path(root, "/usr/bin") begin = "{" end = "}" text_file(logfile, "") text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStartPre=/bin/echo %n ExecStart={bindir}/{testscriptB} 111 ExecStartPost=/bin/echo started $MAINPID ExecStop=/bin/kill -3 $MAINPID ExecStopPost=/bin/echo stopped $MAINPID ExecStopPost=/bin/sleep 2 ExecReload=/bin/kill -10 $MAINPID # KillSignal=SIGQUIT SendSIGKILL=no [Install] WantedBy=multi-user.target """.format(**locals())) shell_file(os_path(bindir, testscriptB),""" #! /bin/sh date +%T,enter > {logfile} stops () {begin} date +%T,stopping >> {logfile} killall {testsleep} date +%T,stopped >> {logfile} {end} reload () {begin} date +%T,reloading >> {logfile} date +%T,reloaded >> {logfile} {end} ignored () {begin} date +%T,ignored >> {logfile} {end} sighup () {begin} date +%T,sighup >> {logfile} {end} trap "stops" 3 # SIGQUIT trap "reload" 10 # SIGUSR1 trap "ignored" 15 # SIGTERM trap "sighup" 1 # SIGHUP date +%T,starting >> {logfile} {bindir}/{testsleepB} $1 >> {logfile} 2>&1 & while kill -0 $!; do # use 'kill -0' to check the existance of the child date +%T,waiting >> {logfile} # use 'wait' for children AND external signals wait done date +%T,leaving >> {logfile} trap - 3 10 15 # SIGQUIT SIGUSR1 SIGTERM date +%T,leave >> {logfile} """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleepB)) copy_tool("/usr/bin/sleep", os_path(bindir, testsleepC)) copy_file(os_path(testdir, "zzb.service"), os_path(root, "/etc/systemd/system/zzb.service")) # cmd = "{systemctl} start zzb.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleepB)) # cmd = "{systemctl} stop zzb.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testscriptB)) self.assertTrue(greps(top, testsleepB)) # cmd = "{systemctl} kill zzb.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # actually killed # time.sleep(1) # kill is asynchronous top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testscriptB)) self.assertFalse(greps(top, testsleepB)) # and it kills children # log = lines(open(logfile).read()) logg.info("LOG %s\n| %s", logfile, "\n| ".join(log)) self.assertTrue(greps(log, "ignored")) self.assertFalse(greps(log, "sighup")) # time.sleep(1) # kill is asynchronous top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) cmd = "killall {testsleepB}" sx____(cmd.format(**locals())) # cleanup before check self.assertFalse(greps(top, testscriptB)) self.assertFalse(greps(top, testsleepB)) # self.rm_testdir() self.coverage() self.end() def test_4151_systemctl_kill_sendsighup(self): """ systemctl kill with sighup""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) quick = "--coverage=quick" systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("testsleep") testsleepB = testsleep+"B" testsleepC = testsleep+"C" testscriptB = self.testname("testscriptB.sh") testscriptC = self.testname("testscriptC.sh") logfile = os_path(root, "/var/log/test.log") bindir = os_path(root, "/usr/bin") begin = "{" end = "}" text_file(logfile, "") text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStartPre=/bin/echo %n ExecStart={bindir}/{testscriptB} 111 ExecStartPost=/bin/echo started $MAINPID ExecStop=/bin/kill -3 $MAINPID ExecStopPost=/bin/echo stopped $MAINPID ExecStopPost=/bin/sleep 2 ExecReload=/bin/kill -10 $MAINPID # KillSignal=SIGQUIT SendSIGHUP=yes [Install] WantedBy=multi-user.target """.format(**locals())) shell_file(os_path(bindir, testscriptB),""" #! /bin/sh date +%T,enter > {logfile} stops () {begin} date +%T,stopping >> {logfile} killall {testsleep} date +%T,stopped >> {logfile} {end} reload () {begin} date +%T,reloading >> {logfile} date +%T,reloaded >> {logfile} {end} ignored () {begin} date +%T,ignored >> {logfile} {end} sighup () {begin} date +%T,sighup >> {logfile} {end} trap "stops" 3 # SIGQUIT trap "reload" 10 # SIGUSR1 trap "ignored" 15 # SIGTERM trap "sighup" 1 # SIGHUP date +%T,starting >> {logfile} {bindir}/{testsleepB} $1 >> {logfile} 2>&1 & while kill -0 $!; do # use 'kill -0' to check the existance of the child date +%T,waiting >> {logfile} # use 'wait' for children AND external signals wait done date +%T,leaving >> {logfile} trap - 3 10 15 # SIGQUIT SIGUSR1 SIGTERM date +%T,leave >> {logfile} """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleepB)) copy_tool("/usr/bin/sleep", os_path(bindir, testsleepC)) copy_file(os_path(testdir, "zzb.service"), os_path(root, "/etc/systemd/system/zzb.service")) # cmd = "{systemctl} start zzb.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleepB)) # cmd = "{systemctl} stop zzb.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testscriptB)) self.assertTrue(greps(top, testsleepB)) # cmd = "{systemctl} kill zzb.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # actually killed # time.sleep(1) # kill is asynchronous top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testscriptB)) self.assertFalse(greps(top, testsleepB)) # and it kills children # log = lines(open(logfile).read()) logg.info("LOG %s\n| %s", logfile, "\n| ".join(log)) self.assertTrue(greps(log, "ignored")) self.assertTrue(greps(log, "sighup")) # time.sleep(1) # kill is asynchronous top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) cmd = "killall {testsleepB}" sx____(cmd.format(**locals())) # cleanup before check self.assertFalse(greps(top, testscriptB)) self.assertFalse(greps(top, testsleepB)) # self.rm_testdir() self.coverage() self.end() def test_4160_systemctl_kill_process_hard(self): """ systemctl kill needs to be hard""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) quick = "--coverage=quick" systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("testsleep") testsleepB = testsleep+"B" testsleepC = testsleep+"C" testscriptB = self.testname("testscriptB.sh") testscriptC = self.testname("testscriptC.sh") logfile = os_path(root, "/var/log/test.log") bindir = os_path(root, "/usr/bin") begin = "{" end = "}" text_file(logfile, "") text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStartPre=/bin/echo %n ExecStart={bindir}/{testscriptB} 111 ExecStartPost=/bin/echo started $MAINPID ExecStop=/bin/kill -3 $MAINPID ExecStopPost=/bin/echo stopped $MAINPID ExecStopPost=/bin/sleep 2 ExecReload=/bin/kill -10 $MAINPID KillMode=process KillSignal=SIGQUIT # SendSIGHUP=yes [Install] WantedBy=multi-user.target """.format(**locals())) shell_file(os_path(bindir, testscriptB),""" #! /bin/sh date +%T,enter > {logfile} stops () {begin} date +%T,stopfails >> {logfile} # killall {testsleep} ############## kill ignored {end} reload () {begin} date +%T,reloading >> {logfile} date +%T,reloaded >> {logfile} {end} ignored () {begin} date +%T,ignored >> {logfile} {end} sighup () {begin} date +%T,sighup >> {logfile} {end} trap "stops" 3 # SIGQUIT trap "reload" 10 # SIGUSR1 trap "ignored" 15 # SIGTERM trap "sighup" 1 # SIGHUP date +%T,starting >> {logfile} {bindir}/{testsleepB} $1 >> {logfile} 2>&1 & while kill -0 $!; do # use 'kill -0' to check the existance of the child date +%T,waiting >> {logfile} # use 'wait' for children AND external signals wait done date +%T,leaving >> {logfile} trap - 3 10 15 # SIGQUIT SIGUSR1 SIGTERM date +%T,leave >> {logfile} """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleepB)) copy_tool("/usr/bin/sleep", os_path(bindir, testsleepC)) copy_file(os_path(testdir, "zzb.service"), os_path(root, "/etc/systemd/system/zzb.service")) # cmd = "{systemctl} start zzb.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleepB)) # cmd = "{systemctl} stop zzb.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testscriptB)) self.assertTrue(greps(top, testsleepB)) # cmd = "{systemctl} kill zzb.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # actually killed # time.sleep(1) # kill is asynchronous top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testscriptB)) self.assertTrue(greps(top, testsleepB)) # log = lines(open(logfile).read()) logg.info("LOG %s\n| %s", logfile, "\n| ".join(log)) # self.assertTrue(greps(log, "ignored")) # self.assertTrue(greps(log, "sighup")) # time.sleep(1) # kill is asynchronous top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) cmd = "killall {testsleepB}" sx____(cmd.format(**locals())) # cleanup before check self.assertFalse(greps(top, testscriptB)) self.assertTrue(greps(top, testsleepB)) ##TODO?## # self.rm_testdir() self.coverage() self.end() def test_4161_systemctl_kill_mixed_hard(self): """ systemctl kill needs to be hard""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) quick = "--coverage=quick" systemctl = cover() + _systemctl_py + " --root=" + root testsleep = self.testname("testsleep") testsleepB = testsleep+"B" testsleepC = testsleep+"C" testscriptB = self.testname("testscriptB.sh") testscriptC = self.testname("testscriptC.sh") logfile = os_path(root, "/var/log/test.log") bindir = os_path(root, "/usr/bin") begin = "{" end = "}" text_file(logfile, "") text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStartPre=/bin/echo %n ExecStart={bindir}/{testscriptB} 111 ExecStartPost=/bin/echo started $MAINPID ExecStop=/bin/kill -3 $MAINPID ExecStopPost=/bin/echo stopped $MAINPID ExecStopPost=/bin/sleep 2 ExecReload=/bin/kill -10 $MAINPID KillMode=mixed KillSignal=SIGQUIT # SendSIGHUP=yes [Install] WantedBy=multi-user.target """.format(**locals())) shell_file(os_path(bindir, testscriptB),""" #! /bin/sh date +%T,enter > {logfile} stops () {begin} date +%T,stopfails >> {logfile} # killall {testsleep} ############## kill ignored {end} reload () {begin} date +%T,reloading >> {logfile} date +%T,reloaded >> {logfile} {end} ignored () {begin} date +%T,ignored >> {logfile} {end} sighup () {begin} date +%T,sighup >> {logfile} {end} trap "stops" 3 # SIGQUIT trap "reload" 10 # SIGUSR1 trap "ignored" 15 # SIGTERM trap "sighup" 1 # SIGHUP date +%T,starting >> {logfile} {bindir}/{testsleepB} $1 >> {logfile} 2>&1 & while kill -0 $!; do # use 'kill -0' to check the existance of the child date +%T,waiting >> {logfile} # use 'wait' for children AND external signals wait done date +%T,leaving >> {logfile} trap - 3 10 15 # SIGQUIT SIGUSR1 SIGTERM date +%T,leave >> {logfile} """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleepB)) copy_tool("/usr/bin/sleep", os_path(bindir, testsleepC)) copy_file(os_path(testdir, "zzb.service"), os_path(root, "/etc/systemd/system/zzb.service")) # cmd = "{systemctl} start zzb.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleepB)) # cmd = "{systemctl} stop zzb.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testscriptB)) self.assertTrue(greps(top, testsleepB)) # cmd = "{systemctl} kill zzb.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # actually killed # time.sleep(1) # kill is asynchronous top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testscriptB)) self.assertFalse(greps(top, testsleepB)) ##TODO?## # log = lines(open(logfile).read()) logg.info("LOG %s\n| %s", logfile, "\n| ".join(log)) # self.assertTrue(greps(log, "ignored")) # self.assertTrue(greps(log, "sighup")) # time.sleep(1) # kill is asynchronous top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) cmd = "killall {testsleepB}" sx____(cmd.format(**locals())) # cleanup before check self.assertFalse(greps(top, testscriptB)) self.assertFalse(greps(top, testsleepB)) # self.rm_testdir() self.coverage() self.end() def test_4201_systemctl_py_dependencies_plain_start_order(self): """ check list-dependencies - standard order of starting units is simply the command line order""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root logfile = os_path(root, "/var/log/"+testname+".log") testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zza.service"),""" [Unit] Description=Testing A [Service] Type=simple ExecStartPre={bindir}/logger 'start-A' ExecStart={bindir}/{testsleep} 30 ExecStopPost={bindir}/logger 'stop-A' [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStartPre={bindir}/logger 'start-B' ExecStart={bindir}/{testsleep} 99 ExecStopPost={bindir}/logger 'stop-B' [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C [Service] Type=simple ExecStartPre={bindir}/logger 'start-C' ExecStart={bindir}/{testsleep} 111 ExecStopPost={bindir}/logger 'stop-C' [Install] WantedBy=multi-user.target """.format(**locals())) shell_file(os_path(testdir, "logger"),""" #! /bin/sh echo "$@" >> {logfile} cat {logfile} | sed -e "s|^| : |" true """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_tool(os_path(testdir, "logger"), os_path(bindir, "logger")) copy_file(os_path(testdir, "zza.service"), os_path(root, "/etc/systemd/system/zza.service")) copy_file(os_path(testdir, "zzb.service"), os_path(root, "/etc/systemd/system/zzb.service")) copy_file(os_path(testdir, "zzc.service"), os_path(root, "/etc/systemd/system/zzc.service")) os.makedirs(os_path(root, "/var/run")) os.makedirs(os_path(root, "/var/log")) # list_dependencies = "{systemctl} list-dependencies zza.service --now" deps = output(list_dependencies.format(**locals())) logg.info("deps \n%s", deps) # cmd = "{systemctl} start zza.service zzb.service zzc.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep+" 99")) # # inspect logfile log = lines(open(logfile)) logg.info("logs \n| %s", "\n| ".join(log)) self.assertEqual(log[0], "start-A") self.assertEqual(log[1], "start-B") self.assertEqual(log[2], "start-C") os.remove(logfile) # cmd = "{systemctl} stop zza.service zzb.service zzc.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep+" 99")) # # inspect logfile log = lines(open(logfile)) logg.info("logs \n| %s", "\n| ".join(log)) self.assertEqual(log[0], "stop-A") self.assertEqual(log[1], "stop-B") self.assertEqual(log[2], "stop-C") os.remove(logfile) # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_4211_systemctl_py_dependencies_basic_reorder(self): """ check list-dependencies - standard order of starting units is simply the command line order (After case)""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root logfile = os_path(root, "/var/log/"+testname+".log") testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zza.service"),""" [Unit] Description=Testing A After=zzb.service [Service] Type=simple ExecStartPre={bindir}/logger 'start-A' ExecStart={bindir}/{testsleep} 30 ExecStopPost={bindir}/logger 'stop-A' [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStartPre={bindir}/logger 'start-B' ExecStart={bindir}/{testsleep} 99 ExecStopPost={bindir}/logger 'stop-B' [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C After=zza.service [Service] Type=simple ExecStartPre={bindir}/logger 'start-C' ExecStart={bindir}/{testsleep} 111 ExecStopPost={bindir}/logger 'stop-C' [Install] WantedBy=multi-user.target """.format(**locals())) shell_file(os_path(testdir, "logger"),""" #! /bin/sh echo "$@" >> {logfile} cat {logfile} | sed -e "s|^| : |" true """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_tool(os_path(testdir, "logger"), os_path(bindir, "logger")) copy_file(os_path(testdir, "zza.service"), os_path(root, "/etc/systemd/system/zza.service")) copy_file(os_path(testdir, "zzb.service"), os_path(root, "/etc/systemd/system/zzb.service")) copy_file(os_path(testdir, "zzc.service"), os_path(root, "/etc/systemd/system/zzc.service")) os.makedirs(os_path(root, "/var/run")) os.makedirs(os_path(root, "/var/log")) # list_dependencies = "{systemctl} list-dependencies zza.service --now" deps = output(list_dependencies.format(**locals())) logg.info("deps \n%s", deps) # cmd = "{systemctl} start zza.service zzb.service zzc.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep+" 99")) # # inspect logfile log = lines(open(logfile)) logg.info("logs \n| %s", "\n| ".join(log)) self.assertEqual(log[0], "start-B") self.assertEqual(log[1], "start-A") self.assertEqual(log[2], "start-C") os.remove(logfile) # cmd = "{systemctl} stop zza.service zzb.service zzc.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep+" 99")) # # inspect logfile log = lines(open(logfile)) logg.info("logs \n| %s", "\n| ".join(log)) self.assertEqual(log[0], "stop-C") self.assertEqual(log[1], "stop-A") self.assertEqual(log[2], "stop-B") os.remove(logfile) # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_4251_systemctl_py_dependencies_basic_reorder(self): """ check list-dependencies - standard order of starting units is simply the command line order (Before case)""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root logfile = os_path(root, "/var/log/"+testname+".log") testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zza.service"),""" [Unit] Description=Testing A Before=zzb.service [Service] Type=simple ExecStartPre={bindir}/logger 'start-A' ExecStart={bindir}/{testsleep} 30 ExecStopPost={bindir}/logger 'stop-A' [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStartPre={bindir}/logger 'start-B' ExecStart={bindir}/{testsleep} 99 ExecStopPost={bindir}/logger 'stop-B' [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C Before=zza.service [Service] Type=simple ExecStartPre={bindir}/logger 'start-C' ExecStart={bindir}/{testsleep} 111 ExecStopPost={bindir}/logger 'stop-C' [Install] WantedBy=multi-user.target """.format(**locals())) shell_file(os_path(testdir, "logger"),""" #! /bin/sh echo "$@" >> {logfile} cat {logfile} | sed -e "s|^| : |" true """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_tool(os_path(testdir, "logger"), os_path(bindir, "logger")) copy_file(os_path(testdir, "zza.service"), os_path(root, "/etc/systemd/system/zza.service")) copy_file(os_path(testdir, "zzb.service"), os_path(root, "/etc/systemd/system/zzb.service")) copy_file(os_path(testdir, "zzc.service"), os_path(root, "/etc/systemd/system/zzc.service")) os.makedirs(os_path(root, "/var/run")) os.makedirs(os_path(root, "/var/log")) # list_dependencies = "{systemctl} list-dependencies zza.service --now" deps = output(list_dependencies.format(**locals())) logg.info("deps \n%s", deps) # cmd = "{systemctl} start zza.service zzb.service zzc.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, testsleep+" 99")) # # inspect logfile log = lines(open(logfile)) logg.info("logs \n| %s", "\n| ".join(log)) self.assertEqual(log[0], "start-C") self.assertEqual(log[1], "start-A") self.assertEqual(log[2], "start-B") os.remove(logfile) # cmd = "{systemctl} stop zza.service zzb.service zzc.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) time.sleep(1) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, testsleep+" 99")) # # inspect logfile log = lines(open(logfile)) logg.info("logs \n| %s", "\n| ".join(log)) self.assertEqual(log[0], "stop-B") self.assertEqual(log[1], "stop-A") self.assertEqual(log[2], "stop-C") os.remove(logfile) # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_4301_systemctl_py_list_dependencies_with_after(self): """ check list-dependencies - standard order of starting units is simply the command line order""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root logfile = os_path(root, "/var/log/"+testname+".log") testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zza.service"),""" [Unit] Description=Testing A After=zzb.service [Service] Type=simple ExecStartPre={bindir}/logger 'start-A' ExecStart={bindir}/{testsleep} 30 ExecStopPost={bindir}/logger 'stop-A' [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStartPre={bindir}/logger 'start-B' ExecStart={bindir}/{testsleep} 99 ExecStopPost={bindir}/logger 'stop-B' [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C After=zza.service [Service] Type=simple ExecStartPre={bindir}/logger 'start-C' ExecStart={bindir}/{testsleep} 111 ExecStopPost={bindir}/logger 'stop-C' [Install] WantedBy=multi-user.target """.format(**locals())) shell_file(os_path(testdir, "logger"),""" #! /bin/sh echo "$@" >> {logfile} cat {logfile} | sed -e "s|^| : |" true """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_tool(os_path(testdir, "logger"), os_path(bindir, "logger")) copy_file(os_path(testdir, "zza.service"), os_path(root, "/etc/systemd/system/zza.service")) copy_file(os_path(testdir, "zzb.service"), os_path(root, "/etc/systemd/system/zzb.service")) copy_file(os_path(testdir, "zzc.service"), os_path(root, "/etc/systemd/system/zzc.service")) os.makedirs(os_path(root, "/var/run")) os.makedirs(os_path(root, "/var/log")) # list_dependencies = "{systemctl} list-dependencies zza.service --now" deps_text = output(list_dependencies.format(**locals())) # logg.info("deps \n%s", deps_text) # # inspect logfile deps = lines(deps_text) logg.info("deps \n| %s", "\n| ".join(deps)) self.assertEqual(deps[0], "zza.service\t(Requested)") self.assertEqual(len(deps), 1) # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_4302_systemctl_py_list_dependencies_with_wants(self): """ check list-dependencies - standard order of starting units is simply the command line order""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root logfile = os_path(root, "/var/log/"+testname+".log") testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zza.service"),""" [Unit] Description=Testing A Wants=zzb.service [Service] Type=simple ExecStartPre={bindir}/logger 'start-A' ExecStart={bindir}/{testsleep} 30 ExecStopPost={bindir}/logger 'stop-A' [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStartPre={bindir}/logger 'start-B' ExecStart={bindir}/{testsleep} 99 ExecStopPost={bindir}/logger 'stop-B' [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C Wants=zza.service [Service] Type=simple ExecStartPre={bindir}/logger 'start-C' ExecStart={bindir}/{testsleep} 111 ExecStopPost={bindir}/logger 'stop-C' [Install] WantedBy=multi-user.target """.format(**locals())) shell_file(os_path(testdir, "logger"),""" #! /bin/sh echo "$@" >> {logfile} cat {logfile} | sed -e "s|^| : |" true """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_tool(os_path(testdir, "logger"), os_path(bindir, "logger")) copy_file(os_path(testdir, "zza.service"), os_path(root, "/etc/systemd/system/zza.service")) copy_file(os_path(testdir, "zzb.service"), os_path(root, "/etc/systemd/system/zzb.service")) copy_file(os_path(testdir, "zzc.service"), os_path(root, "/etc/systemd/system/zzc.service")) os.makedirs(os_path(root, "/var/run")) os.makedirs(os_path(root, "/var/log")) # list_dependencies = "{systemctl} list-dependencies zza.service --now" deps_text = output(list_dependencies.format(**locals())) # logg.info("deps \n%s", deps_text) # # inspect logfile deps = lines(deps_text) logg.info("deps \n| %s", "\n| ".join(deps)) self.assertEqual(deps[0], "zzb.service\t(Wants)") self.assertEqual(deps[1], "zza.service\t(Requested)") self.assertEqual(len(deps), 2) # list_dependencies = "{systemctl} list-dependencies zzb.service --now" deps_text = output(list_dependencies.format(**locals())) # logg.info("deps \n%s", deps_text) # # inspect logfile deps = lines(deps_text) logg.info("deps \n| %s", "\n| ".join(deps)) self.assertEqual(deps[0], "zzb.service\t(Requested)") self.assertEqual(len(deps), 1) # # list_dependencies = "{systemctl} list-dependencies zzc.service --now" deps_text = output(list_dependencies.format(**locals())) # logg.info("deps \n%s", deps_text) # # inspect logfile deps = lines(deps_text) logg.info("deps \n| %s", "\n| ".join(deps)) self.assertEqual(deps[0], "zza.service\t(Wants)") self.assertEqual(deps[1], "zzc.service\t(Requested)") self.assertEqual(len(deps), 2) # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_4303_systemctl_py_list_dependencies_with_requires(self): """ check list-dependencies - standard order of starting units is simply the command line order""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root logfile = os_path(root, "/var/log/"+testname+".log") testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zza.service"),""" [Unit] Description=Testing A Requires=zzb.service [Service] Type=simple ExecStartPre={bindir}/logger 'start-A' ExecStart={bindir}/{testsleep} 30 ExecStopPost={bindir}/logger 'stop-A' [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStartPre={bindir}/logger 'start-B' ExecStart={bindir}/{testsleep} 99 ExecStopPost={bindir}/logger 'stop-B' [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C Requires=zza.service [Service] Type=simple ExecStartPre={bindir}/logger 'start-C' ExecStart={bindir}/{testsleep} 111 ExecStopPost={bindir}/logger 'stop-C' [Install] WantedBy=multi-user.target """.format(**locals())) shell_file(os_path(testdir, "logger"),""" #! /bin/sh echo "$@" >> {logfile} cat {logfile} | sed -e "s|^| : |" true """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_tool(os_path(testdir, "logger"), os_path(bindir, "logger")) copy_file(os_path(testdir, "zza.service"), os_path(root, "/etc/systemd/system/zza.service")) copy_file(os_path(testdir, "zzb.service"), os_path(root, "/etc/systemd/system/zzb.service")) copy_file(os_path(testdir, "zzc.service"), os_path(root, "/etc/systemd/system/zzc.service")) os.makedirs(os_path(root, "/var/run")) os.makedirs(os_path(root, "/var/log")) # list_dependencies = "{systemctl} list-dependencies zza.service --now" deps_text = output(list_dependencies.format(**locals())) # logg.info("deps \n%s", deps_text) # # inspect logfile deps = lines(deps_text) logg.info("deps \n| %s", "\n| ".join(deps)) self.assertEqual(deps[0], "zzb.service\t(Requires)") self.assertEqual(deps[1], "zza.service\t(Requested)") self.assertEqual(len(deps), 2) # list_dependencies = "{systemctl} list-dependencies zzb.service --now" deps_text = output(list_dependencies.format(**locals())) # logg.info("deps \n%s", deps_text) # # inspect logfile deps = lines(deps_text) logg.info("deps \n| %s", "\n| ".join(deps)) self.assertEqual(deps[0], "zzb.service\t(Requested)") self.assertEqual(len(deps), 1) # # list_dependencies = "{systemctl} list-dependencies zzc.service --now" deps_text = output(list_dependencies.format(**locals())) # logg.info("deps \n%s", deps_text) # # inspect logfile deps = lines(deps_text) logg.info("deps \n| %s", "\n| ".join(deps)) self.assertEqual(deps[0], "zza.service\t(Requires)") self.assertEqual(deps[1], "zzc.service\t(Requested)") self.assertEqual(len(deps), 2) # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_4401_systemctl_py_list_dependencies_with_after(self): """ check list-dependencies - standard order of starting units is simply the command line order""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root logfile = os_path(root, "/var/log/"+testname+".log") testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zza.service"),""" [Unit] Description=Testing A After=zzb.service [Service] Type=simple ExecStartPre={bindir}/logger 'start-A' ExecStart={bindir}/{testsleep} 30 ExecStopPost={bindir}/logger 'stop-A' [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStartPre={bindir}/logger 'start-B' ExecStart={bindir}/{testsleep} 99 ExecStopPost={bindir}/logger 'stop-B' [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C After=zza.service [Service] Type=simple ExecStartPre={bindir}/logger 'start-C' ExecStart={bindir}/{testsleep} 111 ExecStopPost={bindir}/logger 'stop-C' [Install] WantedBy=multi-user.target """.format(**locals())) shell_file(os_path(testdir, "logger"),""" #! /bin/sh echo "$@" >> {logfile} cat {logfile} | sed -e "s|^| : |" true """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_tool(os_path(testdir, "logger"), os_path(bindir, "logger")) copy_file(os_path(testdir, "zza.service"), os_path(root, "/etc/systemd/system/zza.service")) copy_file(os_path(testdir, "zzb.service"), os_path(root, "/etc/systemd/system/zzb.service")) copy_file(os_path(testdir, "zzc.service"), os_path(root, "/etc/systemd/system/zzc.service")) os.makedirs(os_path(root, "/var/run")) os.makedirs(os_path(root, "/var/log")) # list_dependencies = "{systemctl} list-dependencies zza.service" deps_text = output(list_dependencies.format(**locals())) # logg.info("deps \n%s", deps_text) # # inspect logfile deps = lines(deps_text) logg.info("deps \n| %s", "\n| ".join(deps)) self.assertEqual(deps[0], "zza.service:") self.assertEqual(len(deps), 1) # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_4402_systemctl_py_list_dependencies_with_wants(self): """ check list-dependencies - standard order of starting units is simply the command line order""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root logfile = os_path(root, "/var/log/"+testname+".log") testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zza.service"),""" [Unit] Description=Testing A Wants=zzb.service [Service] Type=simple ExecStartPre={bindir}/logger 'start-A' ExecStart={bindir}/{testsleep} 30 ExecStopPost={bindir}/logger 'stop-A' [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStartPre={bindir}/logger 'start-B' ExecStart={bindir}/{testsleep} 99 ExecStopPost={bindir}/logger 'stop-B' [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C Wants=zza.service [Service] Type=simple ExecStartPre={bindir}/logger 'start-C' ExecStart={bindir}/{testsleep} 111 ExecStopPost={bindir}/logger 'stop-C' [Install] WantedBy=multi-user.target """.format(**locals())) shell_file(os_path(testdir, "logger"),""" #! /bin/sh echo "$@" >> {logfile} cat {logfile} | sed -e "s|^| : |" true """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_tool(os_path(testdir, "logger"), os_path(bindir, "logger")) copy_file(os_path(testdir, "zza.service"), os_path(root, "/etc/systemd/system/zza.service")) copy_file(os_path(testdir, "zzb.service"), os_path(root, "/etc/systemd/system/zzb.service")) copy_file(os_path(testdir, "zzc.service"), os_path(root, "/etc/systemd/system/zzc.service")) os.makedirs(os_path(root, "/var/run")) os.makedirs(os_path(root, "/var/log")) # list_dependencies = "{systemctl} list-dependencies zza.service" deps_text = output(list_dependencies.format(**locals())) # logg.info("deps \n%s", deps_text) # # inspect logfile deps = lines(deps_text) logg.info("deps \n| %s", "\n| ".join(deps)) self.assertEqual(deps[0], "zza.service:") self.assertEqual(deps[1], "| zzb.service: wanted to start") self.assertEqual(len(deps), 2) # list_dependencies = "{systemctl} list-dependencies zzb.service" deps_text = output(list_dependencies.format(**locals())) # logg.info("deps \n%s", deps_text) # # inspect logfile deps = lines(deps_text) logg.info("deps \n| %s", "\n| ".join(deps)) self.assertEqual(deps[0], "zzb.service:") self.assertEqual(len(deps), 1) # # list_dependencies = "{systemctl} list-dependencies zzc.service" deps_text = output(list_dependencies.format(**locals())) # logg.info("deps \n%s", deps_text) # # inspect logfile deps = lines(deps_text) logg.info("deps \n| %s", "\n| ".join(deps)) self.assertEqual(deps[0], "zzc.service:") self.assertEqual(deps[1], "| zza.service: wanted to start") self.assertEqual(deps[2], "| | zzb.service: wanted to start") self.assertEqual(len(deps), 3) # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_4403_systemctl_py_list_dependencies_with_requires(self): """ check list-dependencies - standard order of starting units is simply the command line order""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root logfile = os_path(root, "/var/log/"+testname+".log") testsleep = self.testname("sleep") bindir = os_path(root, "/usr/bin") text_file(os_path(testdir, "zza.service"),""" [Unit] Description=Testing A Requires=zzb.service [Service] Type=simple ExecStartPre={bindir}/logger 'start-A' ExecStart={bindir}/{testsleep} 30 ExecStopPost={bindir}/logger 'stop-A' [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStartPre={bindir}/logger 'start-B' ExecStart={bindir}/{testsleep} 99 ExecStopPost={bindir}/logger 'stop-B' [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C Requires=zza.service [Service] Type=simple ExecStartPre={bindir}/logger 'start-C' ExecStart={bindir}/{testsleep} 111 ExecStopPost={bindir}/logger 'stop-C' [Install] WantedBy=multi-user.target """.format(**locals())) shell_file(os_path(testdir, "logger"),""" #! /bin/sh echo "$@" >> {logfile} cat {logfile} | sed -e "s|^| : |" true """.format(**locals())) copy_tool("/usr/bin/sleep", os_path(bindir, testsleep)) copy_tool(os_path(testdir, "logger"), os_path(bindir, "logger")) copy_file(os_path(testdir, "zza.service"), os_path(root, "/etc/systemd/system/zza.service")) copy_file(os_path(testdir, "zzb.service"), os_path(root, "/etc/systemd/system/zzb.service")) copy_file(os_path(testdir, "zzc.service"), os_path(root, "/etc/systemd/system/zzc.service")) os.makedirs(os_path(root, "/var/run")) os.makedirs(os_path(root, "/var/log")) # list_dependencies = "{systemctl} list-dependencies zza.service" deps_text = output(list_dependencies.format(**locals())) # logg.info("deps \n%s", deps_text) # # inspect logfile deps = lines(deps_text) logg.info("deps \n| %s", "\n| ".join(deps)) self.assertEqual(deps[0], "zza.service:") self.assertEqual(deps[1], "| zzb.service: required to start") self.assertEqual(len(deps), 2) # list_dependencies = "{systemctl} list-dependencies zzb.service" deps_text = output(list_dependencies.format(**locals())) # logg.info("deps \n%s", deps_text) # # inspect logfile deps = lines(deps_text) logg.info("deps \n| %s", "\n| ".join(deps)) self.assertEqual(deps[0], "zzb.service:") self.assertEqual(len(deps), 1) # # list_dependencies = "{systemctl} list-dependencies zzc.service" deps_text = output(list_dependencies.format(**locals())) # logg.info("deps \n%s", deps_text) # # inspect logfile deps = lines(deps_text) logg.info("deps \n| %s", "\n| ".join(deps)) self.assertEqual(deps[0], "zzc.service:") self.assertEqual(deps[1], "| zza.service: required to start") self.assertEqual(deps[2], "| | zzb.service: required to start") self.assertEqual(len(deps), 3) # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) self.rm_testdir() self.coverage() self.end() def test_4900_unreadable_files_can_be_handled(self): """ a file may exist but it is unreadable""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root logfile = os_path(root, "/var/log/"+testname+".log") text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A Requires=zzb.service [Service] Type=simple ExecStart=/bin/sleep 10 [Install] WantedBy=multi-user.target """.format(**locals())) text_file(os_path(root, "/etc/systemd/system-preset/our.preset"),""" enable zza.service disable zzb.service""") os.makedirs(os_path(root, "/var/run")) os.makedirs(os_path(root, "/var/log")) # os.chmod(os_path(root, "/etc/systemd/system/zza.service"), 0222) # cmd = "{systemctl} start zza" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "{systemctl} start zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) cmd = "{systemctl} stop zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) cmd = "{systemctl} reload zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) cmd = "{systemctl} restart zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) cmd = "{systemctl} try-restart zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) cmd = "{systemctl} reload-or-restart zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) cmd = "{systemctl} reload-or-try-restart zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) cmd = "{systemctl} kill zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) # cmd = "{systemctl} is-active zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) self.assertTrue(greps(out, "unknown")) cmd = "{systemctl} is-failed zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 1) self.assertTrue(greps(out, "unknown")) # cmd = "{systemctl} status zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) self.assertTrue(greps(out, "zza.service - NOT-FOUND")) # cmd = "{systemctl} show zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # shows not-found state ok self.assertTrue(greps(out, "LoadState=not-found")) # cmd = "{systemctl} cat zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) self.assertTrue(greps(out, "Unit zza.service is not-loaded")) # cmd = "{systemctl} list-dependencies zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # always succeeds # cmd = "{systemctl} enable zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) cmd = "{systemctl} disable zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) cmd = "{systemctl} is-enabled zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # ok # cmd = "{systemctl} preset zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) cmd = "{systemctl} preset-all" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) # cmd = "{systemctl} daemon-reload" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # always succeeds # self.rm_testdir() self.coverage() self.end() def test_4901_unsupported_run_type_for_service(self): """ a service file may exist but the run type is not supported""" self.begin() testname = self.testname() testdir = self.testdir() user = self.user() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root logfile = os_path(root, "/var/log/"+testname+".log") text_file(os_path(root, "/etc/systemd/system/zza.service"),""" [Unit] Description=Testing A Requires=zzb.service [Service] Type=foo ExecStart=/bin/sleep 10 ExecStop=/bin/kill $MAINPID [Install] WantedBy=multi-user.target """.format(**locals())) # cmd = "{systemctl} start zza" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) cmd = "{systemctl} start zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) cmd = "{systemctl} stop zza.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) cmd = "{systemctl} reload zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) cmd = "{systemctl} restart zza.service" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) self.rm_testdir() self.coverage() self.end() # # ########################################################################### # # INSIDE A CONTAINER # ########################################################################### # # def prep_coverage(self, testname, cov_option = None): """ install a shell-wrapper /usr/bin/systemctl (testdir/systemctl.sh) which calls the develop systemctl.py prefixed by our coverage tool. . The weird name for systemctl_py_run is special for save_coverage(). We take the realpath of our develop systemctl.py on purpose here. """ testdir = self.testdir(testname, keep = True) cov_run = cover() cov_option = cov_option or "" systemctl_py = realpath(_systemctl_py) systemctl_sh = os_path(testdir, "systemctl.sh") systemctl_py_run = systemctl_py.replace("/","_")[1:] shell_file(systemctl_sh,""" #! /bin/sh cd /tmp exec {cov_run} /{systemctl_py_run} "$@" -vv {cov_option} """.format(**locals())) cmd = "docker cp {systemctl_py} {testname}:/{systemctl_py_run}" sh____(cmd.format(**locals())) cmd = "docker cp {systemctl_sh} {testname}:/usr/bin/systemctl" sh____(cmd.format(**locals())) def save_coverage(self, *testnames): """ Copying the image's /tmp/.coverage to our local ./.coverage.image file. Since the path of systemctl.py inside the container is different than our develop systemctl.py we have to patch the .coverage file. . Some older coverage2 did use a binary format, so we had ensured the path of systemctl.py inside the container has the exact same length as the realpath of our develop systemctl.py outside the container. That way 'coverage combine' maps the results correctly.""" if not COVERAGE: return systemctl_py = realpath(_systemctl_py) systemctl_py_run = systemctl_py.replace("/","_")[1:] for testname in testnames: coverage_file = ".coverage." + testname cmd = "docker cp {testname}:/tmp/.coverage .coverage.{testname}" sh____(cmd.format(**locals())) cmd = "sed -i -e 's:/{systemctl_py_run}:{systemctl_py}:' .coverage.{testname}" sh____(cmd.format(**locals())) # def test_5000_systemctl_py_inside_container(self): """ check that we can run systemctl.py inside a docker container """ if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(IMAGE or CENTOS) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") testname = self.testname() testdir = self.testdir() package = package_tool(image) refresh = refresh_tool(image) python = os.path.basename(_python) python_coverage = coverage_package(image) systemctl_py = _systemctl_py sometime = SOMETIME or 188 # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) cmd = "docker cp {systemctl_py} {testname}:/usr/bin/systemctl" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) out = output(cmd.format(**locals())) logg.info("\n>\n%s", out) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) self.assertTrue(greps(out, "systemctl.py")) # self.rm_testdir() def test_5001_coverage_systemctl_py_inside_container(self): """ check that we can run systemctl.py with coverage inside a docker container """ if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) # <<<< need to use COVERAGE image here if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") testname = self.testname() testdir = self.testdir() package = package_tool(image) refresh = refresh_tool(image) python = os.path.basename(_python) python_coverage = coverage_package(image) # <<<< and install the tool for the COVERAGE image systemctl_py = _systemctl_py sometime = SOMETIME or 188 # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" # <<<< like here sx____(cmd.format(**locals())) self.prep_coverage(testname) ### setup a shell-wrapper /usr/bin/systemctl calling systemctl.py cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) out = output(cmd.format(**locals())) logg.info("\n>\n%s", out) # self.save_coverage(testname) ### fetch {image}:.coverage and set path to develop systemctl.py # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) self.assertTrue(greps(out, "systemctl.py")) # self.rm_testdir() def test_5002_systemctl_py_enable_in_container(self): """ check that we can enable services in a docker container """ if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") testname = self.testname() testdir = self.testdir() package = package_tool(image) refresh = refresh_tool(image) python = os.path.basename(_python) python_coverage = coverage_package(image) systemctl_py = _systemctl_py sometime = SOMETIME or 188 text_file(os_path(testdir, "zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Install] WantedBy=multi-user.target""") text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C [Install] WantedBy=multi-user.target""") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker exec {testname} mkdir -p /etc/systemd/system" sx____(cmd.format(**locals())) cmd = "docker cp {testdir}/zza.service {testname}:/etc/systemd/system/zza.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzb.service {testname}:/etc/systemd/system/zzb.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzc.service {testname}:/etc/systemd/system/zzc.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzc.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) list_units_systemctl = "docker exec {testname} systemctl list-unit-files" # sh____(list_units_systemctl.format(**locals())) out = output(list_units_systemctl.format(**locals())) logg.info("\n>\n%s", out) # self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) self.assertTrue(greps(out, "zza.service.*static")) self.assertTrue(greps(out, "zzb.service.*disabled")) self.assertTrue(greps(out, "zzc.service.*enabled")) # self.rm_testdir() def test_5003_systemctl_py_default_services_in_container(self): """ check that we can enable services in a docker container to have default-services""" if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") testname = self.testname() testdir = self.testdir() package = package_tool(image) refresh = refresh_tool(image) python = os.path.basename(_python) python_coverage = coverage_package(image) systemctl_py = _systemctl_py sometime = SOMETIME or 188 text_file(os_path(testdir, "zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Install] WantedBy=multi-user.target""") text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C [Install] WantedBy=multi-user.target""") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker exec {testname} mkdir -p /etc/systemd/system" sx____(cmd.format(**locals())) cmd = "docker cp {testdir}/zza.service {testname}:/etc/systemd/system/zza.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzb.service {testname}:/etc/systemd/system/zzb.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzc.service {testname}:/etc/systemd/system/zzc.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzb.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzc.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl default-services -vv" out2 = output(cmd.format(**locals())) logg.info("\n>\n%s", out2) cmd = "docker exec {testname} systemctl --all default-services -vv" out3 = output(cmd.format(**locals())) logg.info("\n>\n%s", out3) # self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) self.assertTrue(greps(out2, "zzb.service")) self.assertTrue(greps(out2, "zzc.service")) self.assertEqual(len(lines(out2)), 2) self.assertTrue(greps(out3, "zzb.service")) self.assertTrue(greps(out3, "zzc.service")) # self.assertGreater(len(lines(out2)), 2) # self.rm_testdir() # # # compare the following with the test_4030 series # # def test_5030_simple_service_functions_system(self): """ check that we manage simple services in a root env with commands like start, restart, stop, etc""" self.begin() testname = self.testname() testdir = self.testdir() self.runuser_simple_service_functions("system", testname, testdir) self.rm_testdir() self.end() def test_5031_runuser_simple_service_functions_user(self): """ check that we manage simple services in a root env with commands like start, restart, stop, etc""" self.begin() testname = self.testname() testdir = self.testdir() self.runuser_simple_service_functions("user", testname, testdir) self.rm_testdir() self.end() def runuser_simple_service_functions(self, system, testname, testdir): """ check that we manage simple services in a root env with commands like start, restart, stop, etc""" if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) python = os.path.basename(_python) python_coverage = coverage_package(image) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) sometime = SOMETIME or 188 quick = "--coverage=quick" # user = self.user() root = "" systemctl_py = realpath(_systemctl_py) systemctl = "/usr/bin/systemctl" systemctl += " --{system}".format(**locals()) testsleep = testname+"_testsleep" testscript = testname+"_testscript.sh" logfile = os_path(root, "/var/log/test.log") bindir = os_path(root, "/usr/bin") begin = "{" end = "}" text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] User=somebody Type=simple ExecStartPre=/bin/echo %n ExecStart={bindir}/{testscript} 111 ExecStartPost=/bin/echo started $MAINPID ExecStop=/bin/kill -3 $MAINPID ExecStopPost=/bin/echo stopped $MAINPID ExecStopPost=/bin/sleep 2 ExecReload=/bin/kill -10 $MAINPID KillSignal=SIGQUIT [Install] WantedBy=multi-user.target """.format(**locals())) shell_file(os_path(testdir, testscript),""" #! /bin/sh date +%T,enter >> {logfile} stops () {begin} date +%T,stopping >> {logfile} killall {testsleep} >> {logfile} 2>&1 date +%T,stopped >> {logfile} {end} reload () {begin} date +%T,reloading >> {logfile} date +%T,reloaded >> {logfile} {end} trap "stops" 3 # SIGQUIT trap "reload" 10 # SIGUSR1 date +%T,starting >> {logfile} {bindir}/{testsleep} $1 >> {logfile} 2>&1 & pid="$!" while kill -0 $pid; do # use 'kill -0' to check the existance of the child date +%T,waiting >> {logfile} # use 'wait' for children AND external signals wait done date +%T,leaving >> {logfile} trap - 3 10 # SIGQUIT SIGUSR1 date +%T,leave >> {logfile} """.format(**locals())) cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/killall || {package} install -y psmisc'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker cp /usr/bin/sleep {testname}:{bindir}/{testsleep}" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/{testscript} {testname}:{bindir}/{testscript}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} mkdir -p /etc/systemd/system /etc/systemd/user" sx____(cmd.format(**locals())) zzz_service = "/etc/systemd/{system}/zzz.service".format(**locals()) cmd = "docker cp {testdir}/zzz.service {testname}:{zzz_service}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chmod 666 {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'grep nobody /etc/group || groupadd nobody'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} useradd somebody -g nobody -m" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} {systemctl} enable zzz.service -vv" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'start' shall start a service that is NOT is-active ") cmd = "docker exec {testname} {systemctl} start zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) self.assertEqual(end, 0) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") # logg.info("== 'stop' shall stop a service that is-active") cmd = "docker exec {testname} {systemctl} stop zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) time.sleep(1) # kill is async cmd = "docker exec {testname} cat {logfile}" sh____(cmd.format(**locals())) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # # inspect the service's log log = lines(output("docker exec {testname} cat {logfile}".format(**locals()))) logg.info("LOG\n %s", "\n ".join(log)) self.assertTrue(greps(log, "enter")) self.assertTrue(greps(log, "leave")) self.assertTrue(greps(log, "starting")) self.assertTrue(greps(log, "stopped")) self.assertFalse(greps(log, "reload")) sh____("docker exec {testname} truncate -s0 {logfile}".format(**locals())) # logg.info("== 'restart' shall start a service that NOT is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service -vvvv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top1= top # # inspect the service's log log = lines(output("docker exec {testname} cat {logfile}".format(**locals()))) logg.info("LOG\n %s", "\n ".join(log)) self.assertTrue(greps(log, "enter")) self.assertFalse(greps(log, "leave")) self.assertTrue(greps(log, "starting")) self.assertFalse(greps(log, "stopped")) self.assertFalse(greps(log, "reload")) sh____("docker exec {testname} truncate -s0 {logfile}".format(**locals())) # logg.info("== 'restart' shall restart a service that is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top2 = top # logg.info("-- and we check that there is a new PID for the service process") def find_pids(ps_output, command): pids = [] for line in _lines(ps_output): if command not in line: continue m = re.match(r"\s*[\d:]*\s+(\S+)\s+(\S+)\s+(.*)", line) pid, ppid, args = m.groups() # logg.info(" %s | %s | %s", pid, ppid, args) pids.append(pid) return pids ps1 = find_pids(top1, testsleep) ps2 = find_pids(top2, testsleep) logg.info("found PIDs %s and %s", ps1, ps2) self.assertTrue(len(ps1), 1) self.assertTrue(len(ps2), 1) self.assertNotEqual(ps1[0], ps2[0]) # # inspect the service's log log = lines(output("docker exec {testname} cat {logfile}".format(**locals()))) logg.info("LOG\n %s", "\n ".join(log)) self.assertTrue(greps(log, "enter")) self.assertTrue(greps(log, "starting")) self.assertFalse(greps(log, "reload")) sh____("docker exec {testname} truncate -s0 {logfile}".format(**locals())) # # logg.info("== 'reload' will NOT restart a service that is-active") cmd = "docker exec {testname} {systemctl} reload zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top3 = top # logg.info("-- and we check that there is NO new PID for the service process") ps3 = find_pids(top3, testsleep) logg.info("found PIDs %s and %s", ps2, ps3) self.assertTrue(len(ps2), 1) self.assertTrue(len(ps3), 1) self.assertEqual(ps2[0], ps3[0]) # # inspect the service's log log = lines(output("docker exec {testname} cat {logfile}".format(**locals()))) logg.info("LOG\n %s", "\n ".join(log)) self.assertFalse(greps(log, "enter")) self.assertFalse(greps(log, "leave")) self.assertFalse(greps(log, "starting")) self.assertFalse(greps(log, "stopped")) self.assertTrue(greps(log, "reload")) sh____("docker exec {testname} truncate -s0 {logfile}".format(**locals())) # logg.info("== 'reload-or-restart' will restart a service that is-active (if ExecReload)") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top4 = top # logg.info("-- and we check that there is NO new PID for the service process (if ExecReload)") ps4 = find_pids(top4, testsleep) logg.info("found PIDs %s and %s", ps3, ps4) self.assertTrue(len(ps3), 1) self.assertTrue(len(ps4), 1) self.assertEqual(ps3[0], ps4[0]) # logg.info("== 'kill' will bring is-active non-active as well (when the PID is known)") cmd = "docker exec {testname} {systemctl} kill zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'stop' will turn 'failed' to 'inactive' (when the PID is known)") cmd = "docker exec {testname} {systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) # no PID known so 'kill $MAINPID' fails cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'reload-or-try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'reload-or-restart' will start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top5 = top # logg.info("== 'reload-or-try-restart' will NOT restart an is-active service (with ExecReload)") cmd = "docker exec {testname} {systemctl} reload-or-try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top6 = top # logg.info("-- and we check that there is NO new PID for the service process (if ExecReload)") ps5 = find_pids(top5, testsleep) ps6 = find_pids(top6, testsleep) logg.info("found PIDs %s and %s", ps5, ps6) self.assertTrue(len(ps5), 1) self.assertTrue(len(ps6), 1) self.assertEqual(ps5[0], ps6[0]) # logg.info("== 'try-restart' will restart an is-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top7 = top # logg.info("-- and we check that there is a new PID for the service process") ps7 = find_pids(top7, testsleep) logg.info("found PIDs %s and %s", ps6, ps7) self.assertTrue(len(ps6), 1) self.assertTrue(len(ps7), 1) self.assertNotEqual(ps6[0], ps7[0]) # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) # self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) def test_5032_runuser_forking_service_functions_system(self): """ check that we manage forking services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart.""" self.begin() testname = self.testname() testdir = self.testdir() self.runuser_forking_service_functions("system", testname, testdir) self.rm_testdir() self.end() def test_5033_runuser_forking_service_functions_user(self): """ check that we manage forking services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart.""" self.begin() testname = self.testname() testdir = self.testdir() self.runuser_forking_service_functions("user", testname, testdir) self.rm_testdir() self.end() def runuser_forking_service_functions(self, system, testname, testdir): if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) python = os.path.basename(_python) python_coverage = coverage_package(image) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) sometime = SOMETIME or 188 quick = "--coverage=quick" # user = self.user() root = "" systemctl_py = realpath(_systemctl_py) systemctl = "/usr/bin/systemctl" # path in container systemctl += " --{system}".format(**locals()) testsleep = testname+"_sleep" logfile = os_path(root, "/var/log/"+testsleep+".log") bindir = os_path(root, "/usr/bin") begin = "{" ; end = "}" shell_file(os_path(testdir, "zzz.init"), """ #! /bin/bash logfile={logfile} start() {begin} [ -d /var/run ] || mkdir -p /var/run ({bindir}/{testsleep} 111 0<&- &>/dev/null & echo $! > /tmp/zzz.init.pid ) & wait %1 # ps -o pid,ppid,user,args {end} stop() {begin} killall {testsleep} {end} case "$1" in start) date "+START.%T" >> $logfile start >> $logfile 2>&1 date "+start.%T" >> $logfile ;; stop) date "+STOP.%T" >> $logfile stop >> $logfile 2>&1 date "+stop.%T" >> $logfile ;; restart) date "+RESTART.%T" >> $logfile stop >> $logfile 2>&1 start >> $logfile 2>&1 date "+.%T" >> $logfile ;; reload) date "+RELOAD.%T" >> $logfile echo "...." >> $logfile 2>&1 date "+reload.%T" >> $logfile ;; esac echo "done$1" >&2 exit 0 """.format(**locals())) text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] User=somebody Type=forking PIDFile=/tmp/zzz.init.pid ExecStart=/usr/bin/zzz.init start ExecStop=/usr/bin/zzz.init stop [Install] WantedBy=multi-user.target """.format(**locals())) cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/killall || {package} install -y psmisc'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker exec {testname} mkdir -p /etc/systemd/system /etc/systemd/user" sx____(cmd.format(**locals())) zzz_service = "/etc/systemd/{system}/zzz.service".format(**locals()) cmd = "docker cp /usr/bin/sleep {testname}:{bindir}/{testsleep}" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.init {testname}:/usr/bin/zzz.init" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.service {testname}:{zzz_service}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chmod 666 {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'grep nobody /etc/group || groupadd nobody'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} useradd somebody -g nobody -m" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} {systemctl} enable zzz.service -vv" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'start' shall start a service that is NOT is-active ") cmd = "docker exec {testname} {systemctl} start zzz.service -vvvv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") # logg.info("== 'stop' shall stop a service that is-active") cmd = "docker exec {testname} {systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'restart' shall start a service that NOT is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top1= top # logg.info("== 'restart' shall restart a service that is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top2 = top # logg.info("-- and we check that there is a new PID for the service process") def find_pids(ps_output, command): pids = [] for line in _lines(ps_output): if command not in line: continue m = re.match(r"\s*[\d:]*\s+(\S+)\s+(\S+)\s+(.*)", line) pid, ppid, args = m.groups() # logg.info(" %s | %s | %s", pid, ppid, args) pids.append(pid) return pids ps1 = find_pids(running(top1), testsleep) ps2 = find_pids(running(top2), testsleep) logg.info("found PIDs %s and %s", ps1, ps2) self.assertTrue(len(ps1), 1) self.assertTrue(len(ps2), 1) self.assertNotEqual(ps1[0], ps2[0]) # logg.info("== 'reload' will NOT restart a service that is-active") cmd = "docker exec {testname} {systemctl} reload zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top3 = top # logg.info("-- and we check that there is NO new PID for the service process") ps3 = find_pids(running(top3), testsleep) logg.info("found PIDs %s and %s", ps2, ps3) self.assertTrue(len(ps2), 1) self.assertTrue(len(ps3), 1) self.assertEqual(ps2[0], ps3[0]) # logg.info("== 'reload-or-restart' will restart a service that is-active (if no ExecReload)") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top4 = top # logg.info("-- and we check that there is a new PID for the service process (if no ExecReload)") ps4 = find_pids(running(top4), testsleep) logg.info("found PIDs %s and %s", ps3, ps4) self.assertTrue(len(ps3), 1) self.assertTrue(len(ps4), 1) self.assertNotEqual(ps3[0], ps4[0]) # logg.info("== 'kill' will bring is-active non-active as well (when the PID is known)") cmd = "docker exec {testname} {systemctl} kill zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "failed") # logg.info("== 'stop' will turn 'failed' to 'inactive' (when the PID is known)") cmd = "docker exec {testname} {systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'reload-or-try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'reload-or-restart' will start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top5 = top # logg.info("== 'reload-or-try-restart' will restart an is-active service (with no ExecReload)") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top6 = top # logg.info("-- and we check that there is a new PID for the service process (if no ExecReload)") ps5 = find_pids(running(top5), testsleep) ps6 = find_pids(running(top6), testsleep) logg.info("found PIDs %s and %s", ps5, ps6) self.assertTrue(len(ps5), 1) self.assertTrue(len(ps6), 1) self.assertNotEqual(ps5[0], ps6[0]) # logg.info("== 'try-restart' will restart an is-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top7 = top # logg.info("-- and we check that there is a new PID for the service process") ps7 = find_pids(running(top7), testsleep) logg.info("found PIDs %s and %s", ps6, ps7) self.assertTrue(len(ps6), 1) self.assertTrue(len(ps7), 1) self.assertNotEqual(ps6[0], ps7[0]) # logg.info("LOG\n%s", " "+output("docker exec {testname} cat {logfile}".format(**locals())).replace("\n","\n ")) self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) def test_5034_runuser_notify_service_functions_system(self): """ check that we manage notify services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart.""" self.begin() testname = self.testname() testdir = self.testdir() self.runuser_notify_service_functions("system", testname, testdir) self.rm_testdir() self.end() def test_5035_runuser_notify_service_functions_user(self): """ check that we manage notify services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart.""" self.begin() testname = self.testname() testdir = self.testdir() self.runuser_notify_service_functions("user", testname, testdir) self.rm_testdir() self.end(266) #TODO# too long? def runuser_notify_service_functions(self, system, testname, testdir): if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) python = os.path.basename(_python) python_coverage = coverage_package(image) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) sometime = SOMETIME or 288 quick = "--coverage=quick" # user = self.user() root = "" systemctl_py = realpath(_systemctl_py) systemctl = "/usr/bin/systemctl" # path in container systemctl += " --{system}".format(**locals()) testsleep = testname+"_sleep" logfile = os_path(root, "/var/log/"+testsleep+".log") bindir = os_path(root, "/usr/bin") begin = "{" ; end = "}" shell_file(os_path(testdir, "zzz.init"), """ #! /bin/bash logfile={logfile} start() {begin} ls -l $NOTIFY_SOCKET {bindir}/{testsleep} 111 0<&- &>/dev/null & echo "MAINPID=$!" | socat -v -d - UNIX-CLIENT:$NOTIFY_SOCKET echo "READY=1" | socat -v -d - UNIX-CLIENT:$NOTIFY_SOCKET wait %1 # ps -o pid,ppid,user,args {end} stop() {begin} killall {testsleep} {end} case "$1" in start) date "+START.%T" >> $logfile start >> $logfile 2>&1 date "+start.%T" >> $logfile ;; stop) date "+STOP.%T" >> $logfile stop >> $logfile 2>&1 date "+stop.%T" >> $logfile ;; restart) date "+RESTART.%T" >> $logfile stop >> $logfile 2>&1 start >> $logfile 2>&1 date "+.%T" >> $logfile ;; reload) date "+RELOAD.%T" >> $logfile echo "...." >> $logfile 2>&1 date "+reload.%T" >> $logfile ;; esac echo "done$1" >&2 exit 0 """.format(**locals())) text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] User=somebody Type=notify # PIDFile={root}/var/run/zzz.init.pid ExecStart={root}/usr/bin/zzz.init start ExecStop={root}/usr/bin/zzz.init stop [Install] WantedBy=multi-user.target """.format(**locals())) cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/killall || {package} install -y psmisc'" sx____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/socat || {package} install -y socat'" if sx____(cmd.format(**locals())): self.skipTest("unable to install socat in a container from "+image) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker exec {testname} mkdir -p /etc/systemd/system /etc/systemd/user" sx____(cmd.format(**locals())) zzz_service = "/etc/systemd/{system}/zzz.service".format(**locals()) cmd = "docker cp /usr/bin/sleep {testname}:{bindir}/{testsleep}" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.init {testname}:/usr/bin/zzz.init" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.service {testname}:{zzz_service}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chmod 666 {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'grep nobody /etc/group || groupadd nobody'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} useradd somebody -g nobody -m" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} {systemctl} enable zzz.service -vv" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'start' shall start a service that is NOT is-active ") sh____("docker exec {testname} ls -l /var/run".format(**locals())) cmd = "docker exec {testname} {systemctl} start zzz.service -vv -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") # logg.info("== 'stop' shall stop a service that is-active") cmd = "docker exec {testname} {systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'restart' shall start a service that NOT is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "docker exec {testname} cat {logfile}" sh____(cmd.format(**locals())) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) self.assertEqual(end, 0) # cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top1= top # logg.info("== 'restart' shall restart a service that is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "docker exec {testname} cat {logfile}" sh____(cmd.format(**locals())) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) self.assertEqual(end, 0) # cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top2 = top # logg.info("-- and we check that there is a new PID for the service process") def find_pids(ps_output, command): pids = [] for line in _lines(ps_output): if command not in line: continue m = re.match(r"\s*[\d:]*\s+(\S+)\s+(\S+)\s+(.*)", line) pid, ppid, args = m.groups() # logg.info(" %s | %s | %s", pid, ppid, args) pids.append(pid) return pids ps1 = find_pids(running(top1), testsleep) ps2 = find_pids(running(top2), testsleep) logg.info("found PIDs %s and %s", ps1, ps2) self.assertTrue(len(ps1), 1) self.assertTrue(len(ps2), 1) self.assertNotEqual(ps1[0], ps2[0]) # logg.info("== 'reload' will NOT restart a service that is-active") cmd = "docker exec {testname} {systemctl} reload zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top3 = top # logg.info("-- and we check that there is NO new PID for the service process") ps3 = find_pids(running(top3), testsleep) logg.info("found PIDs %s and %s", ps2, ps3) self.assertTrue(len(ps2), 1) self.assertTrue(len(ps3), 1) self.assertEqual(ps2[0], ps3[0]) # logg.info("== 'reload-or-restart' will restart a service that is-active (if no ExecReload)") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top4 = top # logg.info("-- and we check that there is a new PID for the service process (if no ExecReload)") ps4 = find_pids(running(top4), testsleep) logg.info("found PIDs %s and %s", ps3, ps4) self.assertTrue(len(ps3), 1) self.assertTrue(len(ps4), 1) self.assertNotEqual(ps3[0], ps4[0]) # logg.info("== 'kill' will bring is-active non-active as well (when the PID is known)") cmd = "docker exec {testname} {systemctl} kill zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'stop' will turn 'failed' to 'inactive' (when the PID is known)") cmd = "docker exec {testname} {systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'reload-or-try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-try-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'reload-or-restart' will start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top5 = top # logg.info("== 'reload-or-try-restart' will restart an is-active service (with no ExecReload)") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top6 = top # logg.info("-- and we check that there is a new PID for the service process (if no ExecReload)") ps5 = find_pids(running(top5), testsleep) ps6 = find_pids(running(top6), testsleep) logg.info("found PIDs %s and %s", ps5, ps6) self.assertTrue(len(ps5), 1) self.assertTrue(len(ps6), 1) self.assertNotEqual(ps5[0], ps6[0]) # logg.info("== 'try-restart' will restart an is-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top7 = top # logg.info("-- and we check that there is a new PID for the service process") ps7 = find_pids(running(top7), testsleep) logg.info("found PIDs %s and %s", ps6, ps7) self.assertTrue(len(ps6), 1) self.assertTrue(len(ps7), 1) self.assertNotEqual(ps6[0], ps7[0]) # logg.info("LOG\n%s", " "+output("docker exec {testname} cat {logfile}".format(**locals())).replace("\n","\n ")) # self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) def test_5036_runuser_notify_service_functions_with_reload(self): """ check that we manage notify services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart. (with ExecReload)""" self.begin() testname = self.testname() testdir = self.testdir() self.runuser_notify_service_functions_with_reload("system", testname, testdir) self.rm_testdir() logg.error("too long") #TODO self.end(200) def test_5037_runuser_notify_service_functions_with_reload_user(self): """ check that we manage notify services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart. (with ExecReload)""" # test_5037 is triggering len(socketfile) > 100 | "new notify socketfile" self.begin() testname = self.testname() testdir = self.testdir() self.runuser_notify_service_functions_with_reload("user", testname, testdir) self.rm_testdir() self.end(266) #TODO# too long? def runuser_notify_service_functions_with_reload(self, system, testname, testdir): if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) python = os.path.basename(_python) python_coverage = coverage_package(image) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) sometime = SOMETIME or 288 quick = "--coverage=quick" # user = self.user() root = "" systemctl_py = realpath(_systemctl_py) systemctl = "/usr/bin/systemctl" # path in container systemctl += " --{system}".format(**locals()) testsleep = self.testname("sleep") logfile = os_path(root, "/var/log/"+testsleep+".log") bindir = os_path(root, "/usr/bin") begin = "{" ; end = "}" shell_file(os_path(testdir, "zzz.init"), """ #! /bin/bash logfile={logfile} start() {begin} ls -l $NOTIFY_SOCKET {bindir}/{testsleep} 111 0<&- &>/dev/null & echo "MAINPID=$!" | socat -v -d - UNIX-CLIENT:$NOTIFY_SOCKET echo "READY=1" | socat -v -d - UNIX-CLIENT:$NOTIFY_SOCKET wait %1 # ps -o pid,ppid,user,args {end} stop() {begin} killall {testsleep} {end} case "$1" in start) date "+START.%T" >> $logfile start >> $logfile 2>&1 date "+start.%T" >> $logfile ;; stop) date "+STOP.%T" >> $logfile stop >> $logfile 2>&1 date "+stop.%T" >> $logfile ;; restart) date "+RESTART.%T" >> $logfile stop >> $logfile 2>&1 start >> $logfile 2>&1 date "+.%T" >> $logfile ;; reload) date "+RELOAD.%T" >> $logfile echo "...." >> $logfile 2>&1 date "+reload.%T" >> $logfile ;; esac echo "done$1" >&2 exit 0 """.format(**locals())) text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] User=somebody Type=notify # PIDFile={root}/var/run/zzz.init.pid ExecStart={root}/usr/bin/zzz.init start ExecReload={root}/usr/bin/zzz.init reload ExecStop={root}/usr/bin/zzz.init stop TimeoutRestartSec=4 TimeoutReloadSec=4 [Install] WantedBy=multi-user.target """.format(**locals())) cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/killall || {package} install -y psmisc'" sx____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/socat || {package} install -y socat'" if sx____(cmd.format(**locals())): self.skipTest("unable to install socat in a container from "+image) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker exec {testname} mkdir -p /etc/systemd/system /etc/systemd/user" sx____(cmd.format(**locals())) zzz_service = "/etc/systemd/{system}/zzz.service".format(**locals()) cmd = "docker cp /usr/bin/sleep {testname}:{bindir}/{testsleep}" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.init {testname}:/usr/bin/zzz.init" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.service {testname}:{zzz_service}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chmod 666 {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'grep nobody /etc/group || groupadd nobody'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} useradd somebody -g nobody -m" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} {systemctl} enable zzz.service -vv" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'start' shall start a service that is NOT is-active ") cmd = "docker exec {testname} {systemctl} start zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") # logg.info("== 'stop' shall stop a service that is-active") cmd = "docker exec {testname} {systemctl} stop zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'restart' shall start a service that NOT is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top1= top # logg.info("== 'restart' shall restart a service that is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top2 = top # logg.info("-- and we check that there is a new PID for the service process") def find_pids(ps_output, command): pids = [] for line in _lines(ps_output): if command not in line: continue m = re.match(r"\s*[\d:]*\s+(\S+)\s+(\S+)\s+(.*)", line) pid, ppid, args = m.groups() # logg.info(" %s | %s | %s", pid, ppid, args) pids.append(pid) return pids ps1 = find_pids(running(top1), testsleep) ps2 = find_pids(running(top2), testsleep) logg.info("found PIDs %s and %s", ps1, ps2) self.assertTrue(len(ps1), 1) self.assertTrue(len(ps2), 1) self.assertNotEqual(ps1[0], ps2[0]) # logg.info("== 'reload' will NOT restart a service that is-active") cmd = "docker exec {testname} {systemctl} reload zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top3 = top # logg.info("-- and we check that there is NO new PID for the service process") ps3 = find_pids(running(top3), testsleep) logg.info("found PIDs %s and %s", ps2, ps3) self.assertTrue(len(ps2), 1) self.assertTrue(len(ps3), 1) self.assertEqual(ps2[0], ps3[0]) # logg.info("== 'reload-or-restart' will restart a service that is-active (if no ExecReload)") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top4 = top # logg.info("-- and we check that there is the same PID for the service process (if ExecReload)") ps4 = find_pids(running(top4), testsleep) logg.info("found PIDs %s and %s", ps3, ps4) self.assertTrue(len(ps3), 1) self.assertTrue(len(ps4), 1) self.assertEqual(ps3[0], ps4[0]) # logg.info("== 'kill' will bring is-active non-active as well (when the PID is known)") cmd = "docker exec {testname} {systemctl} kill zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'stop' will turn 'failed' to 'inactive' (when the PID is known)") #TODO# cmd = "docker exec {testname} {systemctl} stop zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'reload-or-try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-try-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'reload-or-restart' will start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top5 = top # logg.info("== 'reload-or-try-restart' will restart an is-active service (with no ExecReload)") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top6 = top # logg.info("-- and we check that there is a new PID for the service process (if no ExecReload)") ps5 = find_pids(running(top5), testsleep) ps6 = find_pids(running(top6), testsleep) logg.info("found PIDs %s and %s", ps5, ps6) self.assertTrue(len(ps5), 1) self.assertTrue(len(ps6), 1) self.assertNotEqual(ps5[0], ps6[0]) # logg.info("== 'try-restart' will restart an is-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top7 = top # logg.info("-- and we check that there is a new PID for the service process") ps7 = find_pids(running(top7), testsleep) logg.info("found PIDs %s and %s", ps6, ps7) self.assertTrue(len(ps6), 1) self.assertTrue(len(ps7), 1) self.assertNotEqual(ps6[0], ps7[0]) # logg.info("LOG\n%s", " "+output("docker exec {testname} cat {logfile}".format(**locals())).replace("\n","\n ")) # self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) def test_5040_runuser_oneshot_service_functions(self): """ check that we manage oneshot services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart.""" self.begin() testname = self.testname() testdir = self.testdir() self.runuser_oneshot_service_functions("system", testname, testdir) self.rm_testdir() self.end() def test_5041_runuser_oneshot_service_functions_user(self): """ check that we manage oneshot services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart.""" self.begin() testname = self.testname() testdir = self.testdir() self.runuser_oneshot_service_functions("user", testname, testdir) self.rm_testdir() self.end() def runuser_oneshot_service_functions(self, system, testname, testdir): if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) python = os.path.basename(_python) python_coverage = coverage_package(image) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) sometime = SOMETIME or 188 quick = "--coverage=quick" # user = self.user() root = "" systemctl_py = realpath(_systemctl_py) systemctl = "/usr/bin/systemctl" # path in container systemctl += " --{system}".format(**locals()) testsleep = self.testname("sleep") logfile = os_path(root, "/var/log/"+testsleep+".log") bindir = os_path(root, "/usr/bin") begin = "{" ; end = "}" text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] User=somebody Type=oneshot ExecStartPre={bindir}/backup {root}/var/tmp/test.1 {root}/var/tmp/test.2 ExecStart=/usr/bin/touch {root}/var/tmp/test.1 ExecStop=/bin/rm {root}/var/tmp/test.1 ExecStopPost=/bin/rm -f {root}/var/tmp/test.2 [Install] WantedBy=multi-user.target """.format(**locals())) shell_file(os_path(testdir, "backup"), """ #! /bin/sh set -x test ! -f "$1" || mv -v "$1" "$2" """) cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/killall || {package} install -y psmisc'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker exec {testname} mkdir -p /etc/systemd/system /etc/systemd/user" sx____(cmd.format(**locals())) zzz_service = "/etc/systemd/{system}/zzz.service".format(**locals()) cmd = "docker cp /usr/bin/sleep {testname}:{bindir}/{testsleep}" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.service {testname}:{zzz_service}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chmod 666 {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'grep nobody /etc/group || groupadd nobody'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} useradd somebody -g nobody -m" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/backup {testname}:/usr/bin/backup" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch /var/tmp/test.0" sh____(cmd.format(**locals())) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertFalse(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # cmd = "docker exec {testname} {systemctl} enable zzz.service -vv" sh____(cmd.format(**locals())) # is_active = "docker exec {testname} {systemctl} is-active zzz.service -vv" act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive") self.assertEqual(end, 3) # logg.info("== 'start' shall start a service that is NOT is-active ") cmd = "docker exec {testname} {systemctl} start zzz.service -vvvv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active") self.assertEqual(end, 0) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertTrue(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'stop' shall stop a service that is-active") cmd = "docker exec {testname} {systemctl} stop zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertFalse(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'restart' shall start a service that NOT is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active") self.assertEqual(end, 0) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertTrue(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'restart' shall restart a service that is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active") self.assertEqual(end, 0) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertTrue(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'reload' will NOT restart a service that is-active") cmd = "docker exec {testname} {systemctl} reload zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active") self.assertEqual(end, 0) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertTrue(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'reload-or-restart' will restart a service that is-active") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active") self.assertEqual(end, 0) # logg.info("== 'stop' will brings it back to 'inactive'") cmd = "docker exec {testname} {systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertFalse(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'reload-or-try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-try-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertFalse(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertFalse(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'reload-or-restart' will start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active") self.assertEqual(end, 0) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertTrue(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'reload-or-try-restart' will restart an is-active service") cmd = "docker exec {testname} {systemctl} reload-or-try-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active") self.assertEqual(end, 0) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertTrue(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'try-restart' will restart an is-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active") self.assertEqual(end, 0) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertTrue(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'stop' will brings it back to 'inactive'") cmd = "docker exec {testname} {systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertFalse(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("LOG\n%s", " "+output("docker exec {testname} cat {logfile}".format(**locals())).replace("\n","\n ")) # self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) def test_5042_runuser_oneshot_and_unknown_service_functions(self): """ check that we manage multiple services even when some services are not actually known. Along with oneshot serivce with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart / we have only different exit-code.""" self.begin() testname = self.testname() testdir = self.testdir() if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) python = os.path.basename(_python) python_coverage = coverage_package(image) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) sometime = SOMETIME or 188 quick = "--coverage=quick" # user = self.user() root = "" systemctl_py = realpath(_systemctl_py) systemctl = "/usr/bin/systemctl" # path in container testsleep = self.testname("sleep") logfile = os_path(root, "/var/log/"+testsleep+".log") bindir = os_path(root, "/usr/bin") begin = "{" ; end = "}" text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] User=somebody Type=oneshot ExecStartPre={bindir}/backup {root}/var/tmp/test.1 {root}/var/tmp/test.2 ExecStart=/usr/bin/touch {root}/var/tmp/test.1 ExecStop=/bin/rm {root}/var/tmp/test.1 ExecStopPost=/bin/rm -f {root}/var/tmp/test.2 [Install] WantedBy=multi-user.target """.format(**locals())) shell_file(os_path(testdir, "backup"), """ #! /bin/sh set -x test ! -f "$1" || mv -v "$1" "$2" """) cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/killall || {package} install -y psmisc'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker exec {testname} mkdir -p /etc/systemd/system /etc/systemd/user" sx____(cmd.format(**locals())) cmd = "docker cp /usr/bin/sleep {testname}:{bindir}/{testsleep}" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.service {testname}:/etc/systemd/system/zzz.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/backup {testname}:/usr/bin/backup" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch /var/tmp/test.0" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chmod 666 {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'grep nobody /etc/group || groupadd nobody'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} useradd somebody -g nobody -m" sh____(cmd.format(**locals())) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertFalse(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # cmd = "docker exec {testname} {systemctl} enable zzz.service -vv" sh____(cmd.format(**locals())) is_active = "docker exec {testname} {systemctl} is-active zzz.service other.service -vv" act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive\nunknown") self.assertEqual(end, 3) # logg.info("== 'start' shall start a service that is NOT is-active ") cmd = "docker exec {testname} {systemctl} start zzz.service other.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) is_active = "docker exec {testname} {systemctl} is-active zzz.service other.service -vv" act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active\nunknown") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertTrue(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'stop' shall stop a service that is-active") cmd = "docker exec {testname} {systemctl} stop zzz.service other.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive\nunknown") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertFalse(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'restart' shall start a service that NOT is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service other.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active\nunknown") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertTrue(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'restart' shall restart a service that is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service other.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active\nunknown") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertTrue(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'reload' will NOT restart a service that is-active") cmd = "docker exec {testname} {systemctl} reload zzz.service other.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active\nunknown") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertTrue(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'reload-or-restart' will restart a service that is-active") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service other.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active\nunknown") self.assertEqual(end, 3) # logg.info("== 'stop' will brings it back to 'inactive'") cmd = "docker exec {testname} {systemctl} stop zzz.service other.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive\nunknown") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertFalse(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'reload-or-try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-try-restart zzz.service other.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive\nunknown") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertFalse(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service other.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive\nunknown") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertFalse(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'reload-or-restart' will start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service other.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active\nunknown") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertTrue(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'reload-or-try-restart' will restart an is-active service") cmd = "docker exec {testname} {systemctl} reload-or-try-restart zzz.service other.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active\nunknown") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertTrue(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'try-restart' will restart an is-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service other.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active\nunknown") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertTrue(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'stop' will brings it back to 'inactive'") cmd = "docker exec {testname} {systemctl} stop zzz.service other.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive\nunknown") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertFalse(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("LOG\n%s", " "+output("docker exec {testname} cat {logfile}".format(**locals())).replace("\n","\n ")) # self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) self.rm_testdir() self.end() def test_5044_runuser_sysv_service_functions(self): """ check that we manage SysV services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart.""" self.begin() testname = self.testname() testdir = self.testdir() if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) python = os.path.basename(_python) python_coverage = coverage_package(image) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) sometime = SOMETIME or 188 quick = "--coverage=quick" # user = self.user() root = "" systemctl_py = realpath(_systemctl_py) systemctl = "/usr/bin/systemctl" # path in container testsleep = self.testname("sleep") logfile = os_path(root, "/var/log/"+testsleep+".log") bindir = os_path(root, "/usr/bin") begin = "{" ; end = "}" shell_file(os_path(testdir, "zzz.init"), """ #! /bin/bash ### BEGIN INIT INFO # Required-Start: $local_fs $remote_fs $syslog $network # Required-Stop: $local_fs $remote_fs $syslog $network # Default-Start: 3 5 # Default-Stop: 0 1 2 6 # Short-Description: Testing Z # Description: Allows for SysV testing ### END INIT INFO logfile={logfile} sleeptime=111 start() {begin} [ -d /var/run ] || mkdir -p /var/run (runuser -u somebody {bindir}/{testsleep} $sleeptime 0<&- &>/dev/null & echo $! > {root}/var/run/zzz.init.pid ) & wait %1 # ps -o pid,ppid,user,args cat "RUNNING `cat {root}/var/run/zzz.init.pid`" {end} stop() {begin} kill `cat {root}/var/run/zzz.init.pid` >>$logfile 2>&1 killall {testsleep} >> $logfile 2>&1 {end} case "$1" in start) date "+START.%T" >> $logfile start >> $logfile 2>&1 date "+start.%T" >> $logfile ;; stop) date "+STOP.%T" >> $logfile stop >> $logfile 2>&1 date "+stop.%T" >> $logfile ;; restart) date "+RESTART.%T" >> $logfile stop >> $logfile 2>&1 start >> $logfile 2>&1 date "+.%T" >> $logfile ;; reload) date "+RELOAD.%T" >> $logfile echo "...." >> $logfile 2>&1 date "+reload.%T" >> $logfile ;; esac echo "done$1" >&2 exit 0 """.format(**locals())) cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/killall || {package} install -y psmisc'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker cp /usr/bin/sleep {testname}:{bindir}/{testsleep}" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.init {testname}:/etc/init.d/zzz" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chmod 666 {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'grep nobody /etc/group || groupadd nobody'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} useradd somebody -g nobody -m" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} {systemctl} enable zzz.service -vv" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'start' shall start a service that is NOT is-active ") cmd = "docker exec {testname} {systemctl} start zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") # logg.info("== 'stop' shall stop a service that is-active") cmd = "docker exec {testname} {systemctl} stop zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) time.sleep(1) # killall is async sx____("docker exec {testname} bash -c 'sed s/^/.../ {logfile} | tail'".format(**locals())) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'restart' shall start a service that NOT is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top1= top # logg.info("== 'restart' shall restart a service that is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top2 = top # logg.info("-- and we check that there is a new PID for the service process") def find_pids(ps_output, command): pids = [] for line in _lines(ps_output): if command not in line: continue m = re.match(r"\s*[\d:]*\s+(\S+)\s+(\S+)\s+(.*)", line) pid, ppid, args = m.groups() # logg.info(" %s | %s | %s", pid, ppid, args) pids.append(pid) return pids ps1 = find_pids(running(top1), testsleep) ps2 = find_pids(running(top2), testsleep) logg.info("found PIDs %s and %s", ps1, ps2) self.assertTrue(len(ps1), 1) self.assertTrue(len(ps2), 1) self.assertNotEqual(ps1[0], ps2[0]) # logg.info("== 'reload' will NOT restart a service that is-active") cmd = "docker exec {testname} {systemctl} reload zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top3 = top # logg.info("-- and we check that there is NO new PID for the service process") ps3 = find_pids(running(top3), testsleep) logg.info("found PIDs %s and %s", ps2, ps3) self.assertTrue(len(ps2), 1) self.assertTrue(len(ps3), 1) self.assertEqual(ps2[0], ps3[0]) # logg.info("== 'reload-or-restart' may restart a service that is-active") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") # logg.info("== 'stop' will turn 'failed' to 'inactive' (when the PID is known)") cmd = "docker exec {testname} {systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'reload-or-try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'reload-or-restart' will start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top5 = top # logg.info("== 'reload-or-try-restart' will restart an is-active service (with no ExecReload)") cmd = "docker exec {testname} {systemctl} reload-or-try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top6 = top # logg.info("== 'try-restart' will restart an is-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top7 = top # logg.info("-- and we check that there is a new PID for the service process") ps6 = find_pids(running(top6), testsleep) ps7 = find_pids(running(top7), testsleep) logg.info("found PIDs %s and %s", ps6, ps7) self.assertTrue(len(ps6), 1) self.assertTrue(len(ps7), 1) self.assertNotEqual(ps6[0], ps7[0]) # logg.info("LOG\n%s", " "+output("docker exec {testname} cat {logfile}".format(**locals())).replace("\n","\n ")) # self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) self.rm_testdir() self.end() # # # compare the following with the test_5030 series # as they are doing the same with usermode-only containers # # def test_5100_usermode_keeps_running(self): """ check that we manage simple services in a root env with commands like start, restart, stop, etc""" self.begin() testname = self.testname() testdir = self.testdir() self.usermode_keeps_running("system", testname, testdir) self.rm_testdir() self.end() def test_5101_usermode_keeps_running_user(self): """ check that we manage simple services in a root env with commands like start, restart, stop, etc""" self.begin() testname = self.testname() testdir = self.testdir() self.usermode_keeps_running("user", testname, testdir) self.rm_testdir() self.end() def usermode_keeps_running(self, system, testname, testdir): """ check that we manage simple services in a root env where the usermode container keeps running on PID 1 """ if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) python = os.path.basename(_python) python_coverage = coverage_package(image) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) sometime = SOMETIME or 288 quick = "--coverage=quick" # user = self.user() root = "" systemctl_py = realpath(_systemctl_py) systemctl = "/usr/bin/systemctl" # path in container systemctl += " --user" # systemctl += " --{system}".format(**locals()) testsleep = testname+"_testsleep" logfile = os_path(root, "/var/log/test.log") bindir = os_path(root, "/usr/bin") begin = "{" end = "}" text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] User=somebody Type=simple ExecStartPre=/bin/echo %n ExecStart=/usr/bin/{testsleep} 8 ExecStartPost=/bin/echo started $MAINPID # ExecStop=/bin/kill $MAINPID ExecStopPost=/bin/echo stopped $MAINPID ExecStopPost=/bin/sleep 2 ExecReload=/bin/kill -10 $MAINPID KillSignal=SIGQUIT [Install] WantedBy=multi-user.target """.format(**locals())) cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/killall || {package} install -y psmisc'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker exec {testname} mkdir -p /etc/systemd/system /etc/systemd/user" sx____(cmd.format(**locals())) cmd = "docker cp /usr/bin/sleep {testname}:{bindir}/{testsleep}" sh____(cmd.format(**locals())) zzz_service = "/etc/systemd/{system}/zzz.service".format(**locals()) cmd = "docker cp {testdir}/zzz.service {testname}:{zzz_service}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chmod 666 {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'grep nobody /etc/group || groupadd nobody'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} useradd somebody -g nobody -m" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} touch /var/log/systemctl.debug.log" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chmod 666 /var/log/systemctl.debug.log" sh____(cmd.format(**locals())) cmd = "docker exec {testname} mkdir -p touch /tmp/run-somebody/log" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch /tmp/run-somebody/log/systemctl.debug.log" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chown somebody -R /tmp/run-somebody" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chown somebody /tmp/.coverage" sx____(cmd.format(**locals())) # cmd = "docker commit -c 'CMD [\"/usr/bin/systemctl\"]' -c 'USER somebody' {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker rm -f {testname}" sh____(cmd.format(**locals())) cmd = "docker run -d --name {testname} {images}:{testname}" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} {systemctl} enable zzz.service -vv" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {systemctl} is-system-running -vv" sx____(cmd.format(**locals())) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'start' shall start a service that is NOT is-active ") cmd = "docker exec {testname} {systemctl} start zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) self.assertEqual(end, 0) # for attempt in xrange(4): # 4*3 = 12s time.sleep(3) logg.info("=====================================================================") top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) cmd = "docker cp {testname}:/var/log/systemctl.debug.log {testdir}/gobal.systemctl.debug.log" sx____(cmd.format(**locals())) cmd = "tail {testdir}/gobal.systemctl.debug.log | sed -e s/^/GLOBAL:.../" sx____(cmd.format(**locals())) cmd = "docker cp {testname}:/tmp/run-somebody/log/systemctl.debug.log {testdir}/somebody.systemctl.debug.log" sx____(cmd.format(**locals())) cmd = "tail {testdir}/somebody.systemctl.debug.log | sed -e s/^/USER:.../" sx____(cmd.format(**locals())) # # out, end = output2(cmd.format(**locals())) if greps(err, "Error response from daemon"): break # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) # self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) if True: cmd = "cat {testdir}/gobal.systemctl.debug.log | sed -e s/^/GLOBAL:.../" sx____(cmd.format(**locals())) cmd = "cat {testdir}/somebody.systemctl.debug.log | sed -e s/^/USER:.../" sx____(cmd.format(**locals())) # self.assertFalse(greps(err, "Error response from daemon")) self.assertEqual(out.strip(), "failed") # sleep did exit but not 'stop' requested def test_5130_usermode_simple_service_functions_system(self): """ check that we manage simple services in a root env with commands like start, restart, stop, etc""" self.begin() testname = self.testname() testdir = self.testdir() self.usermode_simple_service_functions("system", testname, testdir) self.rm_testdir() self.end() def test_5131_simple_service_functions_user(self): """ check that we manage simple services in a root env with commands like start, restart, stop, etc""" self.begin() testname = self.testname() testdir = self.testdir() self.usermode_simple_service_functions("user", testname, testdir) self.rm_testdir() self.end() def usermode_simple_service_functions(self, system, testname, testdir): """ check that we manage simple services in a root env with commands like start, restart, stop, etc""" if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) python = os.path.basename(_python) python_coverage = coverage_package(image) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) sometime = SOMETIME or 288 quick = "--coverage=quick" # user = self.user() root = "" systemctl_py = realpath(_systemctl_py) systemctl = "/usr/bin/systemctl" # path in container systemctl += " --user" # systemctl += " --{system}".format(**locals()) testsleep = testname+"_testsleep" testscript = testname+"_testscript.sh" logfile = os_path(root, "/var/log/test.log") bindir = os_path(root, "/usr/bin") begin = "{" end = "}" text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] User=somebody Type=simple ExecStartPre=/bin/echo %n ExecStart={bindir}/{testscript} 111 ExecStartPost=/bin/echo started $MAINPID ExecStop=/bin/kill -3 $MAINPID ExecStopPost=/bin/echo stopped $MAINPID ExecStopPost=/bin/sleep 2 ExecReload=/bin/kill -10 $MAINPID KillSignal=SIGQUIT [Install] WantedBy=multi-user.target """.format(**locals())) shell_file(os_path(testdir, testscript),""" #! /bin/sh date +%T,enter >> {logfile} stops () {begin} date +%T,stopping >> {logfile} killall {testsleep} >> {logfile} 2>&1 date +%T,stopped >> {logfile} {end} reload () {begin} date +%T,reloading >> {logfile} date +%T,reloaded >> {logfile} {end} trap "stops" 3 # SIGQUIT trap "reload" 10 # SIGUSR1 date +%T,starting >> {logfile} {bindir}/{testsleep} $1 >> {logfile} 2>&1 & pid="$!" while kill -0 $pid; do # use 'kill -0' to check the existance of the child date +%T,waiting >> {logfile} # use 'wait' for children AND external signals wait done date +%T,leaving >> {logfile} trap - 3 10 # SIGQUIT SIGUSR1 date +%T,leave >> {logfile} """.format(**locals())) cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/killall || {package} install -y psmisc'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker cp /usr/bin/sleep {testname}:{bindir}/{testsleep}" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/{testscript} {testname}:{bindir}/{testscript}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} mkdir -p /etc/systemd/system /etc/systemd/user" sx____(cmd.format(**locals())) zzz_service = "/etc/systemd/{system}/zzz.service".format(**locals()) cmd = "docker cp {testdir}/zzz.service {testname}:{zzz_service}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chmod 666 {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'grep nobody /etc/group || groupadd nobody'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} useradd somebody -g nobody -m" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chown somebody /tmp/.coverage" sx____(cmd.format(**locals())) # cmd = "docker commit -c 'CMD [\"/usr/bin/systemctl\"]' -c 'USER somebody' {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker rm -f {testname}" sh____(cmd.format(**locals())) cmd = "docker run -d --name {testname} {images}:{testname}" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} {systemctl} enable zzz.service -vv" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'start' shall start a service that is NOT is-active ") cmd = "docker exec {testname} {systemctl} start zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output(_top_list)) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) self.assertEqual(end, 0) # time.sleep(3) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") # logg.info("== 'stop' shall stop a service that is-active") cmd = "docker exec {testname} {systemctl} stop zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) time.sleep(1) # kill is async cmd = "docker exec {testname} cat {logfile}" sh____(cmd.format(**locals())) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # # inspect the service's log log = lines(output("docker exec {testname} cat {logfile}".format(**locals()))) logg.info("LOG\n %s", "\n ".join(log)) self.assertTrue(greps(log, "enter")) self.assertTrue(greps(log, "leave")) self.assertTrue(greps(log, "starting")) self.assertTrue(greps(log, "stopped")) self.assertFalse(greps(log, "reload")) sh____("docker exec {testname} truncate -s0 {logfile}".format(**locals())) # logg.info("== 'restart' shall start a service that NOT is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service -vvvv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top1= top # # inspect the service's log log = lines(output("docker exec {testname} cat {logfile}".format(**locals()))) logg.info("LOG\n %s", "\n ".join(log)) self.assertTrue(greps(log, "enter")) self.assertFalse(greps(log, "leave")) self.assertTrue(greps(log, "starting")) self.assertFalse(greps(log, "stopped")) self.assertFalse(greps(log, "reload")) sh____("docker exec {testname} truncate -s0 {logfile}".format(**locals())) # logg.info("== 'restart' shall restart a service that is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top2 = top # logg.info("-- and we check that there is a new PID for the service process") def find_pids(ps_output, command): pids = [] for line in _lines(ps_output): if command not in line: continue m = re.match(r"\s*[\d:]*\s+(\S+)\s+(\S+)\s+(.*)", line) pid, ppid, args = m.groups() # logg.info(" %s | %s | %s", pid, ppid, args) pids.append(pid) return pids ps1 = find_pids(top1, testsleep) ps2 = find_pids(top2, testsleep) logg.info("found PIDs %s and %s", ps1, ps2) self.assertTrue(len(ps1), 1) self.assertTrue(len(ps2), 1) self.assertNotEqual(ps1[0], ps2[0]) # # inspect the service's log log = lines(output("docker exec {testname} cat {logfile}".format(**locals()))) logg.info("LOG\n %s", "\n ".join(log)) self.assertTrue(greps(log, "enter")) self.assertTrue(greps(log, "starting")) self.assertFalse(greps(log, "reload")) sh____("docker exec {testname} truncate -s0 {logfile}".format(**locals())) # # logg.info("== 'reload' will NOT restart a service that is-active") cmd = "docker exec {testname} {systemctl} reload zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top3 = top # logg.info("-- and we check that there is NO new PID for the service process") ps3 = find_pids(top3, testsleep) logg.info("found PIDs %s and %s", ps2, ps3) self.assertTrue(len(ps2), 1) self.assertTrue(len(ps3), 1) self.assertEqual(ps2[0], ps3[0]) # # inspect the service's log log = lines(output("docker exec {testname} cat {logfile}".format(**locals()))) logg.info("LOG\n %s", "\n ".join(log)) self.assertFalse(greps(log, "enter")) self.assertFalse(greps(log, "leave")) self.assertFalse(greps(log, "starting")) self.assertFalse(greps(log, "stopped")) self.assertTrue(greps(log, "reload")) sh____("docker exec {testname} truncate -s0 {logfile}".format(**locals())) # logg.info("== 'reload-or-restart' will restart a service that is-active (if ExecReload)") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top4 = top # logg.info("-- and we check that there is NO new PID for the service process (if ExecReload)") ps4 = find_pids(top4, testsleep) logg.info("found PIDs %s and %s", ps3, ps4) self.assertTrue(len(ps3), 1) self.assertTrue(len(ps4), 1) self.assertEqual(ps3[0], ps4[0]) # logg.info("== 'kill' will bring is-active non-active as well (when the PID is known)") cmd = "docker exec {testname} {systemctl} kill zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'stop' will turn 'failed' to 'inactive' (when the PID is known)") cmd = "docker exec {testname} {systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) # no PID known so 'kill $MAINPID' fails cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'reload-or-try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'reload-or-restart' will start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top5 = top # logg.info("== 'reload-or-try-restart' will NOT restart an is-active service (with ExecReload)") cmd = "docker exec {testname} {systemctl} reload-or-try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top6 = top # logg.info("-- and we check that there is NO new PID for the service process (if ExecReload)") ps5 = find_pids(top5, testsleep) ps6 = find_pids(top6, testsleep) logg.info("found PIDs %s and %s", ps5, ps6) self.assertTrue(len(ps5), 1) self.assertTrue(len(ps6), 1) self.assertEqual(ps5[0], ps6[0]) # logg.info("== 'try-restart' will restart an is-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top7 = top # logg.info("-- and we check that there is a new PID for the service process") ps7 = find_pids(top7, testsleep) logg.info("found PIDs %s and %s", ps6, ps7) self.assertTrue(len(ps6), 1) self.assertTrue(len(ps7), 1) self.assertNotEqual(ps6[0], ps7[0]) # kill_testsleep = "killall {testsleep}" sx____(kill_testsleep.format(**locals())) # self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) def test_5132_usermode_forking_service_functions_system(self): """ check that we manage forking services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart.""" self.begin() testname = self.testname() testdir = self.testdir() self.usermode_forking_service_functions("system", testname, testdir) self.rm_testdir() self.end() def test_5133_usermode_forking_service_functions_user(self): """ check that we manage forking services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart.""" self.begin() testname = self.testname() testdir = self.testdir() self.forking_service_functions("user", testname, testdir) self.rm_testdir() self.end() def usermode_forking_service_functions(self, system, testname, testdir): if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) python = os.path.basename(_python) python_coverage = coverage_package(image) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) sometime = SOMETIME or 188 quick = "--coverage=quick" # user = self.user() root = "" systemctl_py = realpath(_systemctl_py) systemctl = "/usr/bin/systemctl" # path in container systemctl += " --user" # systemctl += " --{system}".format(**locals()) testsleep = testname+"_sleep" logfile = os_path(root, "/var/log/"+testsleep+".log") bindir = os_path(root, "/usr/bin") begin = "{" ; end = "}" shell_file(os_path(testdir, "zzz.init"), """ #! /bin/bash logfile={logfile} start() {begin} [ -d /var/run ] || mkdir -p /var/run ({bindir}/{testsleep} 111 0<&- &>/dev/null & echo $! > /tmp/zzz.init.pid ) & wait %1 # ps -o pid,ppid,user,args {end} stop() {begin} killall {testsleep} {end} case "$1" in start) date "+START.%T" >> $logfile start >> $logfile 2>&1 date "+start.%T" >> $logfile ;; stop) date "+STOP.%T" >> $logfile stop >> $logfile 2>&1 date "+stop.%T" >> $logfile ;; restart) date "+RESTART.%T" >> $logfile stop >> $logfile 2>&1 start >> $logfile 2>&1 date "+.%T" >> $logfile ;; reload) date "+RELOAD.%T" >> $logfile echo "...." >> $logfile 2>&1 date "+reload.%T" >> $logfile ;; esac echo "done$1" >&2 exit 0 """.format(**locals())) text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] User=somebody Type=forking PIDFile=/tmp/zzz.init.pid ExecStart=/usr/bin/zzz.init start ExecStop=/usr/bin/zzz.init stop [Install] WantedBy=multi-user.target """.format(**locals())) cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/killall || {package} install -y psmisc'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker exec {testname} mkdir -p /etc/systemd/system /etc/systemd/user" sx____(cmd.format(**locals())) zzz_service = "/etc/systemd/{system}/zzz.service".format(**locals()) cmd = "docker cp /usr/bin/sleep {testname}:{bindir}/{testsleep}" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.init {testname}:/usr/bin/zzz.init" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.service {testname}:{zzz_service}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chmod 666 {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'grep nobody /etc/group || groupadd nobody'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} useradd somebody -g nobody -m" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chown somebody /tmp/.coverage" sx____(cmd.format(**locals())) # cmd = "docker commit -c 'CMD [\"/usr/bin/systemctl\"]' -c 'USER somebody' {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker rm -f {testname}" sh____(cmd.format(**locals())) cmd = "docker run -d --name {testname} {images}:{testname}" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} {systemctl} enable zzz.service -vv" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'start' shall start a service that is NOT is-active ") cmd = "docker exec {testname} {systemctl} start zzz.service -vvvv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") # logg.info("== 'stop' shall stop a service that is-active") cmd = "docker exec {testname} {systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'restart' shall start a service that NOT is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top1= top # logg.info("== 'restart' shall restart a service that is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top2 = top # logg.info("-- and we check that there is a new PID for the service process") def find_pids(ps_output, command): pids = [] for line in _lines(ps_output): if command not in line: continue m = re.match(r"\s*[\d:]*\s+(\S+)\s+(\S+)\s+(.*)", line) pid, ppid, args = m.groups() # logg.info(" %s | %s | %s", pid, ppid, args) pids.append(pid) return pids ps1 = find_pids(running(top1), testsleep) ps2 = find_pids(running(top2), testsleep) logg.info("found PIDs %s and %s", ps1, ps2) self.assertTrue(len(ps1), 1) self.assertTrue(len(ps2), 1) self.assertNotEqual(ps1[0], ps2[0]) # logg.info("== 'reload' will NOT restart a service that is-active") cmd = "docker exec {testname} {systemctl} reload zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top3 = top # logg.info("-- and we check that there is NO new PID for the service process") ps3 = find_pids(running(top3), testsleep) logg.info("found PIDs %s and %s", ps2, ps3) self.assertTrue(len(ps2), 1) self.assertTrue(len(ps3), 1) self.assertEqual(ps2[0], ps3[0]) # logg.info("== 'reload-or-restart' will restart a service that is-active (if no ExecReload)") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top4 = top # logg.info("-- and we check that there is a new PID for the service process (if no ExecReload)") ps4 = find_pids(running(top4), testsleep) logg.info("found PIDs %s and %s", ps3, ps4) self.assertTrue(len(ps3), 1) self.assertTrue(len(ps4), 1) self.assertNotEqual(ps3[0], ps4[0]) # logg.info("== 'kill' will bring is-active non-active as well (when the PID is known)") cmd = "docker exec {testname} {systemctl} kill zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "failed") # logg.info("== 'stop' will turn 'failed' to 'inactive' (when the PID is known)") cmd = "docker exec {testname} {systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'reload-or-try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'reload-or-restart' will start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top5 = top # logg.info("== 'reload-or-try-restart' will restart an is-active service (with no ExecReload)") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top6 = top # logg.info("-- and we check that there is a new PID for the service process (if no ExecReload)") ps5 = find_pids(running(top5), testsleep) ps6 = find_pids(running(top6), testsleep) logg.info("found PIDs %s and %s", ps5, ps6) self.assertTrue(len(ps5), 1) self.assertTrue(len(ps6), 1) self.assertNotEqual(ps5[0], ps6[0]) # logg.info("== 'try-restart' will restart an is-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top7 = top # logg.info("-- and we check that there is a new PID for the service process") ps7 = find_pids(running(top7), testsleep) logg.info("found PIDs %s and %s", ps6, ps7) self.assertTrue(len(ps6), 1) self.assertTrue(len(ps7), 1) self.assertNotEqual(ps6[0], ps7[0]) # logg.info("LOG\n%s", " "+output("docker exec {testname} cat {logfile}".format(**locals())).replace("\n","\n ")) self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) def test_5134_usermode_notify_service_functions_system(self): """ check that we manage notify services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart.""" self.begin() testname = self.testname() testdir = self.testdir() self.usermode_notify_service_functions("system", testname, testdir) self.rm_testdir() self.coverage() self.end() def test_5135_usermode_notify_service_functions_user(self): """ check that we manage notify services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart.""" self.begin() testname = self.testname() testdir = self.testdir() self.usermode_notify_service_functions("user", testname, testdir) self.rm_testdir() self.end() def usermode_notify_service_functions(self, system, testname, testdir): if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) python = os.path.basename(_python) python_coverage = coverage_package(image) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) sometime = SOMETIME or 288 quick = "--coverage=quick" # user = self.user() root = "" systemctl_py = realpath(_systemctl_py) systemctl = "/usr/bin/systemctl" # path in container systemctl += " --user" # systemctl += " --{system}".format(**locals()) testsleep = testname+"_sleep" logfile = os_path(root, "/var/log/"+testsleep+".log") bindir = os_path(root, "/usr/bin") begin = "{" ; end = "}" shell_file(os_path(testdir, "zzz.init"), """ #! /bin/bash logfile={logfile} start() {begin} ls -l $NOTIFY_SOCKET {bindir}/{testsleep} 111 0<&- &>/dev/null & echo "MAINPID=$!" | socat -v -d - UNIX-CLIENT:$NOTIFY_SOCKET echo "READY=1" | socat -v -d - UNIX-CLIENT:$NOTIFY_SOCKET wait %1 # ps -o pid,ppid,user,args {end} stop() {begin} killall {testsleep} {end} case "$1" in start) date "+START.%T" >> $logfile start >> $logfile 2>&1 date "+start.%T" >> $logfile ;; stop) date "+STOP.%T" >> $logfile stop >> $logfile 2>&1 date "+stop.%T" >> $logfile ;; restart) date "+RESTART.%T" >> $logfile stop >> $logfile 2>&1 start >> $logfile 2>&1 date "+.%T" >> $logfile ;; reload) date "+RELOAD.%T" >> $logfile echo "...." >> $logfile 2>&1 date "+reload.%T" >> $logfile ;; esac echo "done$1" >&2 exit 0 """.format(**locals())) text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] User=somebody Type=notify # PIDFile={root}/var/run/zzz.init.pid ExecStart={root}/usr/bin/zzz.init start ExecStop={root}/usr/bin/zzz.init stop [Install] WantedBy=multi-user.target """.format(**locals())) cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/killall || {package} install -y psmisc'" sx____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/socat || {package} install -y socat'" if sx____(cmd.format(**locals())): self.skipTest("unable to install socat in a container from "+image) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker exec {testname} mkdir -p /etc/systemd/system /etc/systemd/user" sx____(cmd.format(**locals())) zzz_service = "/etc/systemd/{system}/zzz.service".format(**locals()) cmd = "docker cp /usr/bin/sleep {testname}:{bindir}/{testsleep}" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.init {testname}:/usr/bin/zzz.init" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.service {testname}:{zzz_service}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chmod 666 {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'grep nobody /etc/group || groupadd nobody'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} useradd somebody -g nobody -m" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chown somebody /tmp/.coverage" sx____(cmd.format(**locals())) # cmd = "docker commit -c 'CMD [\"/usr/bin/systemctl\"]' -c 'USER somebody' {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker rm -f {testname}" sh____(cmd.format(**locals())) cmd = "docker run -d --name {testname} {images}:{testname}" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} {systemctl} enable zzz.service -vv" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'start' shall start a service that is NOT is-active ") sh____("docker exec {testname} ls -l /var/run".format(**locals())) cmd = "docker exec {testname} {systemctl} start zzz.service -vv -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") # logg.info("== 'stop' shall stop a service that is-active") cmd = "docker exec {testname} {systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'restart' shall start a service that NOT is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "docker exec {testname} cat {logfile}" sh____(cmd.format(**locals())) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) self.assertEqual(end, 0) # cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top1= top # logg.info("== 'restart' shall restart a service that is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) cmd = "docker exec {testname} cat {logfile}" sh____(cmd.format(**locals())) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) self.assertEqual(end, 0) # cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top2 = top # logg.info("-- and we check that there is a new PID for the service process") def find_pids(ps_output, command): pids = [] for line in _lines(ps_output): if command not in line: continue m = re.match(r"\s*[\d:]*\s+(\S+)\s+(\S+)\s+(.*)", line) pid, ppid, args = m.groups() # logg.info(" %s | %s | %s", pid, ppid, args) pids.append(pid) return pids ps1 = find_pids(running(top1), testsleep) ps2 = find_pids(running(top2), testsleep) logg.info("found PIDs %s and %s", ps1, ps2) self.assertTrue(len(ps1), 1) self.assertTrue(len(ps2), 1) self.assertNotEqual(ps1[0], ps2[0]) # logg.info("== 'reload' will NOT restart a service that is-active") cmd = "docker exec {testname} {systemctl} reload zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top3 = top # logg.info("-- and we check that there is NO new PID for the service process") ps3 = find_pids(running(top3), testsleep) logg.info("found PIDs %s and %s", ps2, ps3) self.assertTrue(len(ps2), 1) self.assertTrue(len(ps3), 1) self.assertEqual(ps2[0], ps3[0]) # logg.info("== 'reload-or-restart' will restart a service that is-active (if no ExecReload)") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top4 = top # logg.info("-- and we check that there is a new PID for the service process (if no ExecReload)") ps4 = find_pids(running(top4), testsleep) logg.info("found PIDs %s and %s", ps3, ps4) self.assertTrue(len(ps3), 1) self.assertTrue(len(ps4), 1) self.assertNotEqual(ps3[0], ps4[0]) # logg.info("== 'kill' will bring is-active non-active as well (when the PID is known)") cmd = "docker exec {testname} {systemctl} kill zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'stop' will turn 'failed' to 'inactive' (when the PID is known)") cmd = "docker exec {testname} {systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'reload-or-try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-try-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'reload-or-restart' will start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top5 = top # logg.info("== 'reload-or-try-restart' will restart an is-active service (with no ExecReload)") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top6 = top # logg.info("-- and we check that there is a new PID for the service process (if no ExecReload)") ps5 = find_pids(running(top5), testsleep) ps6 = find_pids(running(top6), testsleep) logg.info("found PIDs %s and %s", ps5, ps6) self.assertTrue(len(ps5), 1) self.assertTrue(len(ps6), 1) self.assertNotEqual(ps5[0], ps6[0]) # logg.info("== 'try-restart' will restart an is-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top7 = top # logg.info("-- and we check that there is a new PID for the service process") ps7 = find_pids(running(top7), testsleep) logg.info("found PIDs %s and %s", ps6, ps7) self.assertTrue(len(ps6), 1) self.assertTrue(len(ps7), 1) self.assertNotEqual(ps6[0], ps7[0]) # logg.info("LOG\n%s", " "+output("docker exec {testname} cat {logfile}".format(**locals())).replace("\n","\n ")) # self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) def test_5136_usermode_notify_service_functions_with_reload(self): """ check that we manage notify services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart. (with ExecReload)""" self.begin() testname = self.testname() testdir = self.testdir() self.usermode_notify_service_functions_with_reload("system", testname, testdir) self.rm_testdir() logg.error("too long") #TODO self.end(200) def test_5137_usermode_notify_service_functions_with_reload_user(self): """ check that we manage notify services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart. (with ExecReload)""" # test_5037 is triggering len(socketfile) > 100 | "new notify socketfile" self.begin() testname = self.testname() testdir = self.testdir() self.usermode_notify_service_functions_with_reload("user", testname, testdir) self.coverage() self.end(266) #TODO# too long? def usermode_notify_service_functions_with_reload(self, system, testname, testdir): if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) python = os.path.basename(_python) python_coverage = coverage_package(image) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) sometime = SOMETIME or 288 quick = "--coverage=quick" # user = self.user() root = "" systemctl_py = realpath(_systemctl_py) systemctl = "/usr/bin/systemctl" # path in container systemctl += " --user" # systemctl += " --{system}".format(**locals()) testsleep = self.testname("sleep") logfile = os_path(root, "/var/log/"+testsleep+".log") bindir = os_path(root, "/usr/bin") begin = "{" ; end = "}" shell_file(os_path(testdir, "zzz.init"), """ #! /bin/bash logfile={logfile} start() {begin} ls -l $NOTIFY_SOCKET {bindir}/{testsleep} 111 0<&- &>/dev/null & echo "MAINPID=$!" | socat -v -d - UNIX-CLIENT:$NOTIFY_SOCKET echo "READY=1" | socat -v -d - UNIX-CLIENT:$NOTIFY_SOCKET wait %1 # ps -o pid,ppid,user,args {end} stop() {begin} killall {testsleep} {end} case "$1" in start) date "+START.%T" >> $logfile start >> $logfile 2>&1 date "+start.%T" >> $logfile ;; stop) date "+STOP.%T" >> $logfile stop >> $logfile 2>&1 date "+stop.%T" >> $logfile ;; restart) date "+RESTART.%T" >> $logfile stop >> $logfile 2>&1 start >> $logfile 2>&1 date "+.%T" >> $logfile ;; reload) date "+RELOAD.%T" >> $logfile echo "...." >> $logfile 2>&1 date "+reload.%T" >> $logfile ;; esac echo "done$1" >&2 exit 0 """.format(**locals())) text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] User=somebody Type=notify # PIDFile={root}/var/run/zzz.init.pid ExecStart={root}/usr/bin/zzz.init start ExecReload={root}/usr/bin/zzz.init reload ExecStop={root}/usr/bin/zzz.init stop TimeoutRestartSec=4 TimeoutReloadSec=4 [Install] WantedBy=multi-user.target """.format(**locals())) cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/killall || {package} install -y psmisc'" sx____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/socat || {package} install -y socat'" if sx____(cmd.format(**locals())): self.skipTest("unable to install socat in a container from "+image) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker exec {testname} mkdir -p /etc/systemd/system /etc/systemd/user" sx____(cmd.format(**locals())) zzz_service = "/etc/systemd/{system}/zzz.service".format(**locals()) cmd = "docker cp /usr/bin/sleep {testname}:{bindir}/{testsleep}" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.init {testname}:/usr/bin/zzz.init" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.service {testname}:{zzz_service}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chmod 666 {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'grep nobody /etc/group || groupadd nobody'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} useradd somebody -g nobody -m" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chown somebody /tmp/.coverage" sx____(cmd.format(**locals())) # cmd = "docker commit -c 'CMD [\"/usr/bin/systemctl\"]' -c 'USER somebody' {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker rm -f {testname}" sh____(cmd.format(**locals())) cmd = "docker run -d --name {testname} {images}:{testname}" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} {systemctl} enable zzz.service -vv" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'start' shall start a service that is NOT is-active ") cmd = "docker exec {testname} {systemctl} start zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") # logg.info("== 'stop' shall stop a service that is-active") cmd = "docker exec {testname} {systemctl} stop zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'restart' shall start a service that NOT is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top1= top # logg.info("== 'restart' shall restart a service that is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top2 = top # logg.info("-- and we check that there is a new PID for the service process") def find_pids(ps_output, command): pids = [] for line in _lines(ps_output): if command not in line: continue m = re.match(r"\s*[\d:]*\s+(\S+)\s+(\S+)\s+(.*)", line) pid, ppid, args = m.groups() # logg.info(" %s | %s | %s", pid, ppid, args) pids.append(pid) return pids ps1 = find_pids(running(top1), testsleep) ps2 = find_pids(running(top2), testsleep) logg.info("found PIDs %s and %s", ps1, ps2) self.assertTrue(len(ps1), 1) self.assertTrue(len(ps2), 1) self.assertNotEqual(ps1[0], ps2[0]) # logg.info("== 'reload' will NOT restart a service that is-active") cmd = "docker exec {testname} {systemctl} reload zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top3 = top # logg.info("-- and we check that there is NO new PID for the service process") ps3 = find_pids(running(top3), testsleep) logg.info("found PIDs %s and %s", ps2, ps3) self.assertTrue(len(ps2), 1) self.assertTrue(len(ps3), 1) self.assertEqual(ps2[0], ps3[0]) # logg.info("== 'reload-or-restart' will restart a service that is-active (if no ExecReload)") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top4 = top # logg.info("-- and we check that there is the same PID for the service process (if ExecReload)") ps4 = find_pids(running(top4), testsleep) logg.info("found PIDs %s and %s", ps3, ps4) self.assertTrue(len(ps3), 1) self.assertTrue(len(ps4), 1) self.assertEqual(ps3[0], ps4[0]) # logg.info("== 'kill' will bring is-active non-active as well (when the PID is known)") cmd = "docker exec {testname} {systemctl} kill zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'stop' will turn 'failed' to 'inactive' (when the PID is known)") #TODO# cmd = "docker exec {testname} {systemctl} stop zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'reload-or-try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-try-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "inactive") # logg.info("== 'reload-or-restart' will start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top5 = top # logg.info("== 'reload-or-try-restart' will restart an is-active service (with no ExecReload)") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top6 = top # logg.info("-- and we check that there is a new PID for the service process (if no ExecReload)") ps5 = find_pids(running(top5), testsleep) ps6 = find_pids(running(top6), testsleep) logg.info("found PIDs %s and %s", ps5, ps6) self.assertTrue(len(ps5), 1) self.assertTrue(len(ps6), 1) self.assertNotEqual(ps5[0], ps6[0]) # logg.info("== 'try-restart' will restart an is-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) top = _recent(output("docker exec {testname} ps -eo etime,pid,ppid,user,args".format(**locals()))) logg.info("\n>>>\n%s", top) self.assertTrue(running(greps(top, testsleep))) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 0) self.assertEqual(out.strip(), "active") top7 = top # logg.info("-- and we check that there is a new PID for the service process") ps7 = find_pids(running(top7), testsleep) logg.info("found PIDs %s and %s", ps6, ps7) self.assertTrue(len(ps6), 1) self.assertTrue(len(ps7), 1) self.assertNotEqual(ps6[0], ps7[0]) # logg.info("LOG\n%s", " "+output("docker exec {testname} cat {logfile}".format(**locals())).replace("\n","\n ")) # self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) def test_5140_usermode_oneshot_service_functions(self): """ check that we manage oneshot services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart.""" self.begin() testname = self.testname() testdir = self.testdir() self.usermode_oneshot_service_functions("system", testname, testdir) self.rm_testdir() self.end() def test_5141_usermode_oneshot_service_functions_user(self): """ check that we manage oneshot services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart.""" self.begin() testname = self.testname() testdir = self.testdir() self.usermode_oneshot_service_functions("user", testname, testdir) self.rm_testdir() self.end() def usermode_oneshot_service_functions(self, system, testname, testdir): if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) python = os.path.basename(_python) python_coverage = coverage_package(image) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) sometime = SOMETIME or 188 quick = "--coverage=quick" # user = self.user() root = "" systemctl_py = realpath(_systemctl_py) systemctl = "/usr/bin/systemctl" # path in container systemctl += " --user" # systemctl += " --{system}".format(**locals()) testsleep = self.testname("sleep") logfile = os_path(root, "/var/log/"+testsleep+".log") bindir = os_path(root, "/usr/bin") begin = "{" ; end = "}" text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] User=somebody Type=oneshot ExecStartPre={bindir}/backup {root}/var/tmp/test.1 {root}/var/tmp/test.2 ExecStart=/usr/bin/touch {root}/var/tmp/test.1 ExecStop=/bin/rm {root}/var/tmp/test.1 ExecStopPost=/bin/rm -f {root}/var/tmp/test.2 [Install] WantedBy=multi-user.target """.format(**locals())) shell_file(os_path(testdir, "backup"), """ #! /bin/sh set -x test ! -f "$1" || mv -v "$1" "$2" """) cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/killall || {package} install -y psmisc'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker exec {testname} mkdir -p /etc/systemd/system /etc/systemd/user" sx____(cmd.format(**locals())) zzz_service = "/etc/systemd/{system}/zzz.service".format(**locals()) cmd = "docker cp /usr/bin/sleep {testname}:{bindir}/{testsleep}" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.service {testname}:{zzz_service}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chmod 666 {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'grep nobody /etc/group || groupadd nobody'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} useradd somebody -g nobody -m" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/backup {testname}:/usr/bin/backup" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch /var/tmp/test.0" sh____(cmd.format(**locals())) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertFalse(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chown somebody /tmp/.coverage" sx____(cmd.format(**locals())) # cmd = "docker commit -c 'CMD [\"/usr/bin/systemctl\"]' -c 'USER somebody' {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker rm -f {testname}" sh____(cmd.format(**locals())) cmd = "docker run -d --name {testname} {images}:{testname}" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} {systemctl} enable zzz.service -vv" sh____(cmd.format(**locals())) # is_active = "docker exec {testname} {systemctl} is-active zzz.service -vv" act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive") self.assertEqual(end, 3) # logg.info("== 'start' shall start a service that is NOT is-active ") cmd = "docker exec {testname} {systemctl} start zzz.service -vvvv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active") self.assertEqual(end, 0) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertTrue(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'stop' shall stop a service that is-active") cmd = "docker exec {testname} {systemctl} stop zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertFalse(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'restart' shall start a service that NOT is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active") self.assertEqual(end, 0) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertTrue(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'restart' shall restart a service that is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active") self.assertEqual(end, 0) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertTrue(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'reload' will NOT restart a service that is-active") cmd = "docker exec {testname} {systemctl} reload zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active") self.assertEqual(end, 0) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertTrue(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'reload-or-restart' will restart a service that is-active") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active") self.assertEqual(end, 0) # logg.info("== 'stop' will brings it back to 'inactive'") cmd = "docker exec {testname} {systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertFalse(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'reload-or-try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-try-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertFalse(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertFalse(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'reload-or-restart' will start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active") self.assertEqual(end, 0) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertTrue(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'reload-or-try-restart' will restart an is-active service") cmd = "docker exec {testname} {systemctl} reload-or-try-restart zzz.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active") self.assertEqual(end, 0) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertTrue(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'try-restart' will restart an is-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active") self.assertEqual(end, 0) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertTrue(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'stop' will brings it back to 'inactive'") cmd = "docker exec {testname} {systemctl} stop zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertFalse(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("LOG\n%s", " "+output("docker exec {testname} cat {logfile}".format(**locals())).replace("\n","\n ")) # self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) def test_5142_usermode_oneshot_and_unknown_service_functions(self): """ check that we manage multiple services even when some services are not actually known. Along with oneshot serivce with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart / we have only different exit-code.""" self.begin() testname = self.testname() testdir = self.testdir() if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) python = os.path.basename(_python) python_coverage = coverage_package(image) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) sometime = SOMETIME or 188 quick = "--coverage=quick" # user = self.user() root = "" systemctl_py = realpath(_systemctl_py) systemctl = "/usr/bin/systemctl" # path in container systemctl += " --user" testsleep = self.testname("sleep") logfile = os_path(root, "/var/log/"+testsleep+".log") bindir = os_path(root, "/usr/bin") begin = "{" ; end = "}" text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] User=somebody Type=oneshot ExecStartPre={bindir}/backup {root}/var/tmp/test.1 {root}/var/tmp/test.2 ExecStart=/usr/bin/touch {root}/var/tmp/test.1 ExecStop=/bin/rm {root}/var/tmp/test.1 ExecStopPost=/bin/rm -f {root}/var/tmp/test.2 [Install] WantedBy=multi-user.target """.format(**locals())) shell_file(os_path(testdir, "backup"), """ #! /bin/sh set -x test ! -f "$1" || mv -v "$1" "$2" """) cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/killall || {package} install -y psmisc'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker exec {testname} mkdir -p /etc/systemd/system /etc/systemd/user" sx____(cmd.format(**locals())) cmd = "docker cp /usr/bin/sleep {testname}:{bindir}/{testsleep}" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.service {testname}:/etc/systemd/system/zzz.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/backup {testname}:/usr/bin/backup" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch /var/tmp/test.0" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chmod 666 {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'grep nobody /etc/group || groupadd nobody'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} useradd somebody -g nobody -m" sh____(cmd.format(**locals())) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertFalse(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chown somebody /tmp/.coverage" sx____(cmd.format(**locals())) # cmd = "docker commit -c 'CMD [\"/usr/bin/systemctl\"]' -c 'USER somebody' {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker rm -f {testname}" sh____(cmd.format(**locals())) cmd = "docker run -d --name {testname} {images}:{testname}" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} {systemctl} enable zzz.service -vv" sh____(cmd.format(**locals())) is_active = "docker exec {testname} {systemctl} is-active zzz.service other.service -vv" act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive\nunknown") self.assertEqual(end, 3) # logg.info("== 'start' shall start a service that is NOT is-active ") cmd = "docker exec {testname} {systemctl} start zzz.service other.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) is_active = "docker exec {testname} {systemctl} is-active zzz.service other.service -vv" act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active\nunknown") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertTrue(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'stop' shall stop a service that is-active") cmd = "docker exec {testname} {systemctl} stop zzz.service other.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive\nunknown") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertFalse(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'restart' shall start a service that NOT is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service other.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active\nunknown") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertTrue(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'restart' shall restart a service that is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service other.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active\nunknown") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertTrue(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'reload' will NOT restart a service that is-active") cmd = "docker exec {testname} {systemctl} reload zzz.service other.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active\nunknown") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertTrue(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'reload-or-restart' will restart a service that is-active") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service other.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active\nunknown") self.assertEqual(end, 3) # logg.info("== 'stop' will brings it back to 'inactive'") cmd = "docker exec {testname} {systemctl} stop zzz.service other.service -vv" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive\nunknown") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertFalse(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'reload-or-try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-try-restart zzz.service other.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive\nunknown") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertFalse(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service other.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive\nunknown") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertFalse(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'reload-or-restart' will start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service other.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active\nunknown") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertTrue(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'reload-or-try-restart' will restart an is-active service") cmd = "docker exec {testname} {systemctl} reload-or-try-restart zzz.service other.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active\nunknown") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertTrue(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'try-restart' will restart an is-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service other.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "active\nunknown") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertTrue(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("== 'stop' will brings it back to 'inactive'") cmd = "docker exec {testname} {systemctl} stop zzz.service other.service -vv {quick}" out, end = output2(cmd.format(**locals())) logg.info("%s =>\n%s", cmd, out) self.assertNotEqual(end, 0) act, end = output2(is_active.format(**locals())) self.assertEqual(act.strip(), "inactive\nunknown") self.assertEqual(end, 3) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertFalse(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # logg.info("LOG\n%s", " "+output("docker exec {testname} cat {logfile}".format(**locals())).replace("\n","\n ")) # self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) self.rm_testdir() self.end() def test_5144_usermode_sysv_service_functions(self): """ check that we are disallowed to manage SysV services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart.""" self.begin() testname = self.testname() testdir = self.testdir() if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) python = os.path.basename(_python) python_coverage = coverage_package(image) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) sometime = SOMETIME or 188 quick = "--coverage=quick" # user = self.user() root = "" systemctl_py = realpath(_systemctl_py) systemctl = "/usr/bin/systemctl" # path in container systemctl += " --user" testsleep = self.testname("sleep") logfile = os_path(root, "/var/log/"+testsleep+".log") bindir = os_path(root, "/usr/bin") begin = "{" ; end = "}" shell_file(os_path(testdir, "zzz.init"), """ #! /bin/bash ### BEGIN INIT INFO # Required-Start: $local_fs $remote_fs $syslog $network # Required-Stop: $local_fs $remote_fs $syslog $network # Default-Start: 3 5 # Default-Stop: 0 1 2 6 # Short-Description: Testing Z # Description: Allows for SysV testing ### END INIT INFO logfile={logfile} sleeptime=111 start() {begin} [ -d /var/run ] || mkdir -p /var/run (runuser -u somebody {bindir}/{testsleep} $sleeptime 0<&- &>/dev/null & echo $! > {root}/var/run/zzz.init.pid ) & wait %1 # ps -o pid,ppid,user,args cat "RUNNING `cat {root}/var/run/zzz.init.pid`" {end} stop() {begin} kill `cat {root}/var/run/zzz.init.pid` >>$logfile 2>&1 killall {testsleep} >>$logfile 2>&1 {end} case "$1" in start) date "+START.%T" >> $logfile start >> $logfile 2>&1 date "+start.%T" >> $logfile ;; stop) date "+STOP.%T" >> $logfile stop >> $logfile 2>&1 date "+stop.%T" >> $logfile ;; restart) date "+RESTART.%T" >> $logfile stop >> $logfile 2>&1 start >> $logfile 2>&1 date "+.%T" >> $logfile ;; reload) date "+RELOAD.%T" >> $logfile echo "...." >> $logfile 2>&1 date "+reload.%T" >> $logfile ;; esac echo "done$1" >&2 exit 0 """.format(**locals())) cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/killall || {package} install -y psmisc'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker cp /usr/bin/sleep {testname}:{bindir}/{testsleep}" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.init {testname}:/etc/init.d/zzz" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chmod 666 {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'grep nobody /etc/group || groupadd nobody'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} useradd somebody -g nobody -m" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chown somebody /tmp/.coverage" sx____(cmd.format(**locals())) # cmd = "docker commit -c 'CMD [\"/usr/bin/systemctl\"]' -c 'USER somebody' {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker rm -f {testname}" sh____(cmd.format(**locals())) cmd = "docker run -d --name {testname} {images}:{testname}" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} {systemctl} enable zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Initscript zzz.service not for --user mode")) # # .................... deleted stuff start/stop/etc # self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) self.rm_testdir() self.end() # # def test_5230_bad_usermode_simple_service_functions_system(self): """ check that we are disallowed to manage simple services in a root env with commands like start, restart, stop, etc""" self.begin() testname = self.testname() testdir = self.testdir() self.bad_usermode_simple_service_functions("", testname, testdir) self.rm_testdir() self.end() def test_5231_bad_simple_service_functions_user(self): """ check that we are disallowed to manage simple services in a root env with commands like start, restart, stop, etc""" self.begin() testname = self.testname() testdir = self.testdir() self.bad_usermode_simple_service_functions("User=foo", testname, testdir) self.rm_testdir() self.end() def bad_usermode_simple_service_functions(self, extra, testname, testdir): """ check that we are disallowed to manage simple services in a root env with commands like start, restart, stop, etc""" if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) python = os.path.basename(_python) python_coverage = coverage_package(image) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) sometime = SOMETIME or 288 quick = "--coverage=quick" # user = self.user() root = "" systemctl_py = realpath(_systemctl_py) systemctl = "/usr/bin/systemctl" # path in container systemctl += " --user" testsleep = testname+"_testsleep" testscript = testname+"_testscript.sh" logfile = os_path(root, "/var/log/test.log") bindir = os_path(root, "/usr/bin") begin = "{" end = "}" text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] {extra} Type=simple ExecStartPre=/bin/echo %n ExecStart={bindir}/{testscript} 111 ExecStartPost=/bin/echo started $MAINPID ExecStop=/bin/kill -3 $MAINPID ExecStopPost=/bin/echo stopped $MAINPID ExecStopPost=/bin/sleep 2 ExecReload=/bin/kill -10 $MAINPID KillSignal=SIGQUIT [Install] WantedBy=multi-user.target """.format(**locals())) shell_file(os_path(testdir, testscript),""" #! /bin/sh date +%T,enter >> {logfile} stops () {begin} date +%T,stopping >> {logfile} killall {testsleep} >> {logfile} 2>&1 date +%T,stopped >> {logfile} {end} reload () {begin} date +%T,reloading >> {logfile} date +%T,reloaded >> {logfile} {end} trap "stops" 3 # SIGQUIT trap "reload" 10 # SIGUSR1 date +%T,starting >> {logfile} {bindir}/{testsleep} $1 >> {logfile} 2>&1 & pid="$!" while kill -0 $pid; do # use 'kill -0' to check the existance of the child date +%T,waiting >> {logfile} # use 'wait' for children AND external signals wait done date +%T,leaving >> {logfile} trap - 3 10 # SIGQUIT SIGUSR1 date +%T,leave >> {logfile} """.format(**locals())) cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/killall || {package} install -y psmisc'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker cp /usr/bin/sleep {testname}:{bindir}/{testsleep}" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/{testscript} {testname}:{bindir}/{testscript}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} mkdir -p /etc/systemd/system /etc/systemd/user" sx____(cmd.format(**locals())) zzz_service = "/etc/systemd/system/zzz.service".format(**locals()) cmd = "docker cp {testdir}/zzz.service {testname}:{zzz_service}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chmod 666 {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'grep nobody /etc/group || groupadd nobody'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} useradd somebody -g nobody -m" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chown somebody /tmp/.coverage" sx____(cmd.format(**locals())) # cmd = "docker commit -c 'CMD [\"/usr/bin/systemctl\"]' -c 'USER somebody' {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker rm -f {testname}" sh____(cmd.format(**locals())) cmd = "docker run -d --name {testname} {images}:{testname}" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} {systemctl} enable zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s \n%s", cmd, end, out) self.assertEqual(end, 3) self.assertEqual(out.strip(), "unknown") # logg.info("== 'start' shall start a service that is NOT is-active ") cmd = "docker exec {testname} {systemctl} start zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'stop' shall stop a service that is-active") cmd = "docker exec {testname} {systemctl} stop zzz.service -vv {quick}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) #TODO? #TODO?# self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'restart' shall start a service that NOT is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service -vvvv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'reload' will NOT restart a service that is-active") cmd = "docker exec {testname} {systemctl} reload zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'reload-or-restart' will restart a service that is-active (if ExecReload)") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'kill' will bring is-active non-active as well (when the PID is known)") cmd = "docker exec {testname} {systemctl} kill zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'reload-or-try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-try-restart zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) def test_5232_bad_usermode_forking_service_functions_system(self): """ check that we are disallowed to manage forking services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart.""" self.begin() testname = self.testname() testdir = self.testdir() self.bad_usermode_forking_service_functions("", testname, testdir) self.rm_testdir() self.end() def test_5233_bad_usermode_forking_service_functions_user(self): """ check that we are disallowed to manage forking services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart.""" self.begin() testname = self.testname() testdir = self.testdir() self.bad_usermode_forking_service_functions("User=foo", testname, testdir) self.rm_testdir() self.end() def bad_usermode_forking_service_functions(self, extra, testname, testdir): if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) python = os.path.basename(_python) python_coverage = coverage_package(image) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) sometime = SOMETIME or 188 quick = "--coverage=quick" # user = self.user() root = "" systemctl_py = realpath(_systemctl_py) systemctl = "/usr/bin/systemctl" # path in container systemctl += " --user" # systemctl += " --{system}".format(**locals()) testsleep = testname+"_sleep" logfile = os_path(root, "/var/log/"+testsleep+".log") bindir = os_path(root, "/usr/bin") begin = "{" ; end = "}" shell_file(os_path(testdir, "zzz.init"), """ #! /bin/bash logfile={logfile} start() {begin} [ -d /var/run ] || mkdir -p /var/run ({bindir}/{testsleep} 111 0<&- &>/dev/null & echo $! > /tmp/zzz.init.pid ) & wait %1 # ps -o pid,ppid,user,args {end} stop() {begin} killall {testsleep} {end} case "$1" in start) date "+START.%T" >> $logfile start >> $logfile 2>&1 date "+start.%T" >> $logfile ;; stop) date "+STOP.%T" >> $logfile stop >> $logfile 2>&1 date "+stop.%T" >> $logfile ;; restart) date "+RESTART.%T" >> $logfile stop >> $logfile 2>&1 start >> $logfile 2>&1 date "+.%T" >> $logfile ;; reload) date "+RELOAD.%T" >> $logfile echo "...." >> $logfile 2>&1 date "+reload.%T" >> $logfile ;; esac echo "done$1" >&2 exit 0 """.format(**locals())) text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] {extra} Type=forking PIDFile=/tmp/zzz.init.pid ExecStart=/usr/bin/zzz.init start ExecStop=/usr/bin/zzz.init stop [Install] WantedBy=multi-user.target """.format(**locals())) cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/killall || {package} install -y psmisc'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker exec {testname} mkdir -p /etc/systemd/system /etc/systemd/user" sx____(cmd.format(**locals())) zzz_service = "/etc/systemd/system/zzz.service".format(**locals()) cmd = "docker cp /usr/bin/sleep {testname}:{bindir}/{testsleep}" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.init {testname}:/usr/bin/zzz.init" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.service {testname}:{zzz_service}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chmod 666 {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'grep nobody /etc/group || groupadd nobody'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} useradd somebody -g nobody -m" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chown somebody /tmp/.coverage" sx____(cmd.format(**locals())) # cmd = "docker commit -c 'CMD [\"/usr/bin/systemctl\"]' -c 'USER somebody' {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker rm -f {testname}" sh____(cmd.format(**locals())) cmd = "docker run -d --name {testname} {images}:{testname}" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} {systemctl} enable zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 3) #TODO?# self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) self.assertEqual(out.strip(), "unknown") # logg.info("== 'start' shall start a service that is NOT is-active ") cmd = "docker exec {testname} {systemctl} start zzz.service -vvvv {quick}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'stop' shall stop a service that is-active") cmd = "docker exec {testname} {systemctl} stop zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'restart' shall start a service that NOT is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'reload' will NOT restart a service that is-active") cmd = "docker exec {testname} {systemctl} reload zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'reload-or-restart' will restart a service that is-active (if no ExecReload)") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'kill' will bring is-active non-active as well (when the PID is known)") cmd = "docker exec {testname} {systemctl} kill zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'reload-or-try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-try-restart zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'reload-or-restart' will start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # # self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) def test_5234_bad_usermode_notify_service_functions_system(self): """ check that we are disallowed to manage notify services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart.""" self.begin() testname = self.testname() testdir = self.testdir() self.bad_usermode_notify_service_functions("", testname, testdir) self.rm_testdir() self.coverage() self.end() def test_5235_bad_usermode_notify_service_functions_user(self): """ check that we are disallowed to manage notify services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart.""" self.begin() testname = self.testname() testdir = self.testdir() self.bad_usermode_notify_service_functions("User=foo", testname, testdir) self.rm_testdir() self.end(266) #TODO# too long? def bad_usermode_notify_service_functions(self, extra, testname, testdir): if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) python = os.path.basename(_python) python_coverage = coverage_package(image) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) sometime = SOMETIME or 288 quick = "--coverage=quick" # user = self.user() root = "" systemctl_py = realpath(_systemctl_py) systemctl = "/usr/bin/systemctl" # path in container systemctl += " --user" # systemctl += " --{system}".format(**locals()) testsleep = testname+"_sleep" logfile = os_path(root, "/var/log/"+testsleep+".log") bindir = os_path(root, "/usr/bin") begin = "{" ; end = "}" shell_file(os_path(testdir, "zzz.init"), """ #! /bin/bash logfile={logfile} start() {begin} ls -l $NOTIFY_SOCKET {bindir}/{testsleep} 111 0<&- &>/dev/null & echo "MAINPID=$!" | socat -v -d - UNIX-CLIENT:$NOTIFY_SOCKET echo "READY=1" | socat -v -d - UNIX-CLIENT:$NOTIFY_SOCKET wait %1 # ps -o pid,ppid,user,args {end} stop() {begin} killall {testsleep} {end} case "$1" in start) date "+START.%T" >> $logfile start >> $logfile 2>&1 date "+start.%T" >> $logfile ;; stop) date "+STOP.%T" >> $logfile stop >> $logfile 2>&1 date "+stop.%T" >> $logfile ;; restart) date "+RESTART.%T" >> $logfile stop >> $logfile 2>&1 start >> $logfile 2>&1 date "+.%T" >> $logfile ;; reload) date "+RELOAD.%T" >> $logfile echo "...." >> $logfile 2>&1 date "+reload.%T" >> $logfile ;; esac echo "done$1" >&2 exit 0 """.format(**locals())) text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] {extra} Type=notify # PIDFile={root}/var/run/zzz.init.pid ExecStart={root}/usr/bin/zzz.init start ExecStop={root}/usr/bin/zzz.init stop [Install] WantedBy=multi-user.target """.format(**locals())) cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/killall || {package} install -y psmisc'" sx____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/socat || {package} install -y socat'" if sx____(cmd.format(**locals())): self.skipTest("unable to install socat in a container from "+image) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker exec {testname} mkdir -p /etc/systemd/system /etc/systemd/user" sx____(cmd.format(**locals())) zzz_service = "/etc/systemd/system/zzz.service".format(**locals()) cmd = "docker cp /usr/bin/sleep {testname}:{bindir}/{testsleep}" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.init {testname}:/usr/bin/zzz.init" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.service {testname}:{zzz_service}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chmod 666 {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'grep nobody /etc/group || groupadd nobody'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} useradd somebody -g nobody -m" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chown somebody /tmp/.coverage" sx____(cmd.format(**locals())) # cmd = "docker commit -c 'CMD [\"/usr/bin/systemctl\"]' -c 'USER somebody' {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker rm -f {testname}" sh____(cmd.format(**locals())) cmd = "docker run -d --name {testname} {images}:{testname}" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} {systemctl} enable zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 3) #TODO?# self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) self.assertEqual(out.strip(), "unknown") # logg.info("== 'start' shall start a service that is NOT is-active ") cmd = "docker exec {testname} {systemctl} start zzz.service -vv -vv {quick}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'stop' shall stop a service that is-active") cmd = "docker exec {testname} {systemctl} stop zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'restart' shall start a service that NOT is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service -vv {quick}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'reload' will NOT restart a service that is-active") cmd = "docker exec {testname} {systemctl} reload zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'reload-or-restart' will restart a service that is-active (if no ExecReload)") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service -vv {quick}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'kill' will bring is-active non-active as well (when the PID is known)") cmd = "docker exec {testname} {systemctl} kill zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'reload-or-try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-try-restart zzz.service -vv {quick}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'reload-or-restart' will start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service -vv {quick}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) def test_5236_bad_usermode_notify_service_functions_with_reload(self): """ check that we manage notify services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart. (with ExecReload)""" self.begin() testname = self.testname() testdir = self.testdir() self.bad_usermode_notify_service_functions_with_reload("", testname, testdir) self.rm_testdir() logg.error("too long") #TODO self.end(200) def test_5237_bad_usermode_notify_service_functions_with_reload_user(self): """ check that we are disallowed to manage notify services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart. (with ExecReload)""" # test_5037 is triggering len(socketfile) > 100 | "new notify socketfile" self.begin() testname = self.testname() testdir = self.testdir() self.bad_usermode_notify_service_functions_with_reload("User=foo", testname, testdir) self.coverage() self.end(266) #TODO# too long? def bad_usermode_notify_service_functions_with_reload(self, extra, testname, testdir): if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) python = os.path.basename(_python) python_coverage = coverage_package(image) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) sometime = SOMETIME or 288 quick = "--coverage=quick" # user = self.user() root = "" systemctl_py = realpath(_systemctl_py) systemctl = "/usr/bin/systemctl" # path in container systemctl += " --user" # systemctl += " --{system}".format(**locals()) testsleep = self.testname("sleep") logfile = os_path(root, "/var/log/"+testsleep+".log") bindir = os_path(root, "/usr/bin") begin = "{" ; end = "}" shell_file(os_path(testdir, "zzz.init"), """ #! /bin/bash logfile={logfile} start() {begin} ls -l $NOTIFY_SOCKET {bindir}/{testsleep} 111 0<&- &>/dev/null & echo "MAINPID=$!" | socat -v -d - UNIX-CLIENT:$NOTIFY_SOCKET echo "READY=1" | socat -v -d - UNIX-CLIENT:$NOTIFY_SOCKET wait %1 # ps -o pid,ppid,user,args {end} stop() {begin} killall {testsleep} {end} case "$1" in start) date "+START.%T" >> $logfile start >> $logfile 2>&1 date "+start.%T" >> $logfile ;; stop) date "+STOP.%T" >> $logfile stop >> $logfile 2>&1 date "+stop.%T" >> $logfile ;; restart) date "+RESTART.%T" >> $logfile stop >> $logfile 2>&1 start >> $logfile 2>&1 date "+.%T" >> $logfile ;; reload) date "+RELOAD.%T" >> $logfile echo "...." >> $logfile 2>&1 date "+reload.%T" >> $logfile ;; esac echo "done$1" >&2 exit 0 """.format(**locals())) text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] {extra} Type=notify # PIDFile={root}/var/run/zzz.init.pid ExecStart={root}/usr/bin/zzz.init start ExecReload={root}/usr/bin/zzz.init reload ExecStop={root}/usr/bin/zzz.init stop TimeoutRestartSec=4 TimeoutReloadSec=4 [Install] WantedBy=multi-user.target """.format(**locals())) cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/killall || {package} install -y psmisc'" sx____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/socat || {package} install -y socat'" if sx____(cmd.format(**locals())): self.skipTest("unable to install socat in a container from "+image) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker exec {testname} mkdir -p /etc/systemd/system /etc/systemd/user" sx____(cmd.format(**locals())) zzz_service = "/etc/systemd/system/zzz.service".format(**locals()) cmd = "docker cp /usr/bin/sleep {testname}:{bindir}/{testsleep}" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.init {testname}:/usr/bin/zzz.init" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.service {testname}:{zzz_service}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chmod 666 {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'grep nobody /etc/group || groupadd nobody'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} useradd somebody -g nobody -m" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chown somebody /tmp/.coverage" sx____(cmd.format(**locals())) # cmd = "docker commit -c 'CMD [\"/usr/bin/systemctl\"]' -c 'USER somebody' {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker rm -f {testname}" sh____(cmd.format(**locals())) cmd = "docker run -d --name {testname} {images}:{testname}" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} {systemctl} enable zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 3) #TODO?# self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) self.assertEqual(out.strip(), "unknown") # logg.info("== 'start' shall start a service that is NOT is-active ") cmd = "docker exec {testname} {systemctl} start zzz.service -vv {quick}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'stop' shall stop a service that is-active") cmd = "docker exec {testname} {systemctl} stop zzz.service -vv {quick}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'restart' shall start a service that NOT is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service -vv {quick}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'reload' will NOT restart a service that is-active") cmd = "docker exec {testname} {systemctl} reload zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'reload-or-restart' will restart a service that is-active (if no ExecReload)") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service -vv {quick}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'kill' will bring is-active non-active as well (when the PID is known)") cmd = "docker exec {testname} {systemctl} kill zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'reload-or-try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-try-restart zzz.service -vv {quick}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv {quick}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) def test_5240_bad_usermode_oneshot_service_functions(self): """ check that we are disallowed to manage oneshot services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart.""" self.begin() testname = self.testname() testdir = self.testdir() self.bad_usermode_oneshot_service_functions("", testname, testdir) self.rm_testdir() self.end() def test_5241_bad_usermode_oneshot_service_functions_user(self): """ check that we are disallowed to manage oneshot services in a root env with basic run-service commands: start, stop, restart, reload, try-restart, reload-or-restart, kill and reload-or-try-restart.""" self.begin() testname = self.testname() testdir = self.testdir() self.bad_usermode_oneshot_service_functions("User=foo", testname, testdir) self.rm_testdir() self.end() def bad_usermode_oneshot_service_functions(self, extra, testname, testdir): if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) python = os.path.basename(_python) python_coverage = coverage_package(image) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) sometime = SOMETIME or 188 quick = "--coverage=quick" # user = self.user() root = "" systemctl_py = realpath(_systemctl_py) systemctl = "/usr/bin/systemctl" # path in container systemctl += " --user" # systemctl += " --{system}".format(**locals()) testsleep = self.testname("sleep") logfile = os_path(root, "/var/log/"+testsleep+".log") bindir = os_path(root, "/usr/bin") begin = "{" ; end = "}" text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] {extra} Type=oneshot ExecStartPre={bindir}/backup {root}/var/tmp/test.1 {root}/var/tmp/test.2 ExecStart=/usr/bin/touch {root}/var/tmp/test.1 ExecStop=/bin/rm {root}/var/tmp/test.1 ExecStopPost=/bin/rm -f {root}/var/tmp/test.2 [Install] WantedBy=multi-user.target """.format(**locals())) shell_file(os_path(testdir, "backup"), """ #! /bin/sh set -x test ! -f "$1" || mv -v "$1" "$2" """) cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/killall || {package} install -y psmisc'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker exec {testname} mkdir -p /etc/systemd/system /etc/systemd/user" sx____(cmd.format(**locals())) zzz_service = "/etc/systemd/system/zzz.service".format(**locals()) cmd = "docker cp /usr/bin/sleep {testname}:{bindir}/{testsleep}" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.service {testname}:{zzz_service}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chmod 666 {logfile}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'grep nobody /etc/group || groupadd nobody'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} useradd somebody -g nobody -m" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/backup {testname}:/usr/bin/backup" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch /var/tmp/test.0" sh____(cmd.format(**locals())) testfiles = output("docker exec {testname} find /var/tmp -name test.*".format(**locals())) logg.info("found testfiles:\n%s", testfiles) self.assertFalse(greps(testfiles, "/var/tmp/test.1")) self.assertFalse(greps(testfiles, "/var/tmp/test.2")) # cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chown somebody /tmp/.coverage" sx____(cmd.format(**locals())) # cmd = "docker commit -c 'CMD [\"/usr/bin/systemctl\"]' -c 'USER somebody' {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker rm -f {testname}" sh____(cmd.format(**locals())) cmd = "docker run -d --name {testname} {images}:{testname}" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} {systemctl} enable zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # is_active = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) self.assertEqual(out.strip(), "") #TODO# # logg.info("== 'start' shall start a service that is NOT is-active ") cmd = "docker exec {testname} {systemctl} start zzz.service -vvvv {quick}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'stop' shall stop a service that is-active") cmd = "docker exec {testname} {systemctl} stop zzz.service -vv {quick}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'restart' shall start a service that NOT is-active") cmd = "docker exec {testname} {systemctl} restart zzz.service -vv {quick}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'reload' will NOT restart a service that is-active") cmd = "docker exec {testname} {systemctl} reload zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'reload-or-restart' will restart a service that is-active") cmd = "docker exec {testname} {systemctl} reload-or-restart zzz.service -vv {quick}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'reload-or-try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} reload-or-try-restart zzz.service -vv {quick}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # logg.info("== 'try-restart' will not start a not-active service") cmd = "docker exec {testname} {systemctl} try-restart zzz.service -vv {quick}" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) def test_5290_bad_usermode_other_commands(self): """ check that we are disallowed to manage oneshot services in a root env with other commands: enable, disable, mask, unmaks,...""" self.begin() testname = self.testname() testdir = self.testdir() self.bad_usermode_other_commands("", testname, testdir) self.rm_testdir() self.end() def test_5291_bad_usermode_other_commands(self): """ check that we are disallowed to manage oneshot services in a root env with other commands: enable, disable, mask, unmaks,...""" self.begin() testname = self.testname() testdir = self.testdir() self.bad_usermode_other_commands("User=foo", testname, testdir) self.rm_testdir() self.end() def bad_usermode_other_commands(self, extra, testname, testdir): if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) python = os.path.basename(_python) python_coverage = coverage_package(image) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) sometime = SOMETIME or 188 quick = "--coverage=quick" # user = self.user() root = "" systemctl_py = realpath(_systemctl_py) systemctl = "/usr/bin/systemctl" # path in container systemctl += " --user" # systemctl += " --{system}".format(**locals()) testsleep = self.testname("sleep") logfile = os_path(root, "/var/log/"+testsleep+".log") bindir = os_path(root, "/usr/bin") begin = "{" ; end = "}" text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] {extra} Type=simple ExecStart=/usr/bin/{testsleep} 11 [Install] WantedBy=multi-user.target """.format(**locals())) shell_file(os_path(testdir, "backup"), """ #! /bin/sh set -x test ! -f "$1" || mv -v "$1" "$2" """) cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/killall || {package} install -y psmisc'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker exec {testname} mkdir -p /etc/systemd/system /etc/systemd/user" sx____(cmd.format(**locals())) zzz_service = "/etc/systemd/system/zzz.service".format(**locals()) cmd = "docker cp /usr/bin/sleep {testname}:{bindir}/{testsleep}" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.service {testname}:{zzz_service}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'grep nobody /etc/group || groupadd nobody'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} useradd somebody -g nobody -m" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chown somebody /tmp/.coverage" sx____(cmd.format(**locals())) # cmd = "docker commit -c 'CMD [\"/usr/bin/systemctl\"]' -c 'USER somebody' {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker rm -f {testname}" sh____(cmd.format(**locals())) cmd = "docker run -d --name {testname} {images}:{testname}" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} {systemctl} enable zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # cmd = "docker exec {testname} {systemctl} disable zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # cmd = "docker exec {testname} {systemctl} mask zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # cmd = "docker exec {testname} {systemctl} unmask zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertTrue(greps(err, "Unit zzz.service not for --user mode")) # cmd = "docker exec {testname} {systemctl} is-active zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 3) self.assertFalse(greps(err, "Unit zzz.service not for --user mode")) #TODO self.assertEqual(out.strip(), "unknown") # cmd = "docker exec {testname} {systemctl} is-failed zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertFalse(greps(err, "Unit zzz.service not for --user mode")) #TODO self.assertEqual(out.strip(), "unknown") # cmd = "docker exec {testname} {systemctl} is-enabled zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 1) self.assertFalse(greps(err, "Unit zzz.service not for --user mode")) #TODO self.assertEqual(out.strip(), "disabled") # cmd = "docker exec {testname} {systemctl} status zzz.service -vv" out, err, end = output3(cmd.format(**locals())) logg.info(" %s =>%s \n%s\n%s", cmd, end, err, out) self.assertEqual(end, 3) # # self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) # # # # # # def test_5430_systemctl_py_start_simple(self): """ check that we can start simple services in a container""" if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE and IMAGE or CENTOS) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") testname = self.testname() testdir = self.testdir() package = package_tool(image) refresh = refresh_tool(image) python = os.path.basename(_python) python_coverage = coverage_package(image) systemctl_py = _systemctl_py sometime = SOMETIME or 188 shell_file(os_path(testdir, "killall"),""" #! /bin/sh ps -eo pid,comm | { while read pid comm; do if [ "$comm" = "$1" ]; then echo kill $pid kill $pid fi done } """) text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=simple ExecStart=/usr/bin/testsleep 111 ExecStop=/usr/bin/killall testsleep [Install] WantedBy=multi-user.target""") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/killall || {package} install -y psmisc'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker exec {testname} mkdir -p /etc/systemd/system /etc/systemd/user" sx____(cmd.format(**locals())) cmd = "docker cp /usr/bin/sleep {testname}:/usr/bin/testsleep" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/killall {testname}:/usr/bin/killall" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.service {testname}:/etc/systemd/system/zzz.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzz.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl default-services -vv" # sh____(cmd.format(**locals())) out = output(cmd.format(**locals())) logg.info("\n>\n%s", out) self.assertTrue(greps(out, "zzz.service")) self.assertEqual(len(lines(out)), 1) # cmd = "docker exec {testname} systemctl start zzz.service -vv" sh____(cmd.format(**locals())) top_container = "docker exec {testname} ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, "testsleep")) # cmd = "docker exec {testname} systemctl stop zzz.service -vv" sh____(cmd.format(**locals())) top_container = "docker exec {testname} ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, "testsleep"))) # self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) self.rm_testdir() def test_5431_systemctl_py_start_extra_simple(self): """ check that we can start simple services in a container""" if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") testname = self.testname() testdir = self.testdir() package = package_tool(image) refresh = refresh_tool(image) python = os.path.basename(_python) python_coverage = coverage_package(image) systemctl_py = _systemctl_py sometime = SOMETIME or 188 text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=simple ExecStart=/usr/bin/testsleep 111 [Install] WantedBy=multi-user.target""") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/killall || {package} install -y psmisc'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker cp /usr/bin/sleep {testname}:/usr/bin/testsleep" sh____(cmd.format(**locals())) cmd = "docker exec {testname} mkdir -p /etc/systemd/system /etc/systemd/user" sx____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.service {testname}:/etc/systemd/system/zzz.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzz.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl default-services -vv" # sh____(cmd.format(**locals())) out = output(cmd.format(**locals())) logg.info("\n>\n%s", out) self.assertTrue(greps(out, "zzz.service")) self.assertEqual(len(lines(out)), 1) # cmd = "docker exec {testname} systemctl start zzz.service -vv" sh____(cmd.format(**locals())) top_container = "docker exec {testname} ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, "testsleep")) # cmd = "docker exec {testname} systemctl stop zzz.service -vv" sh____(cmd.format(**locals())) top_container = "docker exec {testname} ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, "testsleep"))) # # self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) self.rm_testdir() def test_5432_systemctl_py_start_forking(self): """ check that we can start forking services in a container w/ PIDFile""" if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") testname = self.testname() testdir = self.testdir() package = package_tool(image) refresh = refresh_tool(image) python = os.path.basename(_python) python_coverage = coverage_package(image) systemctl_py = _systemctl_py sometime = SOMETIME or 188 shell_file(os_path(testdir, "killall"),""" #! /bin/sh ps -eo pid,comm | { while read pid comm; do if [ "$comm" = "$1" ]; then echo kill $pid kill $pid fi done } """) shell_file(os_path(testdir, "zzz.init"), """ #! /bin/bash case "$1" in start) [ -d /var/run ] || mkdir -p /var/run (testsleep 111 0<&- &>/dev/null & echo $! > /var/run/zzz.init.pid ) & wait %1 ps -o pid,ppid,user,args ;; stop) killall testsleep ;; esac echo "done$1" >&2 exit 0""") text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=forking PIDFile=/var/run/zzz.init.pid ExecStart=/usr/bin/zzz.init start ExecStop=/usr/bin/zzz.init stop [Install] WantedBy=multi-user.target""") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker exec {testname} mkdir -p /etc/systemd/system /etc/systemd/user" sx____(cmd.format(**locals())) cmd = "docker cp /usr/bin/sleep {testname}:/usr/bin/testsleep" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/killall {testname}:/usr/bin/killall" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.service {testname}:/etc/systemd/system/zzz.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.init {testname}:/usr/bin/zzz.init" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzz.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl default-services -vv" # sh____(cmd.format(**locals())) out = output(cmd.format(**locals())) logg.info("\n>\n%s", out) self.assertTrue(greps(out, "zzz.service")) self.assertEqual(len(lines(out)), 1) # cmd = "docker exec {testname} systemctl start zzz.service -vv" sx____(cmd.format(**locals())) top_container = "docker exec {testname} ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, "testsleep")) # cmd = "docker exec {testname} systemctl stop zzz.service -vv" sh____(cmd.format(**locals())) top_container = "docker exec {testname} ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, "testsleep"))) # self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) self.rm_testdir() def test_5433_systemctl_py_start_forking_without_pid_file(self): """ check that we can start forking services in a container without PIDFile""" if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") testname = self.testname() testdir = self.testdir() package = package_tool(image) refresh = refresh_tool(image) python = os.path.basename(_python) python_coverage = coverage_package(image) systemctl_py = _systemctl_py sometime = SOMETIME or 188 shell_file(os_path(testdir, "killall"),""" #! /bin/sh ps -eo pid,comm | { while read pid comm; do if [ "$comm" = "$1" ]; then echo kill $pid kill $pid fi done } """) shell_file(os_path(testdir, "zzz.init"), """ #! /bin/bash case "$1" in start) (testsleep 111 0<&- &>/dev/null &) & wait %1 ps -o pid,ppid,user,args >&2 ;; stop) killall testsleep echo killed all testsleep >&2 sleep 1 ;; esac echo "done$1" >&2 exit 0""") text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=forking ExecStart=/usr/bin/zzz.init start ExecStop=/usr/bin/zzz.init stop [Install] WantedBy=multi-user.target""") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker exec {testname} mkdir -p /etc/systemd/system /etc/systemd/user" sx____(cmd.format(**locals())) cmd = "docker cp /usr/bin/sleep {testname}:/usr/bin/testsleep" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/killall {testname}:/usr/bin/killall" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.service {testname}:/etc/systemd/system/zzz.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.init {testname}:/usr/bin/zzz.init" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzz.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl default-services -vv" # sh____(cmd.format(**locals())) out = output(cmd.format(**locals())) logg.info("\n>\n%s", out) self.assertTrue(greps(out, "zzz.service")) self.assertEqual(len(lines(out)), 1) # cmd = "docker exec {testname} systemctl start zzz.service -vv" sx____(cmd.format(**locals())) top_container = "docker exec {testname} ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, "testsleep")) # cmd = "docker exec {testname} systemctl stop zzz.service -vv" sh____(cmd.format(**locals())) top_container = "docker exec {testname} ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, "testsleep"))) # self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) self.rm_testdir() def test_5435_systemctl_py_start_notify_by_timeout(self): """ check that we can start simple services in a container w/ notify timeout""" if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") testname = self.testname() testdir = self.testdir() package = package_tool(image) refresh = refresh_tool(image) python = os.path.basename(_python) python_coverage = coverage_package(image) systemctl_py = _systemctl_py sometime = SOMETIME or 188 shell_file(os_path(testdir, "killall"),""" #! /bin/sh ps -eo pid,comm | { while read pid comm; do if [ "$comm" = "$1" ]; then echo kill $pid kill $pid fi done } """) text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=notify ExecStart=/usr/bin/testsleep 111 ExecStop=/usr/bin/killall testsleep TimeoutSec=4 [Install] WantedBy=multi-user.target""") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker exec {testname} mkdir -p /etc/systemd/system /etc/systemd/user" sx____(cmd.format(**locals())) cmd = "docker cp /usr/bin/sleep {testname}:/usr/bin/testsleep" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/killall {testname}:/usr/bin/killall" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.service {testname}:/etc/systemd/system/zzz.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzz.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl default-services -vv" # sh____(cmd.format(**locals())) out = output(cmd.format(**locals())) logg.info("\n>\n%s", out) self.assertTrue(greps(out, "zzz.service")) self.assertEqual(len(lines(out)), 1) # cmd = "docker exec {testname} systemctl start zzz.service -vvvv" sx____(cmd.format(**locals())) # returncode = 1 top_container = "docker exec {testname} ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, "testsleep")) # cmd = "docker exec {testname} systemctl stop zzz.service -vv" sh____(cmd.format(**locals())) top_container = "docker exec {testname} ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) self.assertFalse(running(greps(top, "testsleep"))) # self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) self.rm_testdir() def test_5500_systemctl_py_run_default_services_in_container(self): """ check that we can enable services in a docker container to be run as default-services""" if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") testname = self.testname() testdir = self.testdir() package = package_tool(image) refresh = refresh_tool(image) python = os.path.basename(_python) python_coverage = coverage_package(image) systemctl_py = _systemctl_py sometime = SOMETIME or 188 text_file(os_path(testdir, "zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStart=/usr/bin/testsleep 99 [Install] WantedBy=multi-user.target""") text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C [Service] Type=simple ExecStart=/usr/bin/testsleep 111 [Install] WantedBy=multi-user.target""") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker exec {testname} mkdir -p /etc/systemd/system /etc/systemd/user" sx____(cmd.format(**locals())) cmd = "docker cp /usr/bin/sleep {testname}:/usr/bin/testsleep" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zza.service {testname}:/etc/systemd/system/zza.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzb.service {testname}:/etc/systemd/system/zzb.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzc.service {testname}:/etc/systemd/system/zzc.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzb.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzc.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl default-services -vv" # sh____(cmd.format(**locals())) out2 = output(cmd.format(**locals())) logg.info("\n>\n%s", out2) cmd = "docker exec {testname} systemctl default -vvvv" # sh____(cmd.format(**locals())) out3 = output(cmd.format(**locals())) logg.info("\n>\n%s", out3) # top_container = "docker exec {testname} ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, "testsleep 99")) self.assertTrue(greps(top, "testsleep 111")) # cmd = "docker exec {testname} systemctl halt -vvvv" # sh____(cmd.format(**locals())) out3 = output(cmd.format(**locals())) logg.info("\n>\n%s", out3) # top_container = "docker exec {testname} ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, "testsleep 99")) self.assertFalse(greps(top, "testsleep 111")) # self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) self.rm_testdir() def test_5520_systemctl_py_run_default_services_from_saved_container(self): """ check that we can enable services in a docker container to be run as default-services after it has been restarted from a commit-saved container image (with --init default)""" if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") testname = self.testname() testdir = self.testdir() package = package_tool(image) refresh = refresh_tool(image) python = os.path.basename(_python) python_coverage = coverage_package(image) systemctl_py = _systemctl_py images = IMAGES sometime = SOMETIME or 188 text_file(os_path(testdir, "zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStartPre=/bin/echo starting B ExecStart=/usr/bin/testsleep 99 [Install] WantedBy=multi-user.target""") text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C [Service] Type=simple ExecStartPre=/bin/echo starting C ExecStart=/usr/bin/testsleep 111 [Install] WantedBy=multi-user.target""") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker exec {testname} mkdir -p /etc/systemd/system /etc/systemd/user" sx____(cmd.format(**locals())) cmd = "docker cp /usr/bin/sleep {testname}:/usr/bin/testsleep" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zza.service {testname}:/etc/systemd/system/zza.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzb.service {testname}:/etc/systemd/system/zzb.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzc.service {testname}:/etc/systemd/system/zzc.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzb.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzc.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl default-services -v" # sh____(cmd.format(**locals())) out2 = output(cmd.format(**locals())) logg.info("\n>\n%s", out2) # cmd = "docker commit -c 'CMD [\"/usr/bin/systemctl\",\"--init\",\"default\",\"-vv\"]' {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker rm --force {testname}x" sx____(cmd.format(**locals())) cmd = "docker run --detach --name {testname}x {images}:{testname}" sh____(cmd.format(**locals())) time.sleep(3) # # top_container2 = "docker exec {testname}x ps -eo pid,ppid,user,args" top = output(top_container2.format(**locals())) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, "testsleep 99")) self.assertTrue(greps(top, "testsleep 111")) # cmd = "docker logs {testname}x" logs = output(cmd.format(**locals())) logg.info("------- docker logs\n>\n%s", logs) self.assertFalse(greps(logs, "starting B")) self.assertFalse(greps(logs, "starting C")) time.sleep(6) # INITLOOPS ticks at 5sec per default cmd = "docker logs {testname}x" logs = output(cmd.format(**locals())) logg.info("------- docker logs\n>\n%s", logs) self.assertTrue(greps(logs, "starting B")) self.assertTrue(greps(logs, "starting C")) # cmd = "docker exec {testname}x systemctl halt -vvvv" # sh____(cmd.format(**locals())) out3 = output(cmd.format(**locals())) logg.info("\n>\n%s", out3) # top_container = "docker exec {testname}x ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, "testsleep 99")) self.assertFalse(greps(top, "testsleep 111")) # cmd = "docker logs {testname}x" logs = output(cmd.format(**locals())) logg.info("------- docker logs\n>\n%s", logs) # self.save_coverage(testname, testname+"x") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rm --force {testname}x" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) self.rm_testdir() def test_5530_systemctl_py_run_default_services_from_simple_saved_container(self): """ check that we can enable services in a docker container to be run as default-services after it has been restarted from a commit-saved container image (without any arg)""" if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") testname = self.testname() testdir = self.testdir() package = package_tool(image) refresh = refresh_tool(image) python = os.path.basename(_python) python_coverage = coverage_package(image) systemctl_py = _systemctl_py images = IMAGES sometime = SOMETIME or 188 text_file(os_path(testdir, "zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStart=/usr/bin/testsleep 99 [Install] WantedBy=multi-user.target""") text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C [Service] Type=simple ExecStart=/usr/bin/testsleep 111 [Install] WantedBy=multi-user.target""") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker exec {testname} mkdir -p /etc/systemd/system /etc/systemd/user" sx____(cmd.format(**locals())) cmd = "docker cp /usr/bin/sleep {testname}:/usr/bin/testsleep" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zza.service {testname}:/etc/systemd/system/zza.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzb.service {testname}:/etc/systemd/system/zzb.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzc.service {testname}:/etc/systemd/system/zzc.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzb.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzc.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl default-services -v" # sh____(cmd.format(**locals())) out2 = output(cmd.format(**locals())) logg.info("\n>\n%s", out2) # cmd = "docker commit -c 'CMD [\"/usr/bin/systemctl\"]' {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker rm --force {testname}x" sx____(cmd.format(**locals())) cmd = "docker run --detach --name {testname}x {images}:{testname}" sh____(cmd.format(**locals())) time.sleep(3) # # top_container2 = "docker exec {testname}x ps -eo pid,ppid,user,args" top = output(top_container2.format(**locals())) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, "testsleep 99")) self.assertTrue(greps(top, "testsleep 111")) # cmd = "docker exec {testname} systemctl halt -vvvv" # sh____(cmd.format(**locals())) out3 = output(cmd.format(**locals())) logg.info("\n>\n%s", out3) # top_container = "docker exec {testname} ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, "testsleep 99")) self.assertFalse(greps(top, "testsleep 111")) # # self.save_coverage(testname, testname+"x") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rm --force {testname}x" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) self.rm_testdir() def test_5533_systemctl_py_run_default_services_from_single_service_saved_container(self): """ check that we can enable services in a docker container to be run as default-services after it has been restarted from a commit-saved container image""" if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") testname = self.testname() testdir = self.testdir() package = package_tool(image) refresh = refresh_tool(image) python = os.path.basename(_python) python_coverage = coverage_package(image) systemctl_py = _systemctl_py sometime = SOMETIME or 188 text_file(os_path(testdir, "zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStart=/usr/bin/testsleep 99 [Install] WantedBy=multi-user.target""") text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C [Service] Type=simple ExecStart=/usr/bin/testsleep 111 [Install] WantedBy=multi-user.target""") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker exec {testname} mkdir -p /etc/systemd/system /etc/systemd/user" sx____(cmd.format(**locals())) cmd = "docker cp /usr/bin/sleep {testname}:/usr/bin/testsleep" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zza.service {testname}:/etc/systemd/system/zza.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzb.service {testname}:/etc/systemd/system/zzb.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzc.service {testname}:/etc/systemd/system/zzc.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzb.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzc.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl default-services -v" # sh____(cmd.format(**locals())) out2 = output(cmd.format(**locals())) logg.info("\n>\n%s", out2) # .........................................vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv cmd = "docker commit -c 'CMD [\"/usr/bin/systemctl\",\"init\",\"zzc.service\"]' {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker rm --force {testname}x" sx____(cmd.format(**locals())) cmd = "docker run --detach --name {testname}x {images}:{testname}" sh____(cmd.format(**locals())) time.sleep(3) # # top_container2 = "docker exec {testname}x ps -eo pid,ppid,user,args" top = output(top_container2.format(**locals())) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, "testsleep 99")) # <<<<<<<<<< difference to 5033 self.assertTrue(greps(top, "testsleep 111")) # cmd = "docker stop {testname}x" # <<< # sh____(cmd.format(**locals())) out3 = output(cmd.format(**locals())) logg.info("\n>\n%s", out3) # top_container = "docker exec {testname} ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, "testsleep 99")) self.assertFalse(greps(top, "testsleep 111")) # # self.save_coverage(testname, testname+"x") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rm --force {testname}x" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) self.rm_testdir() def test_5600_systemctl_py_list_units_running(self): """ check that we can enable services in a docker container to be run as default-services after it has been restarted and that we can filter the list of services shown""" if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") testname = self.testname() testdir = self.testdir() package = package_tool(image) refresh = refresh_tool(image) python = os.path.basename(_python) python_coverage = coverage_package(image) systemctl_py = _systemctl_py sometime = SOMETIME or 188 text_file(os_path(testdir, "zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStart=/usr/bin/testsleep 99 [Install] WantedBy=multi-user.target""") text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C [Service] Type=simple ExecStart=/usr/bin/testsleep 111 [Install] WantedBy=multi-user.target""") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sx____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker exec {testname} mkdir -p /etc/systemd/system /etc/systemd/user" sx____(cmd.format(**locals())) cmd = "docker cp /usr/bin/sleep {testname}:/usr/bin/testsleep" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zza.service {testname}:/etc/systemd/system/zza.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzb.service {testname}:/etc/systemd/system/zzb.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzc.service {testname}:/etc/systemd/system/zzc.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzb.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzc.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl default-services -v" # sh____(cmd.format(**locals())) out2 = output(cmd.format(**locals())) logg.info("\n>\n%s", out2) cmd = "docker exec {testname} bash -c 'grep nobody /etc/group || groupadd nobody'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} useradd somebody -g nobody -m" sh____(cmd.format(**locals())) # .........................................vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv cmd = "docker commit -c 'CMD [\"/usr/bin/systemctl\"]' {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker rm --force {testname}x" sx____(cmd.format(**locals())) cmd = "docker run --detach --name {testname}x {images}:{testname}" sh____(cmd.format(**locals())) time.sleep(3) # # top_container2 = "docker exec -u somebody {testname}x ps -eo pid,ppid,user,args" top = output(top_container2.format(**locals())) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, "testsleep 99")) self.assertTrue(greps(top, "testsleep 111")) self.assertEqual(len(greps(top, "testsleep")), 2) self.assertEqual(len(greps(top, " 1 *.*systemctl")), 1) self.assertEqual(len(greps(top, " root ")), 3) self.assertEqual(len(greps(top, " somebody ")), 1) # check = "docker exec {testname}x bash -c 'ls -ld /var/run/*.status; grep PID /var/run/*.status'" top = output(check.format(**locals())) logg.info("\n>>>\n%s", top) check = "docker exec {testname}x systemctl list-units" top = output(check.format(**locals())) logg.info("\n>>>\n%s", top) self.assertEqual(len(greps(top, "zz")), 3) check = "docker exec {testname}x systemctl list-units --state=running" top = output(check.format(**locals())) logg.info("\n>>>\n%s", top) self.assertEqual(len(greps(top, "zz")), 2) # cmd = "docker stop {testname}x" # <<< # sh____(cmd.format(**locals())) out3 = output(cmd.format(**locals())) logg.info("\n>\n%s", out3) # self.save_coverage(testname, testname+"x") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rm --force {testname}x" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) self.rm_testdir() def test_6130_run_default_services_from_simple_saved_container(self): """ check that we can enable services in a docker container to be run as default-services after it has been restarted from a commit-saved container image. This includes some corage on the init-services.""" if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) testname = self.testname() testdir = self.testdir() python = os.path.basename(_python) python_coverage = coverage_package(image) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) cov_option = "--system" if COVERAGE: cov_option = "--coverage=spawn,oldest" sometime = SOMETIME or 188 text_file(os_path(testdir, "zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStart=/usr/bin/testsleep 99 [Install] WantedBy=multi-user.target""") text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C [Service] Type=simple ExecStart=/usr/bin/testsleep 111 [Install] WantedBy=multi-user.target""") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker cp /usr/bin/sleep {testname}:/usr/bin/testsleep" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sh____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} mkdir -p /etc/systemd/system" sx____(cmd.format(**locals())) cmd = "docker cp {testdir}/zza.service {testname}:/etc/systemd/system/zza.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzb.service {testname}:/etc/systemd/system/zzb.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzc.service {testname}:/etc/systemd/system/zzc.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzb.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzc.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl default-services -v" # sh____(cmd.format(**locals())) out2 = output(cmd.format(**locals())) logg.info("\n>\n%s", out2) # cmd = "docker commit -c 'CMD [\"/usr/bin/systemctl\",\"{cov_option}\"]' {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker rm --force {testname}x" sx____(cmd.format(**locals())) cmd = "docker run --detach --name {testname}x {images}:{testname}" sh____(cmd.format(**locals())) time.sleep(3) # top_container2 = "docker exec {testname}x ps -eo pid,ppid,user,args" top = output(top_container2.format(**locals())) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, "testsleep 99")) self.assertTrue(greps(top, "testsleep 111")) # cmd = "docker exec {testname} systemctl halt -vvvv" # sh____(cmd.format(**locals())) out3 = output(cmd.format(**locals())) logg.info("\n>\n%s", out3) # top_container = "docker exec {testname} ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, "testsleep 99")) self.assertFalse(greps(top, "testsleep 111")) # self.save_coverage(testname, testname+"x") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rm --force {testname}x" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) self.rm_testdir() def test_6133_run_default_services_from_single_service_saved_container(self): """ check that we can enable services in a docker container to be run as default-services after it has been restarted from a commit-saved container image. This includes some corage on the init-services.""" if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) testname = self.testname() testdir = self.testdir() python = os.path.basename(_python) python_coverage = coverage_package(image) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) cov_option = "--system" if COVERAGE: cov_option = "--coverage=spawn,oldest" sometime = SOMETIME or 188 text_file(os_path(testdir, "zza.service"),""" [Unit] Description=Testing A""") text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStart=/usr/bin/testsleep 99 [Install] WantedBy=multi-user.target""") text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C [Service] Type=simple ExecStart=/usr/bin/testsleep 111 [Install] WantedBy=multi-user.target""") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker cp /usr/bin/sleep {testname}:/usr/bin/testsleep" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sh____(cmd.format(**locals())) self.prep_coverage(testname) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} mkdir -p /etc/systemd/system" sx____(cmd.format(**locals())) cmd = "docker cp {testdir}/zza.service {testname}:/etc/systemd/system/zza.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzb.service {testname}:/etc/systemd/system/zzb.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzc.service {testname}:/etc/systemd/system/zzc.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzb.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzc.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl default-services -v" # sh____(cmd.format(**locals())) out2 = output(cmd.format(**locals())) logg.info("\n>\n%s", out2) # .........................................vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv cmd = "docker commit -c 'CMD [\"/usr/bin/systemctl\",\"init\",\"zzc.service\",\"{cov_option}\"]' {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker rm --force {testname}x" sx____(cmd.format(**locals())) cmd = "docker run --detach --name {testname}x {images}:{testname}" sh____(cmd.format(**locals())) time.sleep(3) # # top_container2 = "docker exec {testname}x ps -eo pid,ppid,user,args" top = output(top_container2.format(**locals())) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, "testsleep 99")) # <<<<<<<<<< difference to 5033 self.assertTrue(greps(top, "testsleep 111")) # cmd = "docker stop {testname}x" # <<< # sh____(cmd.format(**locals())) out3 = output(cmd.format(**locals())) logg.info("\n>\n%s", out3) # top_container = "docker exec {testname} ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, "testsleep 99")) self.assertFalse(greps(top, "testsleep 111")) # self.save_coverage(testname, testname+"x") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rm --force {testname}x" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) self.rm_testdir() def test_6160_systemctl_py_init_default_halt_to_exit_container(self): """ check that we can 'halt' in a docker container to stop the service and to exit the PID 1 as the last part of the service.""" if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) testname = self.testname() testdir = self.testdir() python = os.path.basename(_python) python_coverage = coverage_package(image) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) cov_option = "--system" if COVERAGE: cov_option = "--coverage=spawn,oldest" sometime = SOMETIME or 188 text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStart=/usr/bin/testsleep 99 [Install] WantedBy=multi-user.target""") text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C [Service] Type=simple ExecStart=/usr/bin/testsleep 111 [Install] WantedBy=multi-user.target""") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker cp /usr/bin/sleep {testname}:/usr/bin/testsleep" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sh____(cmd.format(**locals())) self.prep_coverage(testname, cov_option) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch /var/log/systemctl.debug.log" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} mkdir -p /etc/systemd/system" sx____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzb.service {testname}:/etc/systemd/system/zzb.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzc.service {testname}:/etc/systemd/system/zzc.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzb.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzc.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl default-services -v" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch /var/log/systemctl.debug.log {testdir}/systemctl.debug.log" # sh____(cmd.format(**locals())) out2 = output(cmd.format(**locals())) logg.info("\n>\n%s", out2) cmd = "docker commit -c 'CMD [\"/usr/bin/systemctl\",\"init\",\"{cov_option}\"]' {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker rm --force {testname}x" sx____(cmd.format(**locals())) cmd = "docker run --detach --name {testname}x {images}:{testname}" sh____(cmd.format(**locals())) time.sleep(2) # top_container2 = "docker exec {testname}x ps -eo pid,ppid,user,args" top = output(top_container2.format(**locals())) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, "testsleep 111")) # # vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv status check now cmd = "docker inspect {testname}x" inspected = output(cmd.format(**locals())) state = json.loads(inspected)[0]["State"] logg.info("Status = %s", state["Status"]) self.assertTrue(state["Running"]) self.assertEqual(state["Status"], "running") # cmd = "docker exec {testname}x systemctl halt" sh____(cmd.format(**locals())) # waits = 3 for attempt in xrange(5): logg.info("[%s] waits %ss for the zombie-reaper to have cleaned up", attempt, waits) time.sleep(waits) cmd = "docker inspect {testname}x" inspected = output(cmd.format(**locals())) state = json.loads(inspected)[0]["State"] logg.info("Status = %s", state["Status"]) logg.info("ExitCode = %s", state["ExitCode"]) if state["Status"] in ["exited"]: break top_container = "docker exec {testname}x ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) cmd = "docker cp {testname}x:/var/log/systemctl.debug.log {testdir}/systemctl.debug.log" sh____(cmd.format(**locals())) log = lines(open(testdir+"/systemctl.debug.log")) logg.info("systemctl.debug.log>\n\t%s", "\n\t".join(log)) # self.assertFalse(state["Running"]) self.assertEqual(state["Status"], "exited") # cmd = "docker stop {testname}x" # <<< this is a no-op now # sh____(cmd.format(**locals())) out3 = output(cmd.format(**locals())) logg.info("\n>\n%s", out3) # cmd = "docker cp {testname}x:/var/log/systemctl.debug.log {testdir}/systemctl.debug.log" sh____(cmd.format(**locals())) log = lines(open(testdir+"/systemctl.debug.log")) logg.info("systemctl.debug.log>\n\t%s", "\n\t".join(log)) self.assertTrue(greps(log, "no more procs - exit init-loop")) # top_container = "docker exec {testname}x ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, "testsleep 99")) self.assertFalse(greps(top, "testsleep 111")) # self.save_coverage(testname, testname+"x") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rm --force {testname}x" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) self.rm_testdir() def test_6170_systemctl_py_init_all_stop_last_service_to_exit_container(self): """ check that we can 'stop ' in a docker container to stop the service being the last service and to exit the PID 1 as the last part of the service.""" if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) testname = self.testname() testdir = self.testdir() python = os.path.basename(_python) python_coverage = coverage_package(image) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) cov_option = "--system" if COVERAGE: cov_option = "--coverage=spawn,oldest" sometime = SOMETIME or 188 text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStart=/usr/bin/testsleep 99 [Install] WantedBy=multi-user.target""") text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C [Service] Type=simple ExecStart=/usr/bin/testsleep 111 [Install] WantedBy=multi-user.target""") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker cp /usr/bin/sleep {testname}:/usr/bin/testsleep" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sh____(cmd.format(**locals())) self.prep_coverage(testname, cov_option) cmd = "docker exec {testname} bash -c 'test -f /etc/init.d/ondemand && systemctl disable ondemand'" # ubuntu:16.04 sx____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch /var/log/systemctl.debug.log" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} mkdir -p /etc/systemd/system" sx____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzb.service {testname}:/etc/systemd/system/zzb.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzc.service {testname}:/etc/systemd/system/zzc.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzb.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzc.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl default-services -v" # sh____(cmd.format(**locals())) out2 = output(cmd.format(**locals())) logg.info("\n>\n%s", out2) cmd = "docker commit -c 'CMD [\"/usr/bin/systemctl\",\"init\",\"--all\",\"{cov_option}\"]' {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker rm --force {testname}x" sx____(cmd.format(**locals())) cmd = "docker run --detach --name {testname}x {images}:{testname}" sh____(cmd.format(**locals())) time.sleep(2) # top_container2 = "docker exec {testname}x ps -eo pid,ppid,user,args" top = output(top_container2.format(**locals())) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, "testsleep 111")) # cmd = "docker inspect {testname}x" inspected = output(cmd.format(**locals())) state = json.loads(inspected)[0]["State"] logg.info("Status = %s", state["Status"]) self.assertTrue(state["Running"]) self.assertEqual(state["Status"], "running") # cmd = "docker exec {testname}x systemctl stop zzb.service" # <<<<<<<<<<<<<<<<<<<<< sh____(cmd.format(**locals())) cmd = "docker exec {testname}x systemctl stop zzc.service" # <<<<<<<<<<<<<<<<<<<<< sh____(cmd.format(**locals())) # waits = 3 for attempt in xrange(5): logg.info("[%s] waits %ss for the zombie-reaper to have cleaned up", attempt, waits) time.sleep(waits) cmd = "docker inspect {testname}x" inspected = output(cmd.format(**locals())) state = json.loads(inspected)[0]["State"] logg.info("Status = %s", state["Status"]) logg.info("ExitCode = %s", state["ExitCode"]) if state["Status"] in ["exited"]: break top_container = "docker exec {testname}x ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) cmd = "docker cp {testname}x:/var/log/systemctl.debug.log {testdir}/systemctl.debug.log" sh____(cmd.format(**locals())) log = lines(open(testdir+"/systemctl.debug.log")) logg.info("systemctl.debug.log>\n\t%s", "\n\t".join(log)) # self.assertFalse(state["Running"]) self.assertEqual(state["Status"], "exited") # cmd = "docker stop {testname}x" # <<< this is a no-op now # sh____(cmd.format(**locals())) out3 = output(cmd.format(**locals())) logg.info("\n>\n%s", out3) # cmd = "docker logs {testname}x" logs = output(cmd.format(**locals())) logg.info("\n>\n%s", logs) # cmd = "docker cp {testname}x:/var/log/systemctl.debug.log {testdir}/systemctl.debug.log" sh____(cmd.format(**locals())) log = lines(open(testdir+"/systemctl.debug.log")) logg.info("systemctl.debug.log>\n\t%s", "\n\t".join(log)) self.assertTrue(greps(log, "no more procs - exit init-loop")) # top_container = "docker exec {testname}x ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, "testsleep 99")) self.assertFalse(greps(top, "testsleep 111")) # self.save_coverage(testname, testname+"x") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rm --force {testname}x" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) self.rm_testdir() def test_6180_systemctl_py_init_explicit_halt_to_exit_container(self): """ check that we can 'halt' in a docker container to stop the service and to exit the PID 1 as the last part of the service.""" if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) testname = self.testname() testdir = self.testdir() python = os.path.basename(_python) python_coverage = coverage_package(image) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) cov_option = "--system" if COVERAGE: cov_option = "--coverage=spawn,oldest" sometime = SOMETIME or 188 text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStart=/usr/bin/testsleep 99 [Install] WantedBy=multi-user.target""") text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C [Service] Type=simple ExecStart=/usr/bin/testsleep 111 [Install] WantedBy=multi-user.target""") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker cp /usr/bin/sleep {testname}:/usr/bin/testsleep" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sh____(cmd.format(**locals())) self.prep_coverage(testname, cov_option) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch /var/log/systemctl.debug.log" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} mkdir -p /etc/systemd/system" sx____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzb.service {testname}:/etc/systemd/system/zzb.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzc.service {testname}:/etc/systemd/system/zzc.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzb.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzc.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl default-services -v" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch /var/log/systemctl.debug.log {testdir}/systemctl.debug.log" # sh____(cmd.format(**locals())) out2 = output(cmd.format(**locals())) logg.info("\n>\n%s", out2) cmd = "docker commit -c 'CMD [\"/usr/bin/systemctl\",\"init\",\"zzc.service\",\"{cov_option}\"]' {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker rm --force {testname}x" sx____(cmd.format(**locals())) cmd = "docker run --detach --name {testname}x {images}:{testname}" sh____(cmd.format(**locals())) time.sleep(2) # top_container2 = "docker exec {testname}x ps -eo pid,ppid,user,args" top = output(top_container2.format(**locals())) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, "testsleep 111")) # # vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv status check now cmd = "docker inspect {testname}x" inspected = output(cmd.format(**locals())) state = json.loads(inspected)[0]["State"] logg.info("Status = %s", state["Status"]) self.assertTrue(state["Running"]) self.assertEqual(state["Status"], "running") # cmd = "docker exec {testname}x systemctl halt" sh____(cmd.format(**locals())) # waits = 3 for attempt in xrange(10): logg.info("[%s] waits %ss for the zombie-reaper to have cleaned up", attempt, waits) time.sleep(waits) cmd = "docker inspect {testname}x" inspected = output(cmd.format(**locals())) state = json.loads(inspected)[0]["State"] logg.info("Status = %s", state["Status"]) logg.info("ExitCode = %s", state["ExitCode"]) if state["Status"] in ["exited"]: break top_container = "docker exec {testname}x ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) self.assertFalse(state["Running"]) self.assertEqual(state["Status"], "exited") # cmd = "docker stop {testname}x" # <<< this is a no-op now # sh____(cmd.format(**locals())) out3 = output(cmd.format(**locals())) logg.info("\n>\n%s", out3) # cmd = "docker cp {testname}x:/var/log/systemctl.debug.log {testdir}/systemctl.debug.log" sh____(cmd.format(**locals())) log = lines(open(testdir+"/systemctl.debug.log")) logg.info("systemctl.debug.log>\n\t%s", "\n\t".join(log)) self.assertTrue(greps(log, "no more services - exit init-loop")) # top_container = "docker exec {testname}x ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, "testsleep 99")) self.assertFalse(greps(top, "testsleep 111")) # self.save_coverage(testname, testname+"x") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rm --force {testname}x" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) self.rm_testdir() def test_6190_systemctl_py_init_explicit_stop_last_service_to_exit_container(self): """ check that we can 'stop ' in a docker container to stop the service being the last service and to exit the PID 1 as the last part of the service.""" if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) testname = self.testname() testdir = self.testdir() python = os.path.basename(_python) python_coverage = coverage_package(image) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) cov_option = "--system" if COVERAGE: cov_option = "--coverage=spawn,oldest" sometime = SOMETIME or 188 text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple ExecStart=/usr/bin/testsleep 99 [Install] WantedBy=multi-user.target""") text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C [Service] Type=simple ExecStart=/usr/bin/testsleep 111 [Install] WantedBy=multi-user.target""") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker cp /usr/bin/sleep {testname}:/usr/bin/testsleep" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sh____(cmd.format(**locals())) self.prep_coverage(testname, cov_option) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch /var/log/systemctl.debug.log" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} mkdir -p /etc/systemd/system" sx____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzb.service {testname}:/etc/systemd/system/zzb.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzc.service {testname}:/etc/systemd/system/zzc.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzb.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzc.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl default-services -v" # sh____(cmd.format(**locals())) out2 = output(cmd.format(**locals())) logg.info("\n>\n%s", out2) cmd = "docker commit -c 'CMD [\"/usr/bin/systemctl\",\"init\",\"zzc.service\",\"{cov_option}\"]' {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker rm --force {testname}x" sx____(cmd.format(**locals())) cmd = "docker run --detach --name {testname}x {images}:{testname}" sh____(cmd.format(**locals())) time.sleep(2) # top_container2 = "docker exec {testname}x ps -eo pid,ppid,user,args" top = output(top_container2.format(**locals())) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, "testsleep 111")) # cmd = "docker inspect {testname}x" inspected = output(cmd.format(**locals())) state = json.loads(inspected)[0]["State"] logg.info("Status = %s", state["Status"]) self.assertTrue(state["Running"]) self.assertEqual(state["Status"], "running") # cmd = "docker exec {testname}x systemctl stop zzc.service" # <<<<<<<<<<<<<<<<<<<<< sh____(cmd.format(**locals())) # waits = 3 for attempt in xrange(10): logg.info("[%s] waits %ss for the zombie-reaper to have cleaned up", attempt, waits) time.sleep(waits) cmd = "docker inspect {testname}x" inspected = output(cmd.format(**locals())) state = json.loads(inspected)[0]["State"] logg.info("Status = %s", state["Status"]) logg.info("ExitCode = %s", state["ExitCode"]) if state["Status"] in ["exited"]: break top_container = "docker exec {testname}x ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) self.assertFalse(state["Running"]) self.assertEqual(state["Status"], "exited") # cmd = "docker stop {testname}x" # <<< this is a no-op now # sh____(cmd.format(**locals())) out3 = output(cmd.format(**locals())) logg.info("\n>\n%s", out3) # cmd = "docker logs {testname}x" logs = output(cmd.format(**locals())) logg.info("\n>\n%s", logs) # cmd = "docker cp {testname}x:/var/log/systemctl.debug.log {testdir}/systemctl.debug.log" sh____(cmd.format(**locals())) log = lines(open(testdir+"/systemctl.debug.log")) logg.info("systemctl.debug.log>\n\t%s", "\n\t".join(log)) self.assertTrue(greps(log, "no more services - exit init-loop")) # top_container = "docker exec {testname}x ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, "testsleep 99")) self.assertFalse(greps(top, "testsleep 111")) # self.save_coverage(testname, testname+"x") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rm --force {testname}x" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) self.rm_testdir() def test_6200_systemctl_py_switch_users_is_possible(self): """ check that we can put setuid/setgid definitions in a service specfile which also works on the pid file itself """ if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) testname = self.testname() testdir = self.testdir() python = os.path.basename(_python) python_coverage = coverage_package(image) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) cov_option = "--system" if COVERAGE: cov_option = "--coverage=spawn,oldest" sometime = SOMETIME or 288 text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple User=user1 Group=root ExecStart=/usr/bin/testsleep 99 [Install] WantedBy=multi-user.target""") text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C [Service] Type=simple User=user1 ExecStart=/usr/bin/testsleep 111 [Install] WantedBy=multi-user.target""") text_file(os_path(testdir, "zzd.service"),""" [Unit] Description=Testing D [Service] Type=simple Group=group2 ExecStart=/usr/bin/testsleep 122 [Install] WantedBy=multi-user.target""") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker cp /usr/bin/sleep {testname}:/usr/bin/testsleep" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sh____(cmd.format(**locals())) self.prep_coverage(testname, cov_option) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) # if COVERAGE: cmd = "docker exec {testname} touch /tmp/.coverage" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chmod 777 /tmp/.coverage" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} groupadd group2" sh____(cmd.format(**locals())) cmd = "docker exec {testname} useradd user1 -g group2" sh____(cmd.format(**locals())) cmd = "docker exec {testname} mkdir -p /etc/systemd/system" sx____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzb.service {testname}:/etc/systemd/system/zzb.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzc.service {testname}:/etc/systemd/system/zzc.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzd.service {testname}:/etc/systemd/system/zzd.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl start zzb.service -v" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl start zzc.service -v" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl start zzd.service -v" sh____(cmd.format(**locals())) # # first of all, it starts commands like the service specs without user/group top_container = "docker exec {testname} ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, "testsleep 99")) self.assertTrue(greps(top, "testsleep 111")) # but really it has some user/group changed top_container = "docker exec {testname} ps -eo user,group,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, "user1 .*root .*testsleep 99")) self.assertTrue(greps(top, "user1 .*group2 .*testsleep 111")) self.assertTrue(greps(top, "root .*group2 .*testsleep 122")) # and the pid file has changed as well cmd = "docker exec {testname} ls -l /var/run/zzb.service.pid" out = output(cmd.format(**locals())) logg.info("found %s", out.strip()) if TODO: self.assertTrue(greps(out, "user1 .*root .*zzb.service.pid")) cmd = "docker exec {testname} ls -l /var/run/zzc.service.pid" out = output(cmd.format(**locals())) logg.info("found %s", out.strip()) if TODO: self.assertTrue(greps(out, "user1 .*group2 .*zzc.service.pid")) cmd = "docker exec {testname} ls -l /var/run/zzd.service.pid" out = output(cmd.format(**locals())) logg.info("found %s", out.strip()) if TODO: self.assertTrue(greps(out, "root .*group2 .*zzd.service.pid")) # self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) self.rm_testdir() def test_6201_systemctl_py_switch_users_is_possible_from_saved_container(self): """ check that we can put setuid/setgid definitions in a service specfile which also works on the pid file itself """ if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) testname = self.testname() testdir = self.testdir() python = os.path.basename(_python) python_coverage = coverage_package(image) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) cov_option = "--system" if COVERAGE: cov_option = "--coverage=spawn" sometime = SOMETIME or 188 text_file(os_path(testdir, "zzb.service"),""" [Unit] Description=Testing B [Service] Type=simple User=user1 Group=root ExecStart=/usr/bin/testsleep 99 [Install] WantedBy=multi-user.target""") text_file(os_path(testdir, "zzc.service"),""" [Unit] Description=Testing C [Service] Type=simple User=user1 ExecStart=/usr/bin/testsleep 111 [Install] WantedBy=multi-user.target""") text_file(os_path(testdir, "zzd.service"),""" [Unit] Description=Testing D [Service] Type=simple Group=group2 ExecStart=/usr/bin/testsleep 122 [Install] WantedBy=multi-user.target""") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker cp /usr/bin/sleep {testname}:/usr/bin/testsleep" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sh____(cmd.format(**locals())) self.prep_coverage(testname, cov_option) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} groupadd group2" sh____(cmd.format(**locals())) cmd = "docker exec {testname} useradd user1 -g group2" sh____(cmd.format(**locals())) cmd = "docker exec {testname} mkdir -p /etc/systemd/system" sx____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzb.service {testname}:/etc/systemd/system/zzb.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzc.service {testname}:/etc/systemd/system/zzc.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzd.service {testname}:/etc/systemd/system/zzd.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzb.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzc.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzd.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl default-services -v" # sh____(cmd.format(**locals())) out2 = output(cmd.format(**locals())) logg.info("\n>\n%s", out2) # .........................................vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv cmd = "docker commit -c 'CMD [\"/usr/bin/systemctl\",\"{cov_option}\"]' {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker rm --force {testname}x" sx____(cmd.format(**locals())) cmd = "docker run --detach --name {testname}x {images}:{testname}" sh____(cmd.format(**locals())) time.sleep(5) # # first of all, it starts commands like the service specs without user/group top_container2 = "docker exec {testname}x ps -eo pid,ppid,user,args" top = output(top_container2.format(**locals())) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, "testsleep 99")) self.assertTrue(greps(top, "testsleep 111")) self.assertTrue(greps(top, "testsleep 122")) # but really it has some user/group changed top_container2 = "docker exec {testname}x ps -eo user,group,args" top = output(top_container2.format(**locals())) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, "user1 .*root .*testsleep 99")) self.assertTrue(greps(top, "user1 .*group2 .*testsleep 111")) self.assertTrue(greps(top, "root .*group2 .*testsleep 122")) # and the pid file has changed as well cmd = "docker exec {testname}x ls -l /var/run/zzb.service.pid" out = output(cmd.format(**locals())) logg.info("found %s", out.strip()) if TODO: self.assertTrue(greps(out, "user1 .*root .*zzb.service.pid")) cmd = "docker exec {testname}x ls -l /var/run/zzc.service.pid" out = output(cmd.format(**locals())) logg.info("found %s", out.strip()) if TODO: self.assertTrue(greps(out, "user1 .*group2 .*zzc.service.pid")) cmd = "docker exec {testname}x ls -l /var/run/zzd.service.pid" out = output(cmd.format(**locals())) logg.info("found %s", out.strip()) if TODO: self.assertTrue(greps(out, "root .*group2 .*zzd.service.pid")) # cmd = "docker stop {testname}x" # <<< out3 = output(cmd.format(**locals())) logg.info("\n>\n%s", out3) # top_container = "docker exec {testname} ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, "testsleep 99")) self.assertFalse(greps(top, "testsleep 111")) self.assertFalse(greps(top, "testsleep 122")) # self.save_coverage(testname, testname+"x") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rm --force {testname}x" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) self.rm_testdir() def test_6210_switch_users_and_workingdir_coverage(self): """ check that we can put workingdir and setuid/setgid definitions in a service and code parts for that are actually executed (test case without fork before) """ if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) testname = self.testname() testdir = self.testdir() python = os.path.basename(_python) python_coverage = coverage_package(image) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) testsleep_sh = os_path(testdir, "testsleep.sh") cov_option = "--system" if COVERAGE: cov_option = "--coverage=spawn" sometime = SOMETIME or 188 shell_file(testsleep_sh,""" #! /bin/sh logfile="/tmp/testsleep-$1.log" date > $logfile echo "pwd": `pwd` >> $logfile echo "user:" `id -un` >> $logfile echo "group:" `id -gn` >> $logfile testsleep $1 """.format(**locals())) text_file(os_path(testdir, "zz4.service"),""" [Unit] Description=Testing 4 [Service] Type=simple User=user1 Group=root WorkingDirectory=/srv ExecStart=/usr/bin/testsleep.sh 4 [Install] WantedBy=multi-user.target""") text_file(os_path(testdir, "zz5.service"),""" [Unit] Description=Testing 5 [Service] Type=simple User=user1 WorkingDirectory=/srv ExecStart=/usr/bin/testsleep.sh 5 [Install] WantedBy=multi-user.target""") text_file(os_path(testdir, "zz6.service"),""" [Unit] Description=Testing 6 [Service] Type=simple Group=group2 WorkingDirectory=/srv ExecStart=/usr/bin/testsleep.sh 6 [Install] WantedBy=multi-user.target""") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker cp /usr/bin/sleep {testname}:/usr/bin/testsleep" sh____(cmd.format(**locals())) cmd = "docker cp {testsleep_sh} {testname}:/usr/bin/testsleep.sh" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chmod 755 /usr/bin/testsleep.sh" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sh____(cmd.format(**locals())) self.prep_coverage(testname, cov_option) cmd = "docker exec {testname} groupadd group2" sh____(cmd.format(**locals())) cmd = "docker exec {testname} useradd user1 -g group2" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) # if COVERAGE: cmd = "docker exec {testname} touch /tmp/.coverage" sh____(cmd.format(**locals())) cmd = "docker exec {testname} chmod 777 /tmp/.coverage" ## << switched user may write sh____(cmd.format(**locals())) # cmd = "docker exec {testname} mkdir -p /etc/systemd/system" sx____(cmd.format(**locals())) cmd = "docker cp {testdir}/zz4.service {testname}:/etc/systemd/system/zz4.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zz5.service {testname}:/etc/systemd/system/zz5.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zz6.service {testname}:/etc/systemd/system/zz6.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl __test_start_unit zz4.service -vvvv {cov_option}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl __test_start_unit zz5.service -vv {cov_option}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl __test_start_unit zz6.service -vv {cov_option}" sh____(cmd.format(**locals())) # cmd = "docker cp {testname}:/tmp/testsleep-4.log {testdir}/" sh____(cmd.format(**locals())) cmd = "docker cp {testname}:/tmp/testsleep-5.log {testdir}/" sh____(cmd.format(**locals())) cmd = "docker cp {testname}:/tmp/testsleep-6.log {testdir}/" sh____(cmd.format(**locals())) log4 = lines(open(os_path(testdir, "testsleep-4.log"))) log5 = lines(open(os_path(testdir, "testsleep-5.log"))) log6 = lines(open(os_path(testdir, "testsleep-6.log"))) logg.info("testsleep-4.log\n %s", "\n ".join(log4)) logg.info("testsleep-5.log\n %s", "\n ".join(log5)) logg.info("testsleep-6.log\n %s", "\n ".join(log6)) self.assertTrue(greps(log4, "pwd: /srv")) self.assertTrue(greps(log5, "pwd: /srv")) self.assertTrue(greps(log6, "pwd: /srv")) self.assertTrue(greps(log4, "group: root")) self.assertTrue(greps(log4, "user: user1")) self.assertTrue(greps(log5, "user: user1")) self.assertTrue(greps(log5, "group: group2")) self.assertTrue(greps(log6, "group: group2")) self.assertTrue(greps(log6, "user: root")) # self.save_coverage(testname) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) self.rm_testdir() def test_6600_systemctl_py_can_reap_zombies_in_a_container(self): """ check that we can reap zombies in a container managed by systemctl.py""" if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(COVERAGE or IMAGE or CENTOS) testname = self.testname() testdir = self.testdir() python = os.path.basename(_python) python_coverage = coverage_package(image) cov_option = "--system" if COVERAGE: cov_option = "--coverage=spawn" if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) sometime = SOMETIME or 188 user = self.user() testsleep = self.testname("sleep") shell_file(os_path(testdir, "zzz.init"), """ #! /bin/bash case "$1" in start) (/usr/bin/{testsleep} 111 0<&- &>/dev/null &) & wait %1 # ps -o pid,ppid,user,args >&2 ;; stop) killall {testsleep} echo killed all {testsleep} >&2 sleep 1 ;; esac echo "done$1" >&2 exit 0 """.format(**locals())) text_file(os_path(testdir, "zzz.service"),""" [Unit] Description=Testing Z [Service] Type=forking ExecStart=/usr/bin/zzz.init start ExecStop=/usr/bin/zzz.init stop [Install] WantedBy=multi-user.target """.format(**locals())) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker cp /usr/bin/sleep {testname}:/usr/bin/{testsleep}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} touch /var/log/systemctl.debug.log" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'ls -l /usr/bin/{python} || {package} install -y {python}'" sx____(cmd.format(**locals())) if COVERAGE: cmd = "docker exec {testname} {package} install -y {python_coverage}" sh____(cmd.format(**locals())) self.prep_coverage(testname, cov_option) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) # cmd = "docker exec {testname} mkdir -p /etc/systemd/system" sx____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.service {testname}:/etc/systemd/system/zzz.service" sh____(cmd.format(**locals())) cmd = "docker cp {testdir}/zzz.init {testname}:/usr/bin/zzz.init" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable zzz.service" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl default-services -v" out2 = output(cmd.format(**locals())) logg.info("\n>\n%s", out2) # cmd = "docker commit -c 'CMD [\"/usr/bin/systemctl\",\"{cov_option}\"]' {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker rm --force {testname}x" sx____(cmd.format(**locals())) cmd = "docker run --detach --name {testname}x {images}:{testname}" sh____(cmd.format(**locals())) time.sleep(3) # cmd = "docker exec {testname}x ps -eo state,pid,ppid,user,args" top = output(cmd.format(**locals())) logg.info("\n>>>\n%s", top) # testsleep is running with parent-pid of '1' self.assertTrue(greps(top, " 1 root */usr/bin/.*sleep 111")) # and the pid '1' is systemctl (actually systemctl.py) self.assertTrue(greps(top, " 1 .* 0 .*systemctl")) # and let's check no zombies around so far: self.assertFalse(greps(top, "Z .*sleep.*")) # <<< no zombie yet # # check the subprocess m = re.search(r"(?m)^(\S+)\s+(\d+)\s+(\d+)\s+(\S+.*sleep 111.*)$", top) if m: state, pid, ppid, args = m.groups() logg.info(" - sleep state = %s", state) logg.info(" - sleep pid = %s", pid) logg.info(" - sleep ppid = %s", ppid) logg.info(" - sleep args = %s", args) self.assertEqual(state, "S") self.assertEqual(ppid, "1") self.assertIn("sleep", args) # # and kill the subprocess cmd = "docker exec {testname}x kill {pid}" sh____(cmd.format(**locals())) # time.sleep(1) cmd = "docker exec {testname}x ps -eo state,pid,ppid,user,args" top = output(cmd.format(**locals())) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, "Z .*sleep.*")) # <<< we have zombie! for attempt in xrange(10): time.sleep(3) cmd = "docker exec {testname}x ps -eo state,pid,ppid,user,args" top = output(cmd.format(**locals())) logg.info("\n[%s]>>>\n%s", attempt, top) if not greps(top, ""): break # cmd = "docker exec {testname}x ps -eo state,pid,ppid,user,args" top = output(cmd.format(**locals())) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, "Z .*sleep.*")) # <<< and it's gone! time.sleep(1) # cmd = "docker stop {testname}x" out3 = output(cmd.format(**locals())) logg.info("\n>\n%s", out3) # self.save_coverage(testname, testname+"x") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker rm --force {testname}x" sx____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) self.rm_testdir() def test_7001_centos_httpd(self): """ WHEN using a systemd-enabled CentOS 7, THEN we can create an image with an Apache HTTP service being installed and enabled. Without a special startup.sh script or container-cmd one can just start the image and in the container expecting that the service is started. Therefore, WHEN we start the image as a docker container THEN we can download the root html showing 'OK' because the test script has placed an index.html in the webserver containing that text. """ if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") if IMAGE and "centos" not in IMAGE: self.skipTest("centos-based test") images = IMAGES image = self.local_image(IMAGE or CENTOS) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) testname=self.testname() testport=self.testport() name="centos-httpd" systemctl_py = _systemctl_py sometime = SOMETIME or 288 logg.info("%s:%s %s", testname, testport, image) # WHEN cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker cp {systemctl_py} {testname}:/usr/bin/systemctl" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {package} install -y httpd httpd-tools" sh____(cmd.format(**locals())) cmd = "docker cp {systemctl_py} {testname}:/usr/bin/systemctl" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable httpd" sh____(cmd.format(**locals())) cmd = "docker exec {testname} sh -c 'echo TEST_OK > /var/www/html/index.html'" sh____(cmd.format(**locals())) cmd = "docker commit -c 'CMD [\"/usr/bin/systemctl\"]' {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker stop {testname}" sx____(cmd.format(**locals())) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run -d -p {testport}:80 --name {testname} {images}:{testname}" sh____(cmd.format(**locals())) # THEN tmp = self.testdir(testname) cmd = "sleep 5; wget -O {tmp}/{testname}.txt http://127.0.0.1:{testport}" sh____(cmd.format(**locals())) cmd = "grep OK {tmp}/{testname}.txt" sh____(cmd.format(**locals())) # CLEAN cmd = "docker stop {testname}" sh____(cmd.format(**locals())) cmd = "docker rm --force {testname}" sh____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) self.rm_testdir() def test_7002_centos_postgres(self): """ WHEN using a systemd-enabled CentOS 7, THEN we can create an image with an PostgreSql DB service being installed and enabled. Without a special startup.sh script or container-cmd one can just start the image and in the container expecting that the service is started. Therefore, WHEN we start the image as a docker container THEN we can see a specific role with an SQL query because the test script has created a new user account in the in the database with a known password. """ if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") if not os.path.exists(PSQL_TOOL): self.skipTest("postgres tools missing on host") if IMAGE and "centos" not in IMAGE: self.skipTest("centos-based test") images = IMAGES image = self.local_image(IMAGE or CENTOS) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) testname=self.testname() testport=self.testport() name="centos-postgres" systemctl_py = _systemctl_py sometime = SOMETIME or 288 logg.info("%s:%s %s", testname, testport, image) psql = PSQL_TOOL PG = "/var/lib/pgsql/data" # WHEN cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker cp {systemctl_py} {testname}:/usr/bin/systemctl" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {package} install -y postgresql-server postgresql-utils" sh____(cmd.format(**locals())) cmd = "docker cp {systemctl_py} {testname}:/usr/bin/systemctl" sh____(cmd.format(**locals())) cmd = "docker exec {testname} postgresql-setup initdb" sh____(cmd.format(**locals())) cmd = "docker exec {testname} sh -c \"sed -i -e 's/.*listen_addresses.*/listen_addresses = '\\\"'*'\\\"'/' {PG}/postgresql.conf\"" sh____(cmd.format(**locals())) cmd = "docker exec {testname} sh -c 'sed -i -e \"s/.*host.*ident/# &/\" {PG}/pg_hba.conf'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} sh -c 'echo \"host all all 0.0.0.0/0 md5\" >> {PG}/pg_hba.conf'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl start postgresql -vv" sh____(cmd.format(**locals())) cmd = "docker exec {testname} sh -c 'sleep 5; ps -ax'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} sh -c \"echo 'CREATE USER testuser_11 LOGIN ENCRYPTED PASSWORD '\\\"'Testuser.11'\\\" | runuser -u postgres /usr/bin/psql\"" sh____(cmd.format(**locals())) cmd = "docker exec {testname} sh -c \"echo 'CREATE USER testuser_OK LOGIN ENCRYPTED PASSWORD '\\\"'Testuser.OK'\\\" | runuser -u postgres /usr/bin/psql\"" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl stop postgresql -vv" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable postgresql" sh____(cmd.format(**locals())) cmd = "docker commit -c 'CMD [\"/usr/bin/systemctl\"]' {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker stop {testname}" sx____(cmd.format(**locals())) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run -d -p {testport}:5432 --name {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} sleep 5" sh____(cmd.format(**locals())) # THEN tmp = self.testdir(testname) login = "export PGUSER=testuser_11; export PGPASSWORD=Testuser.11" query = "SELECT rolname FROM pg_roles" cmd = "{login}; {psql} -p {testport} -h 127.0.0.1 -d postgres -c '{query}' > {tmp}/{testname}.txt" sh____(cmd.format(**locals())) cmd = "grep testuser_ok {tmp}/{testname}.txt" sh____(cmd.format(**locals())) # CLEAN cmd = "docker stop {testname}" sh____(cmd.format(**locals())) cmd = "docker rm --force {testname}" sh____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) self.rm_testdir() def test_7011_centos_httpd_socket_notify(self): """ WHEN using an image for a systemd-enabled CentOS 7, THEN we can create an image with an Apache HTTP service being installed and enabled. WHEN we start the image as a docker container THEN we can download the root html showing 'OK' and in the systemctl.debug.log we can see NOTIFY_SOCKET messages with Apache sending a READY and MAINPID value.""" if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") if IMAGE and "centos" not in IMAGE: self.skipTest("centos-based test") images = IMAGES image = self.local_image(IMAGE or CENTOS) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) testname=self.testname() testdir = self.testdir(testname) testport=self.testport() systemctl_py = _systemctl_py sometime = SOMETIME or 288 logg.info("%s:%s %s", testname, testport, image) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker cp {systemctl_py} {testname}:/usr/bin/systemctl" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {package} install -y httpd httpd-tools" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable httpd" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'echo TEST_OK > /var/www/html/index.html'" sh____(cmd.format(**locals())) # ## commit_container = "docker commit -c 'CMD [\"/usr/bin/systemctl\",\"init\",\"-vv\"]' {testname} {images}:{testname}" ## sh____(commit_container.format(**locals())) ## stop_container = "docker rm --force {testname}" ## sx____(stop_container.format(**locals())) ## start_container = "docker run --detach --name {testname} {images}:{testname} sleep 200" ## sh____(start_container.format(**locals())) ## time.sleep(3) # container = ip_container(testname) cmd = "docker exec {testname} touch /var/log/systemctl.debug.log" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl start httpd" sh____(cmd.format(**locals())) # THEN time.sleep(5) cmd = "wget -O {testdir}/result.txt http://{container}:80" sh____(cmd.format(**locals())) cmd = "grep OK {testdir}/result.txt" sh____(cmd.format(**locals())) # STOP cmd = "docker exec {testname} systemctl status httpd" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl stop httpd" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl status httpd" sx____(cmd.format(**locals())) cmd = "docker cp {testname}:/var/log/systemctl.debug.log {testdir}/systemctl.debug.log" sh____(cmd.format(**locals())) cmd = "docker stop {testname}" sh____(cmd.format(**locals())) cmd = "docker rm --force {testname}" sh____(cmd.format(**locals())) # CHECK self.assertEqual(len(greps(open(testdir+"/systemctl.debug.log"), " ERROR ")), 0) self.assertTrue(greps(open(testdir+"/systemctl.debug.log"), "use NOTIFY_SOCKET=")) self.assertTrue(greps(open(testdir+"/systemctl.debug.log"), "read_notify.*READY=1.*MAINPID=")) self.assertTrue(greps(open(testdir+"/systemctl.debug.log"), "notify start done")) self.assertTrue(greps(open(testdir+"/systemctl.debug.log"), "stop '/bin/kill' '-WINCH'")) self.assertTrue(greps(open(testdir+"/systemctl.debug.log"), "wait [$]NOTIFY_SOCKET")) self.assertTrue(greps(open(testdir+"/systemctl.debug.log"), "wait for PID .* is done")) self.rm_testdir() def test_7020_ubuntu_apache2_with_saved_container(self): """ WHEN using a systemd enabled Ubuntu as the base image THEN we can create an image with an Apache HTTP service being installed and enabled. Without a special startup.sh script or container-cmd one can just start the image and in the container expecting that the service is started. Therefore, WHEN we start the image as a docker container THEN we can download the root html showing 'OK' because the test script has placed an index.html in the webserver containing that text. """ if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") if IMAGE and "ubuntu" not in IMAGE: self.skipTest("ubuntu-based test") testname = self.testname() port=self.testport() images = IMAGES image = self.local_image(IMAGE or UBUNTU) python = os.path.basename(_python) systemctl_py = _systemctl_py sometime = SOMETIME or 288 logg.info("%s:%s %s", testname, port, image) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} apt-get update" sh____(cmd.format(**locals())) cmd = "docker exec {testname} apt-get install -y apache2 {python}" sh____(cmd.format(**locals())) cmd = "docker cp {systemctl_py} {testname}:/usr/bin/systemctl" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'test -L /bin/systemctl || ln -sf /usr/bin/systemctl /bin/systemctl'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable apache2" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'echo TEST_OK > /var/www/html/index.html'" sh____(cmd.format(**locals())) # ......................................... cmd = "docker commit -c 'CMD [\"/usr/bin/systemctl\"]' {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker stop {testname}" sx____(cmd.format(**locals())) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run -d -p {port}:80 --name {testname} {images}:{testname}" sh____(cmd.format(**locals())) # THEN tmp = self.testdir(testname) cmd = "sleep 5; wget -O {tmp}/{testname}.txt http://127.0.0.1:{port}" sh____(cmd.format(**locals())) cmd = "grep OK {tmp}/{testname}.txt" sh____(cmd.format(**locals())) # CLEAN cmd = "docker stop {testname}" sh____(cmd.format(**locals())) cmd = "docker rm --force {testname}" sh____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) self.rm_testdir() def test_7502_centos_postgres_user_mode_container(self): """ WHEN using a systemd-enabled CentOS 7, THEN we can create an image with an PostgreSql DB service being installed and enabled. Without a special startup.sh script or container-cmd one can just start the image and in the container expecting that the service is started. Instead of a normal root-based start we use a --user mode start here. But we do not use special user-mode *.service files.""" if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") if not os.path.exists(PSQL_TOOL): self.skipTest("postgres tools missing on host") if IMAGE and "centos" not in IMAGE: self.skipTest("centos-based test") images = IMAGES image = self.local_image(IMAGE or CENTOS) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) testname=self.testname() testport=self.testport() name="centos-postgres" systemctl_py = _systemctl_py sometime = SOMETIME or 288 logg.info("%s:%s %s", testname, testport, image) psql = PSQL_TOOL PG = "/var/lib/pgsql/data" # WHEN cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker cp {systemctl_py} {testname}:/usr/bin/systemctl" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {package} install -y postgresql-server postgresql-utils" sh____(cmd.format(**locals())) cmd = "docker cp {systemctl_py} {testname}:/usr/bin/systemctl" sh____(cmd.format(**locals())) cmd = "docker exec {testname} postgresql-setup initdb" sh____(cmd.format(**locals())) cmd = "docker exec {testname} sh -c \"sed -i -e 's/.*listen_addresses.*/listen_addresses = '\\\"'*'\\\"'/' {PG}/postgresql.conf\"" sh____(cmd.format(**locals())) cmd = "docker exec {testname} sh -c 'sed -i -e \"s/.*host.*ident/# &/\" {PG}/pg_hba.conf'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} sh -c 'echo \"host all all 0.0.0.0/0 md5\" >> {PG}/pg_hba.conf'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl start postgresql -vv" sh____(cmd.format(**locals())) cmd = "docker exec {testname} sh -c 'sleep 5; ps -ax'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} sh -c \"echo 'CREATE USER testuser_11 LOGIN ENCRYPTED PASSWORD '\\\"'Testuser.11'\\\" | runuser -u postgres /usr/bin/psql\"" sh____(cmd.format(**locals())) cmd = "docker exec {testname} sh -c \"echo 'CREATE USER testuser_OK LOGIN ENCRYPTED PASSWORD '\\\"'Testuser.OK'\\\" | runuser -u postgres /usr/bin/psql\"" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl stop postgresql -vv" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable postgresql" sh____(cmd.format(**locals())) cmd = "docker commit -c 'CMD [\"/usr/bin/systemctl\"]' {testname} {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker stop {testname}" sx____(cmd.format(**locals())) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run -d -p {testport}:5432 --name {testname} -u postgres {images}:{testname}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} sleep 5" sh____(cmd.format(**locals())) ############ the PID-1 has been run in systemctl.py --user mode ##### # THEN tmp = self.testdir(testname) login = "export PGUSER=testuser_11; export PGPASSWORD=Testuser.11" query = "SELECT rolname FROM pg_roles" cmd = "{login}; {psql} -p {testport} -h 127.0.0.1 -d postgres -c '{query}' > {tmp}/{testname}.txt" sh____(cmd.format(**locals())) cmd = "grep testuser_ok {tmp}/{testname}.txt" sh____(cmd.format(**locals())) # CLEAN cmd = "docker stop {testname}" sh____(cmd.format(**locals())) cmd = "docker rm --force {testname}" sh____(cmd.format(**locals())) cmd = "docker rmi {images}:{testname}" sx____(cmd.format(**locals())) self.rm_testdir() # @unittest.expectedFailure def test_8001_issue_1_start_mariadb_centos_7_0(self): """ issue 1: mariadb on centos 7.0 does not start""" # this was based on the expectation that "yum install mariadb" would allow # for a "systemctl start mysql" which in fact it doesn't. Double-checking # with "yum install mariadb-server" and "systemctl start mariadb" shows # that mariadb's unit file is buggy, because it does not specify a kill # signal that it's mysqld_safe controller does not ignore. if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") if IMAGE and "centos" not in IMAGE: self.skipTest("centos-based test") images = IMAGES image = self.local_image(IMAGE or CENTOS) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") # image = "centos:centos7.0.1406" # <<<< can not yum-install mariadb-server ? # image = "centos:centos7.1.1503" testname = self.testname() testdir = self.testdir() systemctl_py = _systemctl_py sometime = SOMETIME or 288 cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) # mariadb has a TimeoutSec=300 in the unit config: cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker cp {systemctl_py} {testname}:/usr/bin/systemctl" sh____(cmd.format(**locals())) cmd = "docker exec {testname} yum install -y mariadb" sh____(cmd.format(**locals())) if False: # expected in bug report but that one can not work: cmd = "docker exec {testname} systemctl enable mysql" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl list-unit-files --type=service" sh____(cmd.format(**locals())) out = output(cmd.format(**locals())) self.assertFalse(greps(out,"mysqld")) # cmd = "docker exec {testname} yum install -y mariadb-server" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl list-unit-files --type=service" sh____(cmd.format(**locals())) out = output(cmd.format(**locals())) self.assertTrue(greps(out,"mariadb.service")) # cmd = "docker exec {testname} systemctl start mariadb -vv" sh____(cmd.format(**locals())) # top_container = "docker exec {testname} ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, "mysqld ")) had_mysqld_safe = greps(top, "mysqld_safe ") # # NOTE: mariadb-5.5.52's mysqld_safe controller does ignore systemctl kill # but after a TimeoutSec=300 the 'systemctl kill' will send a SIGKILL to it # which leaves the mysqld to be still running -> this is an upstream error. cmd = "docker exec {testname} systemctl stop mariadb -vv" sh____(cmd.format(**locals())) top_container = "docker exec {testname} ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) # self.assertFalse(greps(top, "mysqld ")) if greps(top, "mysqld ") and had_mysqld_safe: logg.critical("mysqld still running => this is an uptream error!") # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) self.rm_testdir() def test_8002_issue_2_start_rsyslog_centos7(self): """ issue 2: rsyslog on centos 7 does not start""" # this was based on a ";Requires=xy" line in the unit file # but our unit parser did not regard ";" as starting a comment if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") if IMAGE and "centos" not in IMAGE: self.skipTest("centos-based test") images = IMAGES image = self.local_image(IMAGE or CENTOS) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") testname = self.testname() testdir = self.testdir() systemctl_py = _systemctl_py sometime = SOMETIME or 188 cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker cp {systemctl_py} {testname}:/usr/bin/systemctl" sh____(cmd.format(**locals())) cmd = "docker exec {testname} yum install -y rsyslog" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl --version" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl list-unit-files --type=service" sh____(cmd.format(**locals())) out = output(cmd.format(**locals())) self.assertTrue(greps(out,"rsyslog.service.*enabled")) # cmd = "docker exec {testname} systemctl start rsyslog -vv" sh____(cmd.format(**locals())) # top_container = "docker exec {testname} ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, "/usr/sbin/rsyslog")) # cmd = "docker exec {testname} systemctl stop rsyslog -vv" sh____(cmd.format(**locals())) top_container = "docker exec {testname} ps -eo pid,ppid,user,args" top = output(top_container.format(**locals())) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, "/usr/sbin/rsyslog")) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) self.rm_testdir() def test_8011_centos_httpd_socket_notify(self): """ start/restart behaviour if a httpd has failed - issue #11 """ if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") if IMAGE and "centos" not in IMAGE: self.skipTest("centos-based test") images = IMAGES image = self.local_image(IMAGE or CENTOS) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") testname=self.testname() testdir = self.testdir(testname) testport=self.testport() systemctl_py = _systemctl_py sometime = SOMETIME or 388 logg.info("%s:%s %s", testname, testport, image) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker cp {systemctl_py} {testname}:/usr/bin/systemctl" sh____(cmd.format(**locals())) cmd = "docker exec {testname} yum install -y httpd httpd-tools" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable httpd" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'echo TEST_OK > /var/www/html/index.html'" sh____(cmd.format(**locals())) # container = ip_container(testname) cmd = "docker exec {testname} touch /var/log/systemctl.debug.log" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl start httpd" sh____(cmd.format(**locals())) # THEN time.sleep(5) cmd = "wget -O {testdir}/result.txt http://{container}:80" sh____(cmd.format(**locals())) cmd = "grep OK {testdir}/result.txt" sh____(cmd.format(**locals())) # STOP cmd = "docker exec {testname} systemctl status httpd" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl stop httpd" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl status httpd" # # CRASH cmd = "docker exec {testname} bash -c 'cp /etc/httpd/conf/httpd.conf /etc/httpd/conf/httpd.conf.orig'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'echo foo > /etc/httpd/conf/httpd.conf'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl start httpd" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) # start failed cmd = "docker exec {testname} systemctl status httpd" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) cmd = "docker exec {testname} systemctl restart httpd" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertNotEqual(end, 0) # restart failed # cmd = "docker exec {testname} bash -c 'cat /etc/httpd/conf/httpd.conf.orig > /etc/httpd/conf/httpd.conf'" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl restart httpd" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # restart ok cmd = "docker exec {testname} systemctl stop httpd" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # down cmd = "docker exec {testname} systemctl status httpd" sx____(cmd.format(**locals())) # cmd = "docker cp {testname}:/var/log/systemctl.debug.log {testdir}/systemctl.debug.log" sh____(cmd.format(**locals())) cmd = "docker stop {testname}" sh____(cmd.format(**locals())) cmd = "docker rm --force {testname}" sh____(cmd.format(**locals())) # self.rm_testdir() def test_8031_centos_nginx_restart(self): """ start/restart behaviour if a nginx has failed - issue #31 """ if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") if IMAGE and "centos" not in IMAGE: self.skipTest("centos-based test") images = IMAGES image = self.local_image(IMAGE or CENTOS) if _python.endswith("python3") and "centos" in image: self.skipTest("no python3 on centos") package = package_tool(image) refresh = refresh_tool(image) testname=self.testname() testdir = self.testdir(testname) testport=self.testport() systemctl_py = _systemctl_py sometime = SOMETIME or 388 logg.info("%s:%s %s", testname, testport, image) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker cp {systemctl_py} {testname}:/usr/bin/systemctl" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {package} install -y epel-release" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {package} install -y nginx" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl enable nginx" sh____(cmd.format(**locals())) cmd = "docker exec {testname} rpm -q --list nginx" sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'rm /usr/share/nginx/html/index.html'" # newer nginx is broken sh____(cmd.format(**locals())) cmd = "docker exec {testname} bash -c 'echo TEST_OK > /usr/share/nginx/html/index.html'" sh____(cmd.format(**locals())) # container = ip_container(testname) cmd = "docker exec {testname} touch /var/log/systemctl.debug.log" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl start nginx" sh____(cmd.format(**locals())) # THEN time.sleep(5) cmd = "wget -O {testdir}/result.txt http://{container}:80" sh____(cmd.format(**locals())) cmd = "grep OK {testdir}/result.txt" sh____(cmd.format(**locals())) # STOP cmd = "docker exec {testname} systemctl status nginx" sh____(cmd.format(**locals())) # top = _recent(running(output(_top_list))) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, "nginx")) # cmd = "docker exec {testname} systemctl restart nginx" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # restart ok top = _recent(running(output(_top_list))) logg.info("\n>>>\n%s", top) self.assertTrue(greps(top, "nginx")) # cmd = "docker exec {testname} systemctl status nginx" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl stop nginx" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(end, 0) # down cmd = "docker exec {testname} systemctl status nginx" sx____(cmd.format(**locals())) top = _recent(running(output(_top_list))) logg.info("\n>>>\n%s", top) self.assertFalse(greps(top, "nginx")) # cmd = "docker cp {testname}:/var/log/systemctl.debug.log {testdir}/systemctl.debug.log" sh____(cmd.format(**locals())) cmd = "docker stop {testname}" sh____(cmd.format(**locals())) cmd = "docker rm --force {testname}" sh____(cmd.format(**locals())) # self.rm_testdir() def test_8034_testing_mask_unmask(self): """ Checking the issue 34 on Ubuntu """ if not os.path.exists(DOCKER_SOCKET): self.skipTest("docker-based test") images = IMAGES image = self.local_image(IMAGE or UBUNTU) package = package_tool(image) refresh = refresh_tool(image) testname = self.testname() testdir = self.testdir(testname) port=self.testport() python = os.path.basename(_python) systemctl_py = _systemctl_py sometime = SOMETIME or 288 logg.info("%s:%s %s", testname, port, image) # cmd = "docker rm --force {testname}" sx____(cmd.format(**locals())) cmd = "docker run --detach --name={testname} {image} sleep {sometime}" sh____(cmd.format(**locals())) cmd = "docker cp {systemctl_py} {testname}:/usr/bin/systemctl" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {refresh}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {package} install -y {python}" sh____(cmd.format(**locals())) cmd = "docker exec {testname} {package} install -y rsyslog" sh____(cmd.format(**locals())) ## container = ip_container(testname) cmd = "docker exec {testname} touch /var/log/systemctl.debug.log" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl status rsyslog.service" sx____(cmd.format(**locals())) cmd = "docker exec {testname} ls -l /etc/systemd/system" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl mask rsyslog.service" sx____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl status rsyslog.service" sx____(cmd.format(**locals())) cmd = "docker exec {testname} ls -l /etc/systemd/system" sh____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl start rsyslog.service" sx____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl unmask rsyslog.service" sx____(cmd.format(**locals())) cmd = "docker exec {testname} systemctl status rsyslog.service" sx____(cmd.format(**locals())) cmd = "docker exec {testname} ls -l /etc/systemd/system" sh____(cmd.format(**locals())) # cmd = "docker cp {testname}:/var/log/systemctl.debug.log {testdir}/systemctl.debug.log" sh____(cmd.format(**locals())) cmd = "docker stop {testname}" sh____(cmd.format(**locals())) cmd = "docker rm --force {testname}" sh____(cmd.format(**locals())) # self.rm_testdir() text_8051_serv = """# systemctl.py cat kubelet [Unit] Description=kubelet: The Kubernetes Node Agent Documentation=https://kubernetes.io/docs/home/ [Service] ExecStart=/usr/bin/kubelet Restart=always StartLimitInterval=0 RestartSec=10 [Install] WantedBy=multi-user.target """ text_8051_conf = """# cat /etc/systemd/system/kubelet.service.d/10-kubeadm.conf # Note: This dropin only works with kubeadm and kubelet v1.11+ [Service] Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf" Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml" # This is a file that "kubeadm init" and "kubeadm join" generates at runtime, populating the KUBELET_KUBEADM_ARGS variable dynamically EnvironmentFile=-/var/lib/kubelet/kubeadm-flags.env # This is a file that the user can use for overrides of the kubelet args as a last resort. Preferably, the user should use # the .NodeRegistration.KubeletExtraArgs object in the configuration files instead. KUBELET_EXTRA_ARGS should be sourced from this file. EnvironmentFile=-/etc/default/kubelet ExecStart= ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS """ def test_8051_systemctl_extra_conf_dirs(self): """ checking issue #51 on extra conf dirs """ testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/lib/systemd/system/kubelet.service"), self.text_8051_serv) text_file(os_path(root, "/lib/systemd/system/kubelet.service.d/10-kubeadm.conf"), self.text_8051_conf) # cmd = "{systemctl} environment kubelet -vvv" out, end = output2(cmd.format(**locals())) logg.debug(" %s =>%s\n%s", cmd, end, out) logg.info(" HAVE %s", greps(out, "HOME")) logg.info(" HAVE %s", greps(out, "KUBE")) self.assertTrue(greps(out, "KUBELET_CONFIG_ARGS=--config")) self.assertEqual(len(greps(out, "KUBE")), 2) cmd = "{systemctl} environment kubelet -vvv -p ExecStart" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 1) self.assertTrue(greps(out, "KUBELET_KUBECONFIG_ARGS")) self.rm_testdir() self.coverage() def test_8052_systemctl_extra_conf_dirs(self): """ checking issue #52 on extra conf dirs """ testdir = self.testdir() root = self.root(testdir) systemctl = cover() + _systemctl_py + " --root=" + root text_file(os_path(root, "/lib/systemd/system/kubelet.service"), self.text_8051_serv) text_file(os_path(root, "/etc/systemd/system/kubelet.service.d/10-kubeadm.conf"), self.text_8051_conf) # cmd = "{systemctl} environment kubelet -vvv" out, end = output2(cmd.format(**locals())) logg.debug(" %s =>%s\n%s", cmd, end, out) logg.info(" HAVE %s", greps(out, "HOME")) logg.info(" HAVE %s", greps(out, "KUBE")) self.assertTrue(greps(out, "KUBELET_CONFIG_ARGS=--config")) self.assertEqual(len(greps(out, "KUBE")), 2) cmd = "{systemctl} environment kubelet -vvv -p ExecStart" out, end = output2(cmd.format(**locals())) logg.info(" %s =>%s\n%s", cmd, end, out) self.assertEqual(len(lines(out)), 1) self.assertTrue(greps(out, "KUBELET_KUBECONFIG_ARGS")) self.rm_testdir() self.coverage() def test_9999_drop_local_mirrors(self): """ a helper when using images from https://github.com/gdraheim/docker-mirror-packages-repo" which create containers according to self.local_image(IMAGE) """ containers = output("docker ps -a") for line in lines(containers): found = re.search("\\b(opensuse-repo-\\d[.\\d]*)\\b", line) if found: container = found.group(1) logg.info(" ---> drop %s", container) sx____("docker rm -f {container}".format(**locals())) found = re.search("\\b(centos-repo-\\d[.\\d]*)\\b", line) if found: container = found.group(1) logg.info(" ---> drop %s", container) sx____("docker rm -f {container}".format(**locals())) found = re.search("\\b(ubuntu-repo-\\d[.\\d]*)\\b", line) if found: container = found.group(1) logg.info(" ---> drop %s", container) sx____("docker rm -f {container}".format(**locals())) if __name__ == "__main__": from optparse import OptionParser _o = OptionParser("%prog [options] test*", epilog=__doc__.strip().split("\n")[0]) _o.add_option("-v","--verbose", action="count", default=0, help="increase logging level [%default]") _o.add_option("--with", metavar="FILE", dest="systemctl_py", default=_systemctl_py, help="systemctl.py file to be tested (%default)") _o.add_option("-p","--python", metavar="EXE", default=_python, help="use another python execution engine [%default]") _o.add_option("-a","--coverage", action="count", default=0, help="gather coverage.py data (use -aa for new set) [%default]") _o.add_option("-l","--logfile", metavar="FILE", default="", help="additionally save the output log to a file [%default]") _o.add_option("--xmlresults", metavar="FILE", default=None, help="capture results as a junit xml file [%default]") _o.add_option("--sometime", metavar="SECONDS", default=SOMETIME, help="SOMETIME=%default (use 666)") _o.add_option("--todo", action="store_true", default=TODO, help="enable TODO outtakes [%default])") _o.add_option("--opensuse", metavar="NAME", default=OPENSUSE, help="OPENSUSE=%default") _o.add_option("--ubuntu", metavar="NAME", default=UBUNTU, help="UBUNTU=%default") _o.add_option("--centos", metavar="NAME", default=CENTOS, help="CENTOS=%default") _o.add_option("--image", metavar="NAME", default=IMAGE, help="IMAGE=%default (or CENTOS)") opt, args = _o.parse_args() logging.basicConfig(level = logging.WARNING - opt.verbose * 5) TODO = opt.todo # OPENSUSE = opt.opensuse UBUNTU = opt.ubuntu CENTOS = opt.centos IMAGE = opt.image if CENTOS in CENTOSVER: CENTOS = CENTOSVER[CENTOS] if ":" not in CENTOS: CENTOS = "centos:" + CENTOS if ":" not in OPENSUSE and "42" in OPENSUSE: OPENSUSE = "opensuse:" + OPENSUSE if ":" not in OPENSUSE: OPENSUSE = "opensuse/leap:" + OPENSUSE if ":" not in UBUNTU: UBUNTU = "ubuntu:" + UBUNTU if OPENSUSE not in TESTED_OS: logg.warning(" --opensuse '%s' was never TESTED!!!", OPENSUSE) beep(); time.sleep(2) if UBUNTU not in TESTED_OS: logg.warning(" --ubuntu '%s' was never TESTED!!!", UBUNTU) beep(); time.sleep(2) if CENTOS not in TESTED_OS: logg.warning(" --centos '%s' was never TESTED!!!", UBUNTU) beep(); time.sleep(2) if IMAGE and IMAGE not in TESTED_OS: logg.warning(" --image '%s' was never TESTED!!!", IMAGE) beep(); time.sleep(2) # _systemctl_py = opt.systemctl_py _python = opt.python # logfile = None if opt.logfile: if os.path.exists(opt.logfile): os.remove(opt.logfile) logfile = logging.FileHandler(opt.logfile) logfile.setFormatter(logging.Formatter("%(levelname)s:%(relativeCreated)d:%(message)s")) logging.getLogger().addHandler(logfile) logg.info("log diverted to %s", opt.logfile) xmlresults = None if opt.xmlresults: if os.path.exists(opt.xmlresults): os.remove(opt.xmlresults) xmlresults = open(opt.xmlresults, "w") logg.info("xml results into %s", opt.xmlresults) # if opt.coverage: COVERAGE = detect_local_system() # so that coverage files can be merged if opt.coverage > 1: if os.path.exists(".coverage"): logg.info("rm .coverage") os.remove(".coverage") # unittest.main() suite = unittest.TestSuite() if not args: args = [ "test_*" ] for arg in args: for classname in sorted(globals()): if not classname.endswith("Test"): continue testclass = globals()[classname] for method in sorted(dir(testclass)): if "*" not in arg: arg += "*" if arg.startswith("_"): arg = arg[1:] if fnmatch(method, arg): suite.addTest(testclass(method)) # select runner if not logfile: if xmlresults: import xmlrunner Runner = xmlrunner.XMLTestRunner result = Runner(xmlresults).run(suite) else: Runner = unittest.TextTestRunner result = Runner(verbosity=opt.verbose).run(suite) else: Runner = unittest.TextTestRunner if xmlresults: import xmlrunner Runner = xmlrunner.XMLTestRunner result = Runner(logfile.stream, verbosity=opt.verbose).run(suite) if opt.coverage: print(" " + coverage_tool() + " combine") print(" " + coverage_tool() + " report " + _systemctl_py) print(" " + coverage_tool() + " annotate " + _systemctl_py) if not result.wasSuccessful(): sys.exit(1)