playbook ide

pull/9399/head
Aaron3S 2023-01-18 18:03:33 +08:00 committed by Jiangjie.Bai
parent 7b95859015
commit 9d898f0aec
6 changed files with 219 additions and 12 deletions

View File

@ -1,13 +1,19 @@
import os
import shutil
import zipfile
from django.conf import settings
from django.shortcuts import get_object_or_404
from orgs.mixins.api import OrgBulkModelViewSet
from ..exception import PlaybookNoValidEntry
from ..models import Playbook
from ..serializers.playbook import PlaybookSerializer
__all__ = ["PlaybookViewSet"]
__all__ = ["PlaybookViewSet", "PlaybookFileBrowserAPIView"]
from rest_framework.views import APIView
from rest_framework.response import Response
def unzip_playbook(src, dist):
@ -31,12 +37,175 @@ class PlaybookViewSet(OrgBulkModelViewSet):
def perform_create(self, serializer):
instance = serializer.save()
src_path = os.path.join(settings.MEDIA_ROOT, instance.path.name)
dest_path = os.path.join(settings.DATA_DIR, "ops", "playbook", instance.id.__str__())
unzip_playbook(src_path, dest_path)
valid_entry = ('main.yml', 'main.yaml', 'main')
for f in os.listdir(dest_path):
if f in valid_entry:
return
os.remove(dest_path)
raise PlaybookNoValidEntry
if instance.create_method == 'blank':
dest_path = os.path.join(settings.DATA_DIR, "ops", "playbook", instance.id.__str__())
os.makedirs(dest_path)
with open(os.path.join(dest_path, 'main.yml'), 'w') as f:
f.write('## write your playbook here')
if instance.create_method == 'upload':
src_path = os.path.join(settings.MEDIA_ROOT, instance.path.name)
dest_path = os.path.join(settings.DATA_DIR, "ops", "playbook", instance.id.__str__())
unzip_playbook(src_path, dest_path)
valid_entry = ('main.yml', 'main.yaml', 'main')
for f in os.listdir(dest_path):
if f in valid_entry:
return
os.remove(dest_path)
raise PlaybookNoValidEntry
class PlaybookFileBrowserAPIView(APIView):
rbac_perms = ()
permission_classes = ()
def get(self, request, **kwargs):
playbook_id = kwargs.get('pk')
playbook = get_object_or_404(Playbook, id=playbook_id)
work_path = playbook.work_dir
file_key = request.query_params.get('key', '')
if file_key:
file_path = os.path.join(work_path, file_key)
with open(file_path, 'r') as f:
content = f.read()
return Response({'content': content})
else:
expand_key = request.query_params.get('expand', '')
nodes = self.generate_tree(playbook, work_path, expand_key)
return Response(nodes)
def post(self, request, **kwargs):
playbook_id = kwargs.get('pk')
playbook = get_object_or_404(Playbook, id=playbook_id)
work_path = playbook.work_dir
parent_key = request.data.get('key', '')
if parent_key == 'root':
parent_key = ''
if os.path.dirname(parent_key) == 'root':
parent_key = os.path.basename(parent_key)
full_path = os.path.join(work_path, parent_key)
is_directory = request.data.get('is_directory', False)
content = request.data.get('content', '')
if is_directory:
new_file_path = os.path.join(full_path, 'new_dir')
i = 0
while os.path.exists(new_file_path):
i += 1
new_file_path = os.path.join(full_path, 'new_dir({})'.format(i))
os.makedirs(new_file_path)
else:
new_file_path = os.path.join(full_path, 'new_file.yml')
i = 0
while os.path.exists(new_file_path):
i += 1
new_file_path = os.path.join(full_path, 'new_file({}).yml'.format(i))
with open(new_file_path, 'w') as f:
f.write(content)
relative_path = os.path.relpath(os.path.dirname(new_file_path), work_path)
new_node = {
"name": os.path.basename(new_file_path),
"title": os.path.basename(new_file_path),
"id": os.path.join(relative_path, os.path.basename(new_file_path))
if not os.path.join(relative_path, os.path.basename(new_file_path)).startswith('.')
else os.path.basename(new_file_path),
"isParent": is_directory,
"pId": relative_path if not relative_path.startswith('.') else 'root',
"open": True,
}
if not is_directory:
new_node['iconSkin'] = 'file'
return Response(new_node)
def patch(self, request, **kwargs):
playbook_id = kwargs.get('pk')
playbook = get_object_or_404(Playbook, id=playbook_id)
work_path = playbook.work_dir
file_key = request.data.get('key', '')
if os.path.dirname(file_key) == 'root':
file_key = os.path.basename(file_key)
new_name = request.data.get('new_name', '')
content = request.data.get('content', '')
is_directory = request.data.get('is_directory', False)
if not file_key or file_key == 'root':
return Response(status=400)
file_path = os.path.join(work_path, file_key)
if new_name:
new_file_path = os.path.join(os.path.dirname(file_path), new_name)
os.rename(file_path, new_file_path)
file_path = new_file_path
if not is_directory and content:
with open(file_path, 'w') as f:
f.write(content)
return Response({'msg': 'ok'})
def delete(self, request, **kwargs):
not_delete_allowed = ['root', 'main.yml']
playbook_id = kwargs.get('pk')
playbook = get_object_or_404(Playbook, id=playbook_id)
work_path = playbook.work_dir
file_key = request.query_params.get('key', '')
if not file_key:
return Response(status=400)
if file_key in not_delete_allowed:
return Response(status=400)
file_path = os.path.join(work_path, file_key)
if os.path.isdir(file_path):
shutil.rmtree(file_path)
else:
os.remove(file_path)
return Response({'msg': 'ok'})
@staticmethod
def generate_tree(playbook, root_path, expand_key=None):
nodes = [{
"name": playbook.name,
"title": playbook.name,
"id": 'root',
"isParent": True,
"open": True,
"pId": '',
"temp": False
}]
for path, dirs, files in os.walk(root_path):
dirs.sort()
files.sort()
relative_path = os.path.relpath(path, root_path)
for d in dirs:
node = {
"name": d,
"title": d,
"id": os.path.join(relative_path, d) if not os.path.join(relative_path, d).startswith(
'.') else d,
"isParent": True,
"open": False,
"pId": relative_path if not relative_path.startswith('.') else 'root',
"temp": False
}
if expand_key == node['id']:
node['open'] = True
nodes.append(node)
for f in files:
node = {
"name": f,
"title": f,
"iconSkin": 'file',
"id": os.path.join(relative_path, f) if not os.path.join(relative_path, f).startswith(
'.') else f,
"isParent": False,
"open": False,
"pId": relative_path if not relative_path.startswith('.') else 'root',
"temp": False
}
nodes.append(node)
return nodes

View File

@ -29,6 +29,11 @@ DEFAULT_PASSWORD_RULES = {
}
class CreateMethods(models.TextChoices):
blank = 'blank', _('Blank')
vcs = 'vcs', _('VCS')
class Types(models.TextChoices):
adhoc = 'adhoc', _('Adhoc')
playbook = 'playbook', _('Playbook')

View File

@ -0,0 +1,23 @@
# Generated by Django 3.2.14 on 2023-01-17 03:30
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('ops', '0024_alter_celerytask_date_last_publish'),
]
operations = [
migrations.AddField(
model_name='playbook',
name='create_method',
field=models.CharField(choices=[('blank', 'Blank'), ('upload', 'Upload'), ('vcs', 'VCS')], default='blank', max_length=128, verbose_name='CreateMethod'),
),
migrations.AddField(
model_name='playbook',
name='vcs_url',
field=models.CharField(blank=True, default='', max_length=1024, null=True, verbose_name='VCS URL'),
),
]

View File

@ -5,6 +5,7 @@ from django.conf import settings
from django.db import models
from django.utils.translation import gettext_lazy as _
from ops.const import CreateMethods
from ops.exception import PlaybookNoValidEntry
from orgs.mixins.models import JMSOrgBaseModel
@ -15,12 +16,20 @@ class Playbook(JMSOrgBaseModel):
path = models.FileField(upload_to='playbooks/')
creator = models.ForeignKey('users.User', verbose_name=_("Creator"), on_delete=models.SET_NULL, null=True)
comment = models.CharField(max_length=1024, default='', verbose_name=_('Comment'), null=True, blank=True)
create_method = models.CharField(max_length=128, choices=CreateMethods.choices, default=CreateMethods.blank,
verbose_name=_('CreateMethod'))
vcs_url = models.CharField(max_length=1024, default='', verbose_name=_('VCS URL'), null=True, blank=True)
@property
def entry(self):
work_dir = os.path.join(settings.DATA_DIR, "ops", "playbook", self.id.__str__())
work_dir = self.work_dir
valid_entry = ('main.yml', 'main.yaml', 'main')
for f in os.listdir(work_dir):
if f in valid_entry:
return os.path.join(work_dir, f)
raise PlaybookNoValidEntry
@property
def work_dir(self):
work_dir = os.path.join(settings.DATA_DIR, "ops", "playbook", self.id.__str__())
return work_dir

View File

@ -27,5 +27,5 @@ class PlaybookSerializer(BulkOrgResourceModelSerializer):
model = Playbook
read_only_fields = ["id", "date_created", "date_updated"]
fields = read_only_fields + [
"id", 'path', "name", "comment", "creator",
"id", 'path', "name", "comment", "creator", 'create_method', 'vcs_url',
]

View File

@ -23,6 +23,7 @@ router.register(r'tasks', api.CeleryTaskViewSet, 'task')
router.register(r'task-executions', api.CeleryTaskExecutionViewSet, 'task-executions')
urlpatterns = [
path('playbook/<uuid:pk>/file/', api.PlaybookFileBrowserAPIView.as_view(), name='playbook-file'),
path('variables/help/', api.JobRunVariableHelpAPIView.as_view(), name='variable-help'),
path('job-execution/asset-detail/', api.JobAssetDetail.as_view(), name='asset-detail'),
path('job-execution/task-detail/<uuid:task_id>/', api.JobExecutionTaskDetail.as_view(), name='task-detail'),