Estou tentando criar uma unidade systemctl, que só será iniciada se outra estiver ativa. Pelo que eu sei, isso pode ser feito especificando um Requisite
campo. Assim, configurei minha unidade assim:
[Unit]
Description=Force clients with mismatching IPs to reconnect
[email protected]
[Service]
Type=oneshot
ExecStart=/etc/openvpn/kicker/kicker.py
TimeoutSec=15
No entanto, quando usei systemctl stop openvpn@server
e systemctl start my-unit
, obtive os seguintes logs:
Starting openvpn-kicker.service - Force clients with mismatching IPs to reconnect...
Dependency failed for openvpn-kicker.service - Force clients with mismatching IPs to reconnect.
openvpn-kicker.service: Job openvpn-kicker.service/start failed with result 'dependency'.
kicker.py[1026]: Traceback (most recent call last):
kicker.py[1026]: File "/etc/openvpn/kicker/kicker.py", line 16, in <module>
kicker.py[1026]: manage.connect(Manage_Path)
kicker.py[1026]: FileNotFoundError: [Errno 2] No such file or directory
Main process exited, code=exited, status=1/FAILURE
openvpn-kicker.service: Failed with result 'exit-code'.
Então, basicamente, a unidade falha na verificação de requisitos, mas executa um script em "ExecStart" de qualquer maneira - daí os erros do python. A intenção é que funcione dessa maneira e tenho algo errado ou é um bug? Se relevante, o sistema operacional é Ubuntu 22.04
Primeiro, observe que as dependências de requisitos e as dependências de pedido são ortogonais no systemd. Uma unidade pode ter um requisito fraco/leve/forte em outra (
Wants
,Requires
,Requisite
, etc.), mas a ordem deve ser especificada separadamente usandoBefore
/After
. Pense assim:Wants=foo
: quando estou começando, peçofoo
para começar também, mas realmente não me importo com o que acontece com isso.Requires=foo
: quando eu estiver sendo iniciado, peçafoo
para ser iniciado também e, sefoo
a inicialização de falhar, considere que minha inicialização também falhou, independentemente do status das ações reais tomadas para minha inicialização.Requisite=foo
: quando estou sendo iniciado,foo
já deve ter sido iniciado. Caso contrário, considere que minha startup falhou, independentemente do status das ações reais tomadas para minha startup.Então, posso ter um serviço A que
Requires
B, mas posso ter uma ordenação deBefore
B. Este pode ser o caso quando B está fazendo algo geral que pode ser feito independentemente de A, mas A sempre quer que B faça algo depois de A ter iniciado.E, em particular
Requisite
, a documentação recomenda especificamente usá-lo comAfter
:A segunda coisa a entender é que empregos e unidades são conceitos separados, mas relacionados, no systemd. Veja, por exemplo, esta resposta ou esta resposta para saber mais sobre isso. Então, quando os documentos dizem:
Isso significa que o trabalho de inicialização desta unidade falhará. Mas a partida propriamente dita continuará. A forma como funciona é mais ou menos assim:
systemctl start foo
, ou alguma outra coisa faz comfoo
que seja iniciado (por exemplo, uma dependência de outra unidade, destino, etc.).Requisite
unidade que não está ativa resulta no status do trabalho definido como falhado.Before
no pedido.Required
,Requisite
ou mais forte, ele prosseguirá com a execução dosExecStart*
comandos. Observe que isso significa que se não houver outros trabalhos no pedido anterior, o systemd prosseguirá imediatamente com esta etapa .Requisite
unidade não estiver ativa ou por vários outros motivos, como falha na execução dos comandos, etc.Para que as verificações em (2.1) signifiquem alguma coisa para a execução dos comandos em (4), deve haver uma ordem que o systemd considerará em (3). Neste caso, não há.
Citando Lennart Poettering :