Как включить пользовательские модули в pyinfra?

#python #provisioning #pyinfra

#python #подготовка #pyinfra

Вопрос:

Я использую pyinfra для подготовки некоторых файлов. Я хотел бы знать, где лучше всего разместить пользовательские модули.

Пример

Учитывая пользовательский факт, который возвращается True , если система не является безголовой:

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

    def process(self, output):
        return output
  

Вопросы

Куда мне это поместить? Я полагаю, что могу закодировать этот фрагмент непосредственно в «операционный» файл, но что, если я захочу повторно использовать этот код в нескольких модулях? Как мне абстрагировать это в отдельный модуль или получить к нему доступ из API?

Хотя данные могут быть разделены между модулями, рекомендуемая компоновка, по-видимому, не позволяет легко подключать пользовательские модули к API.

Подходы

  • Я пытался создать отдельный модуль уровня задачи, но он не распознается при вызове из командной строки.
  • Последним средством может быть добавление пути к sys.path , но я бы предпочел более чистый вариант.

Ответ №1:

Эта ошибка предполагает, что пользовательские факты могут быть обнаружены из config.py файла верхнего уровня, расположенного рядом с файлами «deploy».

Код

Пользовательский факт, закодированный в конфигурации (необязательно). Смотрите также пример макета:

 # 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
  

Примечание: завершение не позволяет pyinfra || true выдавать ошибки при сбое. Сбои, похоже, обрабатываются внутренне, несмотря на продолжение.

Когда пользовательский подкласс fact FactBase добавляется в индекс facts . Вы можете получить к ним доступ через атрибуты в змеином корпусе:

 # tasks/operation.py

from pyinfra import host


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

  

ДЕМОНСТРАЦИЯ

Запустите в командной строке.

 > 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
  

Ответ №2:

В реальной версии, для меня, это работает следующим образом:

база: /home/pyinfra

у меня есть поддиректор (пользовательский / факты) с пользовательскими фактами и операциями (и __init__.py файлом):

например /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 
  

Затем в моей задаче я могу импортировать и вызывать его через

 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!")
  

(не вешайте меня, если сам код несовершенен, хотя полезный ввод приветствуется, его достаточно для моего случая)

Комментарии:

1. Выглядит как отличный пример передачи аргументов в пользовательский факт.