Skip to content

Commit

Permalink
feat: predefined jobs (#98)
Browse files Browse the repository at this point in the history
* functional, must add: tests, doc

* fixed responsive layout, added docs
  • Loading branch information
mattLLVW authored Oct 26, 2019
1 parent d52cf8b commit ad49803
Show file tree
Hide file tree
Showing 28 changed files with 458 additions and 110 deletions.
28 changes: 28 additions & 0 deletions api/migrations/0002_jobtemplate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Generated by Django 2.2.6 on 2019-10-26 04:25

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [("api", "0001_initial")]

operations = [
migrations.CreateModel(
name="JobTemplate",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=255)),
("job", models.TextField()),
],
options={"db_table": "salt_job_template"},
)
]
20 changes: 19 additions & 1 deletion api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,13 @@ def user(self):
def arguments(self):
ret = self.loaded_ret()
if "fun_args" in ret and ret["fun_args"]:
return " ".join(str(i) for i in ret["fun_args"])
return " ".join(str(i) for i in ret["fun_args"] if "=" not in str(i))
return ""

def keyword_arguments(self):
ret = self.loaded_ret()
if "fun_args" in ret and ret["fun_args"]:
return " ".join(str(i) for i in ret["fun_args"] if "=" in str(i))
return ""

def success_bool(self):
Expand Down Expand Up @@ -104,6 +110,18 @@ class Meta:
app_label = "api"


class JobTemplate(models.Model):
name = models.CharField(max_length=255)
job = models.TextField()

def __str__(self):
return "{}".format(self.name)

class Meta:
db_table = "salt_job_template"
app_label = "api"


class Minions(models.Model):
minion_id = models.CharField(max_length=128, null=False, blank=False)
grain = models.TextField()
Expand Down
22 changes: 21 additions & 1 deletion api/serializers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import json

from django.contrib.auth.models import User
from rest_framework import serializers
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer

from api.utils import RawCommand
from .models import (
SaltReturns,
SaltEvents,
Expand All @@ -12,12 +15,14 @@
UserSettings,
Functions,
Schedule,
JobTemplate,
)


class SaltReturnsSerializer(serializers.ModelSerializer):
user = serializers.CharField()
arguments = serializers.CharField()
keyword_arguments = serializers.CharField()
success = serializers.BooleanField(source="success_bool")

class Meta:
Expand Down Expand Up @@ -74,11 +79,26 @@ class Meta:
fields = "__all__"


class JobTemplateSerializer(serializers.ModelSerializer):
def create(self, validated_data):
name = validated_data["name"]
job = validated_data["job"]
command = RawCommand(job)
parsed_command = command.parse()
obj, created = JobTemplate.objects.update_or_create(
name=name, defaults={"job": json.dumps(parsed_command[0])}
)
return obj

class Meta:
model = JobTemplate
fields = "__all__"


class UsersSerializer(serializers.ModelSerializer):
user_settings = UserSettingsSerializer(read_only=True)

def create(self, validated_data):
print(validated_data)
for param in ["is_active", "groups", "user_permissions"]:
del validated_data[param]
user = User(**validated_data)
Expand Down
2 changes: 2 additions & 0 deletions api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
search,
verify,
version,
JobTemplateViewSet,
)
from rest_framework import routers

Expand All @@ -43,6 +44,7 @@
router.register(r"minionsfields", MinionsCustomFieldsViewSet)
router.register(r"functions", FunctionsViewSet)
router.register(r"schedules", ScheduleViewSet)
router.register(r"job_templates", JobTemplateViewSet)

urlpatterns = [
path("", index_view, name="index"),
Expand Down
9 changes: 7 additions & 2 deletions api/views/alcali.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
manage_schedules,
)
from api.models import SaltReturns, Keys, Minions, SaltEvents, Schedule, Conformity
from api.models import UserSettings, MinionsCustomFields, Functions
from api.models import UserSettings, MinionsCustomFields, Functions, JobTemplate
from api.permissions import IsLoggedInUserOrAdmin, IsAdminUser
from api.renderer import StreamingRenderer
from api.serializers import (
Expand All @@ -48,6 +48,7 @@
ScheduleSerializer,
MyTokenObtainPairSerializer,
SaltReturnsSerializer,
JobTemplateSerializer,
)
from api.serializers import KeysSerializer, MinionsSerializer
from api.utils import graph_data, render_conformity, RawCommand
Expand Down Expand Up @@ -329,6 +330,11 @@ class UserSettingsViewSet(viewsets.ModelViewSet):
serializer_class = UserSettingsSerializer


class JobTemplateViewSet(viewsets.ModelViewSet):
queryset = JobTemplate.objects.all()
serializer_class = JobTemplateSerializer


@api_view(["GET"])
def jobs_graph(request):
id = request.query_params.get("id", None)
Expand Down Expand Up @@ -387,7 +393,6 @@ def stats(request):

@api_view(["GET"])
def version(request):

return Response({"version": settings.VERSION})


Expand Down
2 changes: 1 addition & 1 deletion dist/index.html
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=/favicon.ico><title>ALCALI</title><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900"><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css><link href="https://fonts.googleapis.com/css?family=Material+Icons" rel=stylesheet><link href=/static/css/app.321866b5.css rel=preload as=style><link href=/static/css/chunk-vendors.47ff193e.css rel=preload as=style><link href=/static/js/app.b0501687.js rel=preload as=script><link href=/static/js/chunk-vendors.439a09fa.js rel=preload as=script><link href=/static/css/chunk-vendors.47ff193e.css rel=stylesheet><link href=/static/css/app.321866b5.css rel=stylesheet></head><body><noscript><strong>We're sorry but my-app doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/static/js/chunk-vendors.439a09fa.js></script><script src=/static/js/app.b0501687.js></script></body></html>
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=/favicon.ico><title>ALCALI</title><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900"><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css><link href="https://fonts.googleapis.com/css?family=Material+Icons" rel=stylesheet><link href=/static/css/app.7dd676e0.css rel=preload as=style><link href=/static/css/chunk-vendors.47ff193e.css rel=preload as=style><link href=/static/js/app.6bf41df6.js rel=preload as=script><link href=/static/js/chunk-vendors.439a09fa.js rel=preload as=script><link href=/static/css/chunk-vendors.47ff193e.css rel=stylesheet><link href=/static/css/app.7dd676e0.css rel=stylesheet></head><body><noscript><strong>We're sorry but my-app doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/static/js/chunk-vendors.439a09fa.js></script><script src=/static/js/app.6bf41df6.js></script></body></html>

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions dist/static/js/app.6bf41df6.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions dist/static/js/app.6bf41df6.js.map

Large diffs are not rendered by default.

2 changes: 0 additions & 2 deletions dist/static/js/app.b0501687.js

This file was deleted.

1 change: 0 additions & 1 deletion dist/static/js/app.b0501687.js.map

This file was deleted.

Binary file added docs/docs/images/job_templates.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/docs/images/job_templates_save.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions docs/docs/screenshots.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ Jobs | Jobs Dark
#### Run
![](images/screenshots/run.png)

#### Job Templates
![](images/job_templates.png)

Keys | Keys Dark
:-------------------------:|:-------------------------:
![](images/screenshots/keys.png) | ![](images/screenshots/keys-dark.png)
Expand Down
8 changes: 8 additions & 0 deletions docs/docs/views/job_templates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Job Templates

![job_templates](../images/job_templates.png)

To create a job template, fill some fields in the [run](run.md) view, switch on "Save as Template",
and choose a name.

![job_templates_save](../images/job_templates_save.png)
6 changes: 2 additions & 4 deletions docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ nav:
- 'Jobs': views/jobs.md
- 'Job Details': views/job_detail.md
- 'Run': views/run.md
- 'Job Templates': views/job_templates.md
- 'Schedule': views/schedule.md
- 'Conformity': views/conformity.md
- 'Conformity Details': views/conformity_details.md
Expand All @@ -22,8 +23,7 @@ nav:
- 'Users': views/users.md
- 'Settings': views/settings.md
- Troubleshooting: troubleshooting.md
- Screenshots:
- '': screenshots.md
- Screenshots: screenshots.md
- Contribute: contribute.md
- Licence: licence.md
site_description: 'A web based tool for monitoring and administrating Saltstack'
Expand All @@ -39,8 +39,6 @@ copyright: 'Copyright &copy; 2019 All rights reserved'

theme:
name: 'material'
feature:
tabs: true
custom_dir: 'theme'
logo: 'images/logo-144.png'
favicon: 'images/favicon.ico'
Expand Down
2 changes: 1 addition & 1 deletion src/components/ConformityTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
color="orange"
tile
dark
:to="'/run/?target='+item.minion_id+'&function=state.apply'"
:to="'/run?tgt='+item.minion_id+'&fun=state.apply'"
>
highstate
</v-btn>
Expand Down
145 changes: 145 additions & 0 deletions src/components/JobTemplatesTable.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
<template>
<v-container>
<v-row no-gutters>
<v-col sm="12">
<v-card>
<v-card-title>
Job Templates
<v-spacer></v-spacer>
<v-text-field
v-model="search"
append-icon="search"
label="Search"
single-line
hide-details
></v-text-field>
</v-card-title>
<v-data-table
sort-by="jid"
sort-desc
:headers="headers"
:items="job_templates"
:search="search"
class="elevation-1"
:loading="loading"
>
<template v-slot:item.name="{ item }">
<b>{{item.name}}</b>
</template>
<template v-slot:item.action="{ item }">
<div class="text-center">
<v-btn
small
class="ma-2"
color="blue-grey"
tile
dark
:to="computeUrl(item, false)"
>
run
</v-btn>
<v-btn
small
class="ma-2"
color="orange"
tile
dark
:to="computeUrl(item, true)"
>
edit
</v-btn>
<v-btn
small
class="ma-2"
color="red"
tile
dark
@click="deleteTemplate(item.id)"
>
delete
</v-btn>
</div>
</template>
</v-data-table>
</v-card>
</v-col>
</v-row>
</v-container>

</template>

<script>
export default {
name: "JobTemplatesTable",
data() {
return {
search: "",
headers: [
{ text: "Name", value: "name" },
{ text: "Client", value: "client" },
{ text: "Target Type", value: "tgt_type" },
{ text: "Target", value: "tgt" },
{ text: "Function", value: "fun" },
{ text: "Arguments", value: "arg" },
{ text: "Keyword Arguments", value: "kwarg" },
{ text: "Batch", value: "batch" },
{ text: "Actions", value: "action", sortable: false },
],
loading: true,
job_templates: [],
}
},
mounted() {
this.loadData()
},
methods: {
loadData() {
this.$http.get("api/job_templates/").then(response => {
let templates = response.data
// Load the job as json and augment the template with its attrs.
templates.forEach(template => {
let loadedJob = JSON.parse(template.job)
// distinction between args and kwargs.
Object.keys(loadedJob).forEach(key => {
if (key === "arg") {
let args = loadedJob[key]
let kwarg = args.filter(item => {
return item.includes("=")
})
args = args.filter(item => {
return !item.includes("=")
})
template[key] = args.join(" ")
template["kwarg"] = kwarg.join(" ")
} else {
template[key] = loadedJob[key]
}
})
delete template.job
})
this.job_templates = templates
this.loading = false
})
},
deleteTemplate(id) {
this.$http.delete("api/job_templates/" + id).then(response => {
this.$toast("Template deleted")
}).then(() => {
this.loadData()
})
},
computeUrl(item, edit = false) {
let searchParams = new URLSearchParams(item)
searchParams.delete("id")
if (!edit) {
searchParams.delete("name")
}
return '/run?' + searchParams.toString()
}
},
}
</script>

<style scoped>
</style>
Loading

0 comments on commit ad49803

Please sign in to comment.