arches_extensions.management.commands.configure

  1import sys
  2from pathlib import Path
  3from django.core.management.base import BaseCommand
  4
  5from arches_extensions.utils import user_confirms
  6class Command(BaseCommand):
  7    """
  8    Configure service files used for Arches. Using this command will derive
  9    information about your app and python environment and write these directly
 10    into the files.
 11
 12    Usage:
 13
 14        python manage.py configure [operation]
 15
 16    Operations:
 17
 18        - `celery`
 19
 20    """
 21
 22    def __init__(self, *args, **kwargs):
 23        self.help = self.__doc__
 24
 25    def add_arguments(self, parser):
 26
 27        parser.add_argument(
 28            "operation",
 29            choices=["celery"]
 30        )
 31        parser.add_argument(
 32            "-a", "--app",
 33            help="Name of the Celery app your project creates (see celery.py)."
 34        )
 35        parser.add_argument(
 36            "-d", "--destination",
 37            default=".services",
 38            help="Optional path for output. Defaults to .services/ in root directory."
 39        )
 40        parser.add_argument(
 41            "--log-dir",
 42            default=".logs",
 43            help="Path to where logs and pid files will go. Defaults to .logs in root directory."
 44        )
 45        parser.add_argument(
 46            "--log-level",
 47            default="DEBUG",
 48            choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL', 'FATAL'],
 49            help="Set log level for celery. Default is DEBUG."
 50        )
 51        parser.add_argument(
 52            "--prefix",
 53            help="Optional prefix for the names of the services to be written."
 54        )
 55        parser.add_argument(
 56            "--require-rabbitmq",
 57            action="store_true",
 58            default=False,
 59            help="If true, Celery services will require rabbitmq.service",
 60        )
 61
 62    def handle(self, *args, **options):
 63
 64        self.dest = Path(options["destination"]).resolve()
 65        self.dest.mkdir(exist_ok=True)
 66
 67        self.log_dir = Path(options["log_dir"]).resolve()
 68        self.log_dir.mkdir(exist_ok=True)
 69
 70        python_env = Path(sys.executable).parent.parent
 71        self.celery_bin = Path(python_env, "bin", "celery")
 72
 73        self.working_directory = Path(".").resolve()
 74
 75        if options["operation"] == "celery":
 76            app = options["app"]
 77            if not app:
 78                print("-a/--app is require for celery configuration. cancelling.")
 79                exit()
 80            self.write_celery_services(
 81                options["app"],
 82                prefix=options["prefix"],
 83                log_level=options["log_level"],
 84                require_rabbitmq=options["require_rabbitmq"],
 85            )
 86
 87    def write_celery_services(self, app_name, prefix=None, log_level="DEBUG", require_rabbitmq=False):
 88
 89        requirement_block = "After=network.target"
 90        if user_confirms("Configure rabbitmq-server as a dependency? "/
 91                         "Do this if rabbitmq runs locally as a systemd service."):
 92            requirement_block = "After=rabbitmq-server.service\nRequires=rabbitmq-server.service"
 93
 94        prefix_ = "" if not prefix else f"{prefix}_"
 95        main_fname = f"{prefix_}celery.service"
 96        main_full_path = Path(self.dest, main_fname)
 97
 98        with open(main_full_path, "w") as o:
 99            o.write(f"""[Unit]
100Description=Celery Service{f" ({prefix})" if prefix else ""}
101{requirement_block}
102
103[Service]
104Type=simple
105WorkingDirectory={self.working_directory}
106ExecStart=/bin/sh -c '{self.celery_bin} \\
107    -A {app_name} worker -n worker1@%h \\
108    -B \\
109    -s celerybeat-schedule \\
110    --pidfile={self.log_dir}/{prefix_}celery.pid \\
111    --logfile={self.log_dir}/{prefix_}celery.log \\
112    --loglevel={log_level}'
113ExecStop=/bin/sh -c '{self.celery_bin} worker stopwait \\
114    --pidfile={self.log_dir}/{prefix_}celery.pid \\
115    --logfile={self.log_dir}/{prefix_}celery.log \\
116    --loglevel={log_level}'
117Restart=always
118RestartSec=1
119
120[Install]
121WantedBy=multi-user.target
122""")
123
124        beat_fname = f"{prefix_}celerybeat.service"
125        beat_full_path = Path(self.dest, beat_fname)
126        with open(beat_full_path, "w") as o:
127            o.write(f"""[Unit]
128Description=Celery Beat Service{f" ({prefix})" if prefix else ""}
129After=network.target
130
131[Service]
132Type=simple
133WorkingDirectory={self.working_directory}
134ExecStart=/bin/sh -c '{self.celery_bin} \\
135    -A {app_name} beat  \\
136    --pidfile={self.log_dir}/{prefix_}celerybeat.pid \\
137    --logfile={self.log_dir}/{prefix_}celerybeat.log \\
138    --loglevel={log_level}'
139Restart=always
140RestartSec=1
141
142[Install]
143WantedBy=multi-user.target
144""")
145
146        print(f"""
147Service files written to: {self.dest.relative_to(self.working_directory)}.
148Logs written to: {self.log_dir.relative_to(self.working_directory)}.
149
150*Make sure these directories are gitignored!*
151
152Initial deployment (first time only):
153    sudo ln -sf {main_full_path.absolute()} /etc/systemd/system/
154    sudo ln -sf {beat_full_path.absolute()} /etc/systemd/system/
155    sudo systemctl enable celery
156    sudo systemctl enable celerybeat
157    sudo systemctl start celery
158    sudo systemctl start celery
159
160Reload services:
161    sudo systemctl daemon-reload
162    sudo systemctl restart celery
163    sudo systemctl restart celerybeat
164""")
class Command(django.core.management.base.BaseCommand):
  7class Command(BaseCommand):
  8    """
  9    Configure service files used for Arches. Using this command will derive
 10    information about your app and python environment and write these directly
 11    into the files.
 12
 13    Usage:
 14
 15        python manage.py configure [operation]
 16
 17    Operations:
 18
 19        - `celery`
 20
 21    """
 22
 23    def __init__(self, *args, **kwargs):
 24        self.help = self.__doc__
 25
 26    def add_arguments(self, parser):
 27
 28        parser.add_argument(
 29            "operation",
 30            choices=["celery"]
 31        )
 32        parser.add_argument(
 33            "-a", "--app",
 34            help="Name of the Celery app your project creates (see celery.py)."
 35        )
 36        parser.add_argument(
 37            "-d", "--destination",
 38            default=".services",
 39            help="Optional path for output. Defaults to .services/ in root directory."
 40        )
 41        parser.add_argument(
 42            "--log-dir",
 43            default=".logs",
 44            help="Path to where logs and pid files will go. Defaults to .logs in root directory."
 45        )
 46        parser.add_argument(
 47            "--log-level",
 48            default="DEBUG",
 49            choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL', 'FATAL'],
 50            help="Set log level for celery. Default is DEBUG."
 51        )
 52        parser.add_argument(
 53            "--prefix",
 54            help="Optional prefix for the names of the services to be written."
 55        )
 56        parser.add_argument(
 57            "--require-rabbitmq",
 58            action="store_true",
 59            default=False,
 60            help="If true, Celery services will require rabbitmq.service",
 61        )
 62
 63    def handle(self, *args, **options):
 64
 65        self.dest = Path(options["destination"]).resolve()
 66        self.dest.mkdir(exist_ok=True)
 67
 68        self.log_dir = Path(options["log_dir"]).resolve()
 69        self.log_dir.mkdir(exist_ok=True)
 70
 71        python_env = Path(sys.executable).parent.parent
 72        self.celery_bin = Path(python_env, "bin", "celery")
 73
 74        self.working_directory = Path(".").resolve()
 75
 76        if options["operation"] == "celery":
 77            app = options["app"]
 78            if not app:
 79                print("-a/--app is require for celery configuration. cancelling.")
 80                exit()
 81            self.write_celery_services(
 82                options["app"],
 83                prefix=options["prefix"],
 84                log_level=options["log_level"],
 85                require_rabbitmq=options["require_rabbitmq"],
 86            )
 87
 88    def write_celery_services(self, app_name, prefix=None, log_level="DEBUG", require_rabbitmq=False):
 89
 90        requirement_block = "After=network.target"
 91        if user_confirms("Configure rabbitmq-server as a dependency? "/
 92                         "Do this if rabbitmq runs locally as a systemd service."):
 93            requirement_block = "After=rabbitmq-server.service\nRequires=rabbitmq-server.service"
 94
 95        prefix_ = "" if not prefix else f"{prefix}_"
 96        main_fname = f"{prefix_}celery.service"
 97        main_full_path = Path(self.dest, main_fname)
 98
 99        with open(main_full_path, "w") as o:
100            o.write(f"""[Unit]
101Description=Celery Service{f" ({prefix})" if prefix else ""}
102{requirement_block}
103
104[Service]
105Type=simple
106WorkingDirectory={self.working_directory}
107ExecStart=/bin/sh -c '{self.celery_bin} \\
108    -A {app_name} worker -n worker1@%h \\
109    -B \\
110    -s celerybeat-schedule \\
111    --pidfile={self.log_dir}/{prefix_}celery.pid \\
112    --logfile={self.log_dir}/{prefix_}celery.log \\
113    --loglevel={log_level}'
114ExecStop=/bin/sh -c '{self.celery_bin} worker stopwait \\
115    --pidfile={self.log_dir}/{prefix_}celery.pid \\
116    --logfile={self.log_dir}/{prefix_}celery.log \\
117    --loglevel={log_level}'
118Restart=always
119RestartSec=1
120
121[Install]
122WantedBy=multi-user.target
123""")
124
125        beat_fname = f"{prefix_}celerybeat.service"
126        beat_full_path = Path(self.dest, beat_fname)
127        with open(beat_full_path, "w") as o:
128            o.write(f"""[Unit]
129Description=Celery Beat Service{f" ({prefix})" if prefix else ""}
130After=network.target
131
132[Service]
133Type=simple
134WorkingDirectory={self.working_directory}
135ExecStart=/bin/sh -c '{self.celery_bin} \\
136    -A {app_name} beat  \\
137    --pidfile={self.log_dir}/{prefix_}celerybeat.pid \\
138    --logfile={self.log_dir}/{prefix_}celerybeat.log \\
139    --loglevel={log_level}'
140Restart=always
141RestartSec=1
142
143[Install]
144WantedBy=multi-user.target
145""")
146
147        print(f"""
148Service files written to: {self.dest.relative_to(self.working_directory)}.
149Logs written to: {self.log_dir.relative_to(self.working_directory)}.
150
151*Make sure these directories are gitignored!*
152
153Initial deployment (first time only):
154    sudo ln -sf {main_full_path.absolute()} /etc/systemd/system/
155    sudo ln -sf {beat_full_path.absolute()} /etc/systemd/system/
156    sudo systemctl enable celery
157    sudo systemctl enable celerybeat
158    sudo systemctl start celery
159    sudo systemctl start celery
160
161Reload services:
162    sudo systemctl daemon-reload
163    sudo systemctl restart celery
164    sudo systemctl restart celerybeat
165""")

Configure service files used for Arches. Using this command will derive information about your app and python environment and write these directly into the files.

Usage:

python manage.py configure [operation]

Operations:

- `celery`
Command(*args, **kwargs)
23    def __init__(self, *args, **kwargs):
24        self.help = self.__doc__
help = ''
def add_arguments(self, parser):
26    def add_arguments(self, parser):
27
28        parser.add_argument(
29            "operation",
30            choices=["celery"]
31        )
32        parser.add_argument(
33            "-a", "--app",
34            help="Name of the Celery app your project creates (see celery.py)."
35        )
36        parser.add_argument(
37            "-d", "--destination",
38            default=".services",
39            help="Optional path for output. Defaults to .services/ in root directory."
40        )
41        parser.add_argument(
42            "--log-dir",
43            default=".logs",
44            help="Path to where logs and pid files will go. Defaults to .logs in root directory."
45        )
46        parser.add_argument(
47            "--log-level",
48            default="DEBUG",
49            choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL', 'FATAL'],
50            help="Set log level for celery. Default is DEBUG."
51        )
52        parser.add_argument(
53            "--prefix",
54            help="Optional prefix for the names of the services to be written."
55        )
56        parser.add_argument(
57            "--require-rabbitmq",
58            action="store_true",
59            default=False,
60            help="If true, Celery services will require rabbitmq.service",
61        )

Entry point for subclassed commands to add custom arguments.

def handle(self, *args, **options):
63    def handle(self, *args, **options):
64
65        self.dest = Path(options["destination"]).resolve()
66        self.dest.mkdir(exist_ok=True)
67
68        self.log_dir = Path(options["log_dir"]).resolve()
69        self.log_dir.mkdir(exist_ok=True)
70
71        python_env = Path(sys.executable).parent.parent
72        self.celery_bin = Path(python_env, "bin", "celery")
73
74        self.working_directory = Path(".").resolve()
75
76        if options["operation"] == "celery":
77            app = options["app"]
78            if not app:
79                print("-a/--app is require for celery configuration. cancelling.")
80                exit()
81            self.write_celery_services(
82                options["app"],
83                prefix=options["prefix"],
84                log_level=options["log_level"],
85                require_rabbitmq=options["require_rabbitmq"],
86            )

The actual logic of the command. Subclasses must implement this method.

def write_celery_services( self, app_name, prefix=None, log_level='DEBUG', require_rabbitmq=False):
 88    def write_celery_services(self, app_name, prefix=None, log_level="DEBUG", require_rabbitmq=False):
 89
 90        requirement_block = "After=network.target"
 91        if user_confirms("Configure rabbitmq-server as a dependency? "/
 92                         "Do this if rabbitmq runs locally as a systemd service."):
 93            requirement_block = "After=rabbitmq-server.service\nRequires=rabbitmq-server.service"
 94
 95        prefix_ = "" if not prefix else f"{prefix}_"
 96        main_fname = f"{prefix_}celery.service"
 97        main_full_path = Path(self.dest, main_fname)
 98
 99        with open(main_full_path, "w") as o:
100            o.write(f"""[Unit]
101Description=Celery Service{f" ({prefix})" if prefix else ""}
102{requirement_block}
103
104[Service]
105Type=simple
106WorkingDirectory={self.working_directory}
107ExecStart=/bin/sh -c '{self.celery_bin} \\
108    -A {app_name} worker -n worker1@%h \\
109    -B \\
110    -s celerybeat-schedule \\
111    --pidfile={self.log_dir}/{prefix_}celery.pid \\
112    --logfile={self.log_dir}/{prefix_}celery.log \\
113    --loglevel={log_level}'
114ExecStop=/bin/sh -c '{self.celery_bin} worker stopwait \\
115    --pidfile={self.log_dir}/{prefix_}celery.pid \\
116    --logfile={self.log_dir}/{prefix_}celery.log \\
117    --loglevel={log_level}'
118Restart=always
119RestartSec=1
120
121[Install]
122WantedBy=multi-user.target
123""")
124
125        beat_fname = f"{prefix_}celerybeat.service"
126        beat_full_path = Path(self.dest, beat_fname)
127        with open(beat_full_path, "w") as o:
128            o.write(f"""[Unit]
129Description=Celery Beat Service{f" ({prefix})" if prefix else ""}
130After=network.target
131
132[Service]
133Type=simple
134WorkingDirectory={self.working_directory}
135ExecStart=/bin/sh -c '{self.celery_bin} \\
136    -A {app_name} beat  \\
137    --pidfile={self.log_dir}/{prefix_}celerybeat.pid \\
138    --logfile={self.log_dir}/{prefix_}celerybeat.log \\
139    --loglevel={log_level}'
140Restart=always
141RestartSec=1
142
143[Install]
144WantedBy=multi-user.target
145""")
146
147        print(f"""
148Service files written to: {self.dest.relative_to(self.working_directory)}.
149Logs written to: {self.log_dir.relative_to(self.working_directory)}.
150
151*Make sure these directories are gitignored!*
152
153Initial deployment (first time only):
154    sudo ln -sf {main_full_path.absolute()} /etc/systemd/system/
155    sudo ln -sf {beat_full_path.absolute()} /etc/systemd/system/
156    sudo systemctl enable celery
157    sudo systemctl enable celerybeat
158    sudo systemctl start celery
159    sudo systemctl start celery
160
161Reload services:
162    sudo systemctl daemon-reload
163    sudo systemctl restart celery
164    sudo systemctl restart celerybeat
165""")