2021-08-17 17:00:34 +08:00
|
|
|
|
from flask import render_template,redirect
|
|
|
|
|
from flask_appbuilder.models.sqla.interface import SQLAInterface
|
|
|
|
|
from flask_appbuilder import ModelView, ModelRestApi
|
|
|
|
|
from flask_appbuilder import ModelView,AppBuilder,expose,BaseView,has_access
|
|
|
|
|
from importlib import reload
|
|
|
|
|
from flask_babel import gettext as __
|
|
|
|
|
from flask_babel import lazy_gettext as _
|
|
|
|
|
from flask_appbuilder.forms import GeneralModelConverter
|
|
|
|
|
import uuid
|
|
|
|
|
import re
|
|
|
|
|
from kfp import compiler
|
|
|
|
|
from sqlalchemy.exc import InvalidRequestError
|
|
|
|
|
from myapp.models.model_job import Repository,Images
|
|
|
|
|
from myapp.views.view_team import Project_Filter
|
|
|
|
|
from myapp import app, appbuilder,db,event_logger
|
2022-08-04 14:58:08 +08:00
|
|
|
|
from wtforms.validators import DataRequired, Length, NumberRange, Optional,Regexp
|
2021-08-17 17:00:34 +08:00
|
|
|
|
from wtforms import BooleanField, IntegerField,StringField, SelectField,FloatField,DateField,DateTimeField,SelectMultipleField,FormField,FieldList
|
|
|
|
|
|
|
|
|
|
from flask_appbuilder.fieldwidgets import BS3TextFieldWidget,BS3PasswordFieldWidget,DatePickerWidget,DateTimePickerWidget,Select2ManyWidget,Select2Widget
|
|
|
|
|
from myapp.forms import MyBS3TextAreaFieldWidget,MySelect2Widget,MyCodeArea,MyLineSeparatedListField,MyJSONField,MyBS3TextFieldWidget,MySelectMultipleField
|
|
|
|
|
|
|
|
|
|
from .baseApi import (
|
|
|
|
|
MyappModelRestApi
|
|
|
|
|
)
|
|
|
|
|
from flask import (
|
|
|
|
|
current_app,
|
|
|
|
|
abort,
|
|
|
|
|
flash,
|
|
|
|
|
g,
|
|
|
|
|
Markup,
|
|
|
|
|
make_response,
|
|
|
|
|
redirect,
|
|
|
|
|
render_template,
|
|
|
|
|
request,
|
|
|
|
|
send_from_directory,
|
|
|
|
|
Response,
|
|
|
|
|
url_for,
|
|
|
|
|
)
|
|
|
|
|
from myapp import security_manager
|
|
|
|
|
from werkzeug.datastructures import FileStorage
|
|
|
|
|
from .base import (
|
|
|
|
|
api,
|
|
|
|
|
BaseMyappView,
|
|
|
|
|
check_ownership,
|
|
|
|
|
data_payload_response,
|
|
|
|
|
DeleteMixin,
|
|
|
|
|
generate_download_headers,
|
|
|
|
|
get_error_msg,
|
|
|
|
|
get_user_roles,
|
|
|
|
|
handle_api_exception,
|
|
|
|
|
json_error_response,
|
|
|
|
|
json_success,
|
|
|
|
|
MyappFilter,
|
|
|
|
|
MyappModelView,
|
|
|
|
|
)
|
|
|
|
|
from flask_appbuilder import CompactCRUDMixin, expose
|
|
|
|
|
import pysnooper,datetime,time,json
|
|
|
|
|
conf = app.config
|
|
|
|
|
logging = app.logger
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2022-08-08 20:11:53 +08:00
|
|
|
|
|
2021-08-17 17:00:34 +08:00
|
|
|
|
class Repository_ModelView_Base():
|
|
|
|
|
datamodel = SQLAInterface(Repository)
|
|
|
|
|
|
|
|
|
|
label_title='仓库'
|
2022-08-02 16:02:22 +08:00
|
|
|
|
check_redirect_list_url = conf.get('MODEL_URLS',{}).get('repository','')
|
2022-08-08 20:11:53 +08:00
|
|
|
|
base_permissions = ['can_add', 'can_edit', 'can_delete', 'can_list', 'can_show']
|
2021-08-17 17:00:34 +08:00
|
|
|
|
base_order = ('id', 'desc')
|
|
|
|
|
order_columns = ['id']
|
|
|
|
|
list_columns = ['name','hubsecret','creator','modified']
|
2022-08-02 16:02:22 +08:00
|
|
|
|
cols_width = {
|
|
|
|
|
"name":{"type": "ellip2", "width": 250},
|
|
|
|
|
"hubsecret": {"type": "ellip2", "width": 250},
|
|
|
|
|
}
|
2021-08-17 17:00:34 +08:00
|
|
|
|
show_exclude_columns = ['password']
|
|
|
|
|
add_columns = ['name','server','user','password','hubsecret']
|
|
|
|
|
edit_columns = add_columns
|
|
|
|
|
|
|
|
|
|
add_form_extra_fields = {
|
2022-08-04 14:58:08 +08:00
|
|
|
|
"server": StringField(
|
|
|
|
|
_(datamodel.obj.lab('server')),
|
|
|
|
|
widget=BS3TextFieldWidget(),
|
|
|
|
|
default='harbor.oa.com',
|
|
|
|
|
description="镜像仓库服务地址"
|
|
|
|
|
),
|
|
|
|
|
"user": StringField(
|
|
|
|
|
_(datamodel.obj.lab('user')),
|
2022-08-16 11:09:52 +08:00
|
|
|
|
default='',
|
2022-08-04 14:58:08 +08:00
|
|
|
|
widget=BS3TextFieldWidget(),
|
|
|
|
|
description="镜像仓库的用户名"
|
|
|
|
|
),
|
2021-08-17 17:00:34 +08:00
|
|
|
|
"password": StringField(
|
|
|
|
|
_(datamodel.obj.lab('password')),
|
2022-08-16 11:09:52 +08:00
|
|
|
|
default='',
|
2022-08-04 14:58:08 +08:00
|
|
|
|
widget=BS3TextFieldWidget(),
|
|
|
|
|
description="镜像仓库的链接密码"
|
2021-08-17 17:00:34 +08:00
|
|
|
|
)
|
|
|
|
|
}
|
2022-07-26 20:47:49 +08:00
|
|
|
|
|
2021-08-17 17:00:34 +08:00
|
|
|
|
edit_form_extra_fields = add_form_extra_fields
|
|
|
|
|
|
|
|
|
|
# @pysnooper.snoop()
|
|
|
|
|
def set_column(self):
|
|
|
|
|
self.add_form_extra_fields['name'] = StringField(
|
|
|
|
|
_(self.datamodel.obj.lab('name')),
|
|
|
|
|
default=g.user.username+"-",
|
2022-08-08 20:11:53 +08:00
|
|
|
|
widget=BS3TextFieldWidget(),
|
2022-08-04 14:58:08 +08:00
|
|
|
|
description = "仓库名称"
|
2021-08-17 17:00:34 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
self.add_form_extra_fields['hubsecret'] = StringField(
|
|
|
|
|
_(self.datamodel.obj.lab('hubsecret')),
|
2022-08-04 14:58:08 +08:00
|
|
|
|
default=g.user.username + "-hubsecret",
|
2022-08-08 20:11:53 +08:00
|
|
|
|
widget=BS3TextFieldWidget(),
|
2022-08-04 14:58:08 +08:00
|
|
|
|
description="在k8s中创建的hub secret",
|
|
|
|
|
validators=[Regexp("^[a-z][a-z0-9\-]*[a-z0-9]$"), Length(1, 54),DataRequired()]
|
2021-08-17 17:00:34 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
pre_add_get = set_column
|
|
|
|
|
|
2022-08-08 20:11:53 +08:00
|
|
|
|
# create hubsecret
|
2021-09-07 18:09:47 +08:00
|
|
|
|
# @pysnooper.snoop()
|
2021-08-17 17:00:34 +08:00
|
|
|
|
def apply_hubsecret(self,hubsecret):
|
|
|
|
|
from myapp.utils.py.py_k8s import K8s
|
|
|
|
|
all_cluster=conf.get('CLUSTERS',{})
|
2022-05-27 11:50:05 +08:00
|
|
|
|
all_kubeconfig = [all_cluster[cluster].get('KUBECONFIG','') for cluster in all_cluster]+['']
|
2021-08-17 17:00:34 +08:00
|
|
|
|
all_kubeconfig = list(set(all_kubeconfig))
|
|
|
|
|
for kubeconfig in all_kubeconfig:
|
|
|
|
|
k8s = K8s(kubeconfig)
|
|
|
|
|
namespaces = conf.get('HUBSECRET_NAMESPACE')
|
|
|
|
|
for namespace in namespaces:
|
|
|
|
|
k8s.apply_hubsecret(namespace=namespace,
|
|
|
|
|
name=hubsecret.hubsecret,
|
|
|
|
|
user=hubsecret.user,
|
|
|
|
|
password=hubsecret.password,
|
|
|
|
|
server=hubsecret.server
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def post_add(self, item):
|
|
|
|
|
self.apply_hubsecret(item)
|
|
|
|
|
|
|
|
|
|
def post_update(self, item):
|
|
|
|
|
self.apply_hubsecret(item)
|
|
|
|
|
|
2022-08-02 16:02:22 +08:00
|
|
|
|
# class Repository_ModelView(Repository_ModelView_Base,MyappModelView,DeleteMixin):
|
|
|
|
|
# datamodel = SQLAInterface(Repository)
|
|
|
|
|
#
|
|
|
|
|
# appbuilder.add_view(Repository_ModelView,"仓库",icon = 'fa-shopping-basket',category = '训练',category_icon = 'fa-sitemap')
|
2021-08-17 17:00:34 +08:00
|
|
|
|
|
2022-08-08 20:11:53 +08:00
|
|
|
|
|
2021-08-17 17:00:34 +08:00
|
|
|
|
class Repository_ModelView_Api(Repository_ModelView_Base,MyappModelRestApi):
|
|
|
|
|
datamodel = SQLAInterface(Repository)
|
|
|
|
|
|
|
|
|
|
route_base = '/repository_modelview/api'
|
|
|
|
|
|
|
|
|
|
appbuilder.add_api(Repository_ModelView_Api)
|
|
|
|
|
|
|
|
|
|
|
2021-09-07 18:09:47 +08:00
|
|
|
|
|
|
|
|
|
|
2021-08-17 17:00:34 +08:00
|
|
|
|
# 只能查看到自己归属的项目组的镜像
|
|
|
|
|
class Images_Filter(MyappFilter):
|
|
|
|
|
# @pysnooper.snoop(watch_explode=('result'))
|
|
|
|
|
def apply(self, query, func):
|
|
|
|
|
user_roles = [role.name.lower() for role in list(self.get_user_roles())]
|
|
|
|
|
if "admin" in user_roles:
|
2022-08-16 11:09:52 +08:00
|
|
|
|
return query.order_by(self.model.id.desc())
|
2021-08-17 17:00:34 +08:00
|
|
|
|
|
|
|
|
|
result = query.order_by(self.model.id.desc())
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Images_ModelView_Base():
|
|
|
|
|
label_title='镜像'
|
|
|
|
|
datamodel = SQLAInterface(Images)
|
2022-08-02 16:02:22 +08:00
|
|
|
|
check_redirect_list_url = conf.get('MODEL_URLS',{}).get('images','')
|
2021-08-17 17:00:34 +08:00
|
|
|
|
|
2022-08-02 16:02:22 +08:00
|
|
|
|
list_columns = ['images_url','creator','modified']
|
|
|
|
|
cols_width = {
|
|
|
|
|
"images_url":{"type": "ellip2", "width": 500},
|
|
|
|
|
}
|
2021-08-17 17:00:34 +08:00
|
|
|
|
base_order = ('id', 'desc')
|
|
|
|
|
order_columns = ['id']
|
2021-09-07 18:09:47 +08:00
|
|
|
|
add_columns = ['repository', 'name', 'describe', 'dockerfile', 'gitpath']
|
2021-08-17 17:00:34 +08:00
|
|
|
|
edit_columns = add_columns
|
|
|
|
|
|
|
|
|
|
add_form_extra_fields = {
|
|
|
|
|
"dockerfile": StringField(
|
|
|
|
|
_(datamodel.obj.lab('dockerfile')),
|
|
|
|
|
description='镜像的构建Dockerfile全部内容',
|
2022-08-16 11:09:52 +08:00
|
|
|
|
default='',
|
2022-08-08 20:11:53 +08:00
|
|
|
|
widget=MyBS3TextAreaFieldWidget(rows=10),
|
2021-08-17 17:00:34 +08:00
|
|
|
|
),
|
|
|
|
|
"name": StringField(
|
|
|
|
|
_(datamodel.obj.lab('name')),
|
|
|
|
|
description='镜像名称全称,例如ubuntu:20.04',
|
2022-08-16 11:09:52 +08:00
|
|
|
|
default='',
|
2022-08-08 20:11:53 +08:00
|
|
|
|
widget=BS3TextFieldWidget(),
|
2021-08-17 17:00:34 +08:00
|
|
|
|
),
|
|
|
|
|
"entrypoint": StringField(
|
|
|
|
|
_(datamodel.obj.lab('entrypoint')),
|
|
|
|
|
description='镜像的入口命令,直接写成单行字符串,例如python xx.py,无需添加[]',
|
2022-08-16 11:09:52 +08:00
|
|
|
|
default='',
|
2022-08-08 20:11:53 +08:00
|
|
|
|
widget=BS3TextFieldWidget(),
|
2021-08-17 17:00:34 +08:00
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
edit_form_extra_fields = add_form_extra_fields
|
2022-08-08 20:11:53 +08:00
|
|
|
|
base_filters = [["id", Images_Filter, lambda: []]]
|
2021-08-17 17:00:34 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Images_ModelView(Images_ModelView_Base,MyappModelView,DeleteMixin):
|
|
|
|
|
datamodel = SQLAInterface(Images)
|
|
|
|
|
|
2022-02-26 22:36:57 +08:00
|
|
|
|
appbuilder.add_view(Images_ModelView,"模板镜像",href="/images_modelview/list/?_flt_2_name=",icon = 'fa-file-image-o',category = '训练')
|
2021-08-17 17:00:34 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Images_ModelView_Api(Images_ModelView_Base,MyappModelRestApi):
|
|
|
|
|
datamodel = SQLAInterface(Images)
|
|
|
|
|
route_base = '/images_modelview/api'
|
2022-08-02 16:02:22 +08:00
|
|
|
|
list_columns = ['images_url', 'modified','creator']
|
2021-08-17 17:00:34 +08:00
|
|
|
|
|
|
|
|
|
appbuilder.add_api(Images_ModelView_Api)
|
|
|
|
|
|