pylang
pylang

Reputation: 44565

How to include custom modules in pyinfra?

I'm using pyinfra to provision some files. I would like to know the best place to put custom modules.

Example

Given a custom fact that returns True if a system is not headless:

class HasGui(FactBase):
    default = list
    command = 'ls /usr/share/xsessions/*.desktop || true'

    def process(self, output):
        return output

Questions

Where do I put this? I imagine I can code this snippet directly into an "operation" file, but what if I want to reuse this code in several modules? How do I abstract this into a separate module or access it from the API?

While data can be shared across modules, the recommended layout does not appear to easily permit custom modules to hook into the API.

Approaches

Upvotes: 2

Views: 809

Answers (2)

cernoel
cernoel

Reputation: 21

In actual version, for me, it works like this:

base: /home/pyinfra

i have a subdir (custom/facts) with custom facts and ops (and __init__.py file):

for example /home/pyinfra/custom/facts/FileFacts.py

from pyinfra.api import FactBase

class FileExists(FactBase):
    '''
    Returns if file exists (true/false)
    '''
    __filepath = ""
    def command(self, path):
        """ Checks if app exists via linux ls command """
        self.__filepath = path
        return 'ls {0}'.format(path)

    def process(self, output):
        # ls should return the path itself if it exists
        if str(output[0]) == self.__filepath:
            return True
        return False 

Then in my task i can import and call it via

from custom.facts.FileFacts import FileExists

if host.get_fact(FileExists, "/etc/app/config"):
     logger.info("File already exits!")
else:
     logger.info("File does not exist!")

(dont hang me if the code itself is imperfect, although helpful input is appreciated, its sufficient for my case)

Upvotes: 1

pylang
pylang

Reputation: 44565

This bug suggests custom facts can be detected from a top-level config.py file located next to "deploy" files.

Code

A custom fact coded into the config (optional). See also a sample layout:

# config.py

from pyinfra.api import FactBase


class HasGui(FactBase):
    default = list
    command = 'ls /usr/share/xsessions/*.desktop || true'

    def process(self, output):
        return output

Note: the trailing || true keeps pyinfra from erroring out on fails. Failures seem to be handled internally despite the continuation.

When a custom fact subclasses FactBase, it is added to a facts index. You can access them via snake-cased attributes:

# tasks/operation.py

from pyinfra import host


if host.fact.has_gui:
   # Insert operation
...

Demo

Run in command line.

> pyinfra @local tasks/operation.py
[@local]   Successful: 1   Errors: 0   Commands: 1/1

> pyinfra @<server> tasks/operation.py
[@local]   Successful: 0   Errors: 0   Commands: 1/1

Upvotes: 2

Related Questions