From: Michael Tremer Date: Fri, 2 Jun 2023 11:35:49 +0000 (+0000) Subject: builders: Add controls to manually start/stop builders X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=75a23d24796323173b6bb27ae23d62adaa4f7a75;p=pbs.git builders: Add controls to manually start/stop builders Signed-off-by: Michael Tremer --- diff --git a/Makefile.am b/Makefile.am index 05e2236b..9711e573 100644 --- a/Makefile.am +++ b/Makefile.am @@ -169,7 +169,9 @@ dist_templates_builders_DATA = \ src/templates/builders/delete.html \ src/templates/builders/edit.html \ src/templates/builders/index.html \ - src/templates/builders/show.html + src/templates/builders/show.html \ + src/templates/builders/start.html \ + src/templates/builders/stop.html templates_buildersdir = $(templatesdir)/builders diff --git a/src/buildservice/builders.py b/src/buildservice/builders.py index 52944ba7..ad7b9870 100644 --- a/src/buildservice/builders.py +++ b/src/buildservice/builders.py @@ -174,13 +174,13 @@ class Builders(base.Object): # Start all builders that have been allocated at least one job for builder in builders_to_be_launched: tasks.create_task( - builder.start(wait=wait), + builder.start(auto=True, wait=wait), ) # Shutdown the rest for builder in builders_to_be_shut_down: tasks.create_task( - builder.stop(wait=wait), + builder.stop(auto=True, wait=wait), ) # Stats @@ -616,7 +616,7 @@ class Builder(base.DataObject): if self.instance: return self.instance.state.get("Name") - async def start(self, wait=True): + async def start(self, auto=False, wait=True): """ Starts the instance on AWS """ @@ -624,8 +624,8 @@ class Builder(base.DataObject): if await self.is_running(): return - # Don't start when in maintenance mode - if self.maintenance: + # Don't start automatically when in maintenance mode + if auto and self.maintenance: log.warning("Won't start %s in maintenance mode" % self) return @@ -681,7 +681,7 @@ class Builder(base.DataObject): except botocore.exceptions.ClientError as e: log.warning("Could not change instance type of %s: %s" % (self, e)) - async def stop(self, wait=True): + async def stop(self, auto=False, wait=True): """ Stops this instance on AWS """ @@ -689,8 +689,8 @@ class Builder(base.DataObject): if await self.is_shutting_down() or await self.is_shut_down(): return - # Don't stop when in maintenance mode - if self.maintenance: + # Don't stop automatically when in maintenance mode + if auto and self.maintenance: log.warning("Won't stop %s in maintenance mode" % self) return diff --git a/src/templates/builders/show.html b/src/templates/builders/show.html index b777598f..7365ec8f 100644 --- a/src/templates/builders/show.html +++ b/src/templates/builders/show.html @@ -126,6 +126,19 @@ {{ _("Delete") }} + + {# Maintenance Mode Actions #} + {% if builder.maintenance %} + {% if is_running %} + + {{ _("Stop") }} + + {% elif is_shut_down %} + + {{ _("Start") }} + + {% end %} + {% end %} {% end %} diff --git a/src/templates/builders/start.html b/src/templates/builders/start.html new file mode 100644 index 00000000..d76180b3 --- /dev/null +++ b/src/templates/builders/start.html @@ -0,0 +1,43 @@ +{% extends "../modal.html" %} + +{% block title %}{{ _("Start Builder") }} - {{ builder }}{% end block %} + +{% block breadcrumbs %} + +{% end block %} + +{% block modal_title %} +

{{ _("Start Builder") }}

+
{{ builder }}
+{% end block %} + +{% block modal %} +
+ {% raw xsrf_form_html() %} + +
+

+ {{ _("Do you want to manually start %s?") % builder }} +

+
+ + {# Submit! #} +
+ +
+
+{% end block %} diff --git a/src/templates/builders/stop.html b/src/templates/builders/stop.html new file mode 100644 index 00000000..03569bb7 --- /dev/null +++ b/src/templates/builders/stop.html @@ -0,0 +1,43 @@ +{% extends "../modal.html" %} + +{% block title %}{{ _("Stop Builder") }} - {{ builder }}{% end block %} + +{% block breadcrumbs %} + +{% end block %} + +{% block modal_title %} +

{{ _("Stop Builder") }}

+
{{ builder }}
+{% end block %} + +{% block modal %} +
+ {% raw xsrf_form_html() %} + +
+

+ {{ _("Do you want to manually stop %s?") % builder }} +

+
+ + {# Submit! #} +
+ +
+
+{% end block %} diff --git a/src/web/__init__.py b/src/web/__init__.py index 6be6c04e..5d5ce96e 100644 --- a/src/web/__init__.py +++ b/src/web/__init__.py @@ -183,7 +183,9 @@ class Application(tornado.web.Application): (r"/builders/([A-Za-z0-9\-\.]+)", builders.ShowHandler), (r"/builders/([A-Za-z0-9\-\.]+)/delete", builders.DeleteHandler), (r"/builders/([A-Za-z0-9\-\.]+)/edit", builders.BuilderEditHandler), + (r"/builders/([A-Za-z0-9\-\.]+)/start", builders.StartHandler), (r"/builders/([A-Za-z0-9\-\.]+)/stats", builders.StatsHandler), + (r"/builders/([A-Za-z0-9\-\.]+)/stop", builders.StopHandler), (r"/api/v1/builders/control", builders.APIv1ControlHandler), # Distributions diff --git a/src/web/builders.py b/src/web/builders.py index 89336b4d..6a256891 100644 --- a/src/web/builders.py +++ b/src/web/builders.py @@ -80,12 +80,18 @@ class IndexHandler(base.BaseHandler): class ShowHandler(base.BaseHandler): - def get(self, hostname): + async def get(self, hostname): builder = self.backend.builders.get_by_name(hostname) if not builder: raise tornado.web.HTTPError(404, "Could not find builder %s" % hostname) - self.render("builders/show.html", builder=builder) + # Fetch status + args = { + "is_running" : await builder.is_running(), + "is_shut_down" : await builder.is_shut_down(), + } + + self.render("builders/show.html", builder=builder, **args) class CreateHandler(base.BaseHandler): @@ -176,6 +182,90 @@ class DeleteHandler(base.BaseHandler): self.redirect("/builders") +class StartHandler(base.BaseHandler): + @tornado.web.authenticated + def get(self, name): + builder = self.backend.builders.get_by_name(name) + if not builder: + raise tornado.web.HTTPError(404, "Builder not found: %s" % name) + + # Check permissions + if not builder.has_perm(self.current_user): + raise tornado.web.HTTPError(403) + + # Builders must be in maintenance mode + if not builder.maintenance: + raise tornado.web.HTTPError(400, "%s is not in maintenance mode" % builder) + + self.render("builders/start.html", builder=builder) + + @tornado.web.authenticated + async def post(self, name): + builder = self.backend.builders.get_by_name(name) + if not builder: + raise tornado.web.HTTPError(404, "Builder not found: %s" % name) + + # Check permissions + if not builder.has_perm(self.current_user): + raise tornado.web.HTTPError(403) + + # Builders must be in maintenance mode + if not builder.maintenance: + raise tornado.web.HTTPError(400, "%s is not in maintenance mode" % builder) + + # Start the builder + try: + await builder.start(wait=False) + + # XXX what do we do when this fails? + except: + raise + + self.redirect("/builders/%s" % builder.hostname) + + +class StopHandler(base.BaseHandler): + @tornado.web.authenticated + def get(self, name): + builder = self.backend.builders.get_by_name(name) + if not builder: + raise tornado.web.HTTPError(404, "Builder not found: %s" % name) + + # Check permissions + if not builder.has_perm(self.current_user): + raise tornado.web.HTTPError(403) + + # Builders must be in maintenance mode + if not builder.maintenance: + raise tornado.web.HTTPError(400, "%s is not in maintenance mode" % builder) + + self.render("builders/stop.html", builder=builder) + + @tornado.web.authenticated + async def post(self, name): + builder = self.backend.builders.get_by_name(name) + if not builder: + raise tornado.web.HTTPError(404, "Builder not found: %s" % name) + + # Check permissions + if not builder.has_perm(self.current_user): + raise tornado.web.HTTPError(403) + + # Builders must be in maintenance mode + if not builder.maintenance: + raise tornado.web.HTTPError(400, "%s is not in maintenance mode" % builder) + + # Stop the builder + try: + await builder.stop(wait=False) + + # XXX what do we do when this fails? + except: + raise + + self.redirect("/builders/%s" % builder.hostname) + + class StatsModule(ui_modules.UIModule): def render(self, builder): return self.render_string("builders/modules/stats.html", builder=builder)