bifangback-model初設(shè)計及mock數(shù)據(jù)

作一個記錄,這版bifang后端數(shù)據(jù)庫設(shè)計,以極簡和高度規(guī)范為原則。配置即代碼,devops潮流大勢。

一,base_models

from django.db import models
from simple_history.models import HistoricalRecords
from django.contrib.auth.models import User


# 基礎(chǔ)虛類,所有Model的共同字段,其它model由此繼承,包括記錄orm操作歷史的history字段。
class BaseModel(models.Model):
    # unique=True用于保證名稱不重復(fù)
    name = models.CharField(max_length=100,
                            unique=True,
                            verbose_name="名稱")
    description = models.CharField(max_length=100,
                                   null=True,
                                   blank=True,
                                   verbose_name="描述")
    # 為了避免刪除關(guān)聯(lián)記錄,bigang里所有外鍵都是on_delete=models.SET_NULL
    create_user = models.ForeignKey(User,
                                    blank=True,
                                    null=True,
                                    on_delete=models.SET_NULL,
                                    verbose_name="用戶")
    # auto_now用于orm更新記錄時,每次自動更新此字段時間
    update_date = models.DateTimeField(auto_now=True)
    # auto_now_add只用于第一次新增時,自動更新此字段時間
    create_date = models.DateTimeField(auto_now_add=True)
    # 用于擴展
    base_status = models.BooleanField(default=True)
    # django的simple_history庫,加此字段,用于自動保存每個表的操作歷史
    history = HistoricalRecords(inherit=True)

    # property用于為orm查詢增加一個計算型字段
    @property
    def username(self):
        return self.create_user.username

    # 記錄的默認顯示值
    def __str__(self):
        return self.name

    class Meta:
        # abstract關(guān)鍵字,表示此class不會生成數(shù)據(jù)表,只能被繼承使用
        abstract = True
        # 默認排序規(guī)則,按更新時間降序
        ordering = ('-update_date',)
  • django-simple-history的用法,以后再單文說明。
  • 所有的model都繼承自這個Model,所以這個model的abstract = True。
  • name要求unique,在設(shè)計時要注意條件滿足。
  • property裝飾符,用于每個rest的返回值加用戶名。
  • update_date和create_date也為必有字段,保持每次更新時間和記錄新增時間。

二,git_models

from django.db import models
from .base_models import BaseModel


# GitLab代碼倉庫地址,有的公司可能有多個Git倉庫,所以獨立出一個數(shù)據(jù)表
# 如果只有一個代碼倉庫,當然也可以直接在django的settings文件里定義
class GitTb(BaseModel):
    # gitlab的URL
    git_url = models.URLField(verbose_name="Git API地址")
    # 用于python的gitlab庫去進行API 認證時需要
    git_token = models.CharField(max_length=64, default='no_token', verbose_name="Git API認證token")
    # gitlab的版本,僅于展示記錄,無實在用途
    git_ver = models.CharField(max_length=16, default='12.10', verbose_name="Git版本")

    class Meta:
        # 用于定義數(shù)據(jù)表名稱,而不使用系統(tǒng)自動生成的,統(tǒng)一命名為與類名相同
        db_table = 'GitTb'
        # 用一起定義admin后臺顯示名稱(規(guī)則為數(shù)據(jù)表名及簡短中文)
        verbose_name = 'GitTb代碼倉庫'
        verbose_name_plural = 'GitTb代碼倉庫'
  • db_table均統(tǒng)一命名為與類名相同。
  • Git,Salt,Jenkins這種表,為了避免與可能的第三方庫命令相同,無論是app名稱還是數(shù)據(jù)表名稱,都要注意加一點后綴。
  • verbose_name_plural 和verbose_name相同,命名為表名+中文說明。
  • git_token第三方庫去gitlab拉代碼時的認證。

三,salt_models

from django.db import models
from .base_models import BaseModel


# Sat Api地址,不同環(huán)境,可能需要不同的Salt Master隔離
# 同樣,如果只有一個salt master,只需要在django settings文件里定義
class SaltTb(BaseModel):
    # bifang項目主要使用了saltypie這個第三方庫操作salt-api,之前的字段,主要是滿足saltypie的認證salt-api的參數(shù)
    salt_url = models.URLField(verbose_name="Salt API地址")
    salt_user = models.CharField(max_length=64, verbose_name="Salt API用戶")
    salt_pwd = models.CharField(max_length=64, verbose_name="Salt API密碼")
    eauth = models.CharField(max_length=64, default='pam', verbose_name="Salt API用戶認證")
    trust_host = models.BooleanField(default=True, verbose_name="Salt API安全認證")
    # 同樣,這個版本只用于可能的展示,現(xiàn)在沒有使用
    salt_ver = models.CharField(max_length=12, default='2019.3010', verbose_name="Salt版本")

    class Meta:
        db_table = 'SaltTb'
        verbose_name = 'SaltTb遠程執(zhí)行工具'
        verbose_name_plural = 'SaltTb遠程執(zhí)行工具'
  • salt和git表類似,一般公司可能是只有一個,但為了能靈活,放在數(shù)據(jù)庫里配置。
  • saltypie庫的初始化如下:
    salt = Salt( url=salt_url, username=salt_user, passwd=salt_pwd, trust_host=True, eauth=eauth )

四, env_models

from django.db import models
from .base_models import BaseModel
from .salt_models import SaltTb


# 開發(fā)環(huán)境 ,線上環(huán)境,等等
class Env(BaseModel):
    # 因為繼續(xù)自BaseModel,所以name,description,user,date那些字段都有了,不用重復(fù)
    # 使用id,可以在必要時,減少一些對數(shù)據(jù)表自增id的依賴,在作數(shù)據(jù)庫遷移方面,還是有好處的
    env_id = models.IntegerField(default=0, verbose_name="環(huán)境Id")
    # 一般salt master都是分環(huán)境建的,這樣能達到批量管理的目的,多套saltmaster又可以隔離安全網(wǎng)絡(luò)
    salt = models.ForeignKey(SaltTb,
                             related_name="ra_env",
                             blank=True,
                             null=True,
                             on_delete=models.SET_NULL,
                             verbose_name="Salt地址")

    class Meta:
        db_table = 'Env'
        verbose_name = 'Env環(huán)境'
        verbose_name_plural = 'Env環(huán)境'
  • 環(huán)境表,可能只需要name就可以滿足需求,但為了更有擴展性,加一個env_id,沒害處。
  • 假定我們可以按環(huán)境可接入salt,所以這里加入一個salt的外鍵字段。
  • django 默認每個主表的對象都有一個是外鍵的屬性,可以通過它來查詢到所有屬于主表的子表的信息。這個屬性的名稱默認是以子表的名稱小寫加上_set()來表示,默認返回的是一個querydict對象。related_name 可以給這個外鍵定義好一個別的名稱。
  • related_name我的命名規(guī)則,是ra_開頭,然后加上表名env。這樣的約定,更易理解代碼中的引用。如果有兩個字段關(guān)聯(lián)到同一個外鍵表,才需要再定義一個不同的related_name(如server里關(guān)聯(lián)兩個release,一個運行發(fā)布單 ,一個可能的回滾單)。
  • blank=True,null=True,on_delete=models.SET_NULL,這三者定義是關(guān)聯(lián)的。如果我們定義了on_delete策略為models.SET_NULL,則必須定義null=True,即允許此字段為空。不然,在作數(shù)據(jù)庫的mock數(shù)據(jù)時,刪除一個表的所有記錄時,會有些細節(jié)和順序要調(diào)整。而如果使用這三個值,則基本無所謂順序。
  • null 是針對數(shù)據(jù)庫而言,如果 null=True, 表示數(shù)據(jù)庫的該字段可以為空。blank 是針對表單的,如果 blank=True,表示你的表單填寫該字段的時候可以不填。

五,project_models

from django.db import models
from .base_models import BaseModel


# 項目,可表示由多個相關(guān)微服務(wù)組成的項目
# 比如,istio里的bookin就算是bifang中的一個project
# 而其中的productpage,reviews,details,ratings這些應(yīng)用,就相當于bifang中的app應(yīng)用
class Project(BaseModel):
    # 只是為了一個中文顯示,及統(tǒng)一的項目id加的。不解釋
    cn_name = models.CharField(max_length=255, verbose_name="中文名")
    project_id = models.IntegerField(default=0, verbose_name="項目編號")

    class Meta:
        db_table = 'Project'
        verbose_name = 'Project項目'
        verbose_name_plural = 'Project項目'

  • 無須解釋,可以用來表示多含多個組件的項目,也可以用來表達一個含有很多微服務(wù)的項目總和,方便分類和管理。
  • 一般項目命令為英文字母,加一個中文別名和項目id,更易溝通和語義定義。
  • istio的bookinfo項目詳情:http://www.itdecent.cn/c/8572c5dbba97

六,app_models

from django.db import models
from .base_models import BaseModel
from .git_models import GitTb
from .project_models import Project


# 應(yīng)用服務(wù),相當于一個可獨立部署的微服務(wù)
# 如istio bookinfo中,可獨立部署,且使用不同語言開發(fā)的4個應(yīng)用(productpage, details, reviews, ratings)
class App(BaseModel):
    # cn_name和app_id意義和project中的一樣。但要注意,這個app_id,會和一些外鍵表中的app_id有雷同,這是高級技巧了,
    # 這是一個坑,但本來這樣命名,也是最自然的,無所畏懼。
    cn_name = models.CharField(max_length=255, verbose_name="中文名")
    app_id = models.IntegerField(default=0, verbose_name="應(yīng)用編號")
    # 每個app應(yīng)用,與一個代碼關(guān)聯(lián)
    git = models.ForeignKey(GitTb,
                            related_name="ra_app",
                            blank=True,
                            null=True,
                            on_delete=models.SET_NULL,
                            verbose_name="Git實例")
    # 這里單獨定義一個git中這個app的id,可能有優(yōu)化的空間,也可能沒有。git庫在定義具體的代碼倉庫時,就是要這個參數(shù)
    git_app_id = models.IntegerField(default=0, verbose_name="Git應(yīng)用ID")
    # 為了加強控制,git中每一個ci/cd功能,當代碼提交之后,都不會自動運行,而要通過一個trigger token去人工觸發(fā)
    # 這個trigger token的生成,在gitlab的ci/cd里,很容易生成。
    git_trigger_token = models.CharField(max_length=64,
                                         blank=True,
                                         null=True,
                                         verbose_name="git trigger token")
    # 將app與project進行關(guān)聯(lián),方便數(shù)據(jù)統(tǒng)計,關(guān)聯(lián)顯示
    project = models.ForeignKey(Project,
                                related_name='ra_project',
                                blank=True,
                                null=True,
                                on_delete=models.SET_NULL,
                                verbose_name='項目')
    # 按devops及gitops的理念,開發(fā)維護自己的編譯腳本和部署腳本,并進行版本管理
    # build_script用于定義通過構(gòu)建,生成軟件包的腳本
    build_script = models.CharField(max_length=255, default='build.sh', verbose_name="編譯腳本名")
    # deploy_script用于定義服務(wù)部署,啟停,備份,回滾,健康狀態(tài)檢測等功能,
    # 如果為開發(fā)提供了模板,是很容易作為代碼一部份管理起來的,配置即代碼!
    # 如果bifang本身來存儲這些腳本,反而增加管理難度,不透明度及中心化
    deploy_script = models.CharField(max_length=255, default='bifang.sh', verbose_name="部署腳本名")
    # 定義軟件包的名稱,這里也有糾結(jié),是自定義,還是強約定
    # 如果規(guī)定了軟件包名必須與app名稱相同,會少很多事,但又顯得過于霸道
    # 這里留個小口吧,另外,在部署腳本時,還會有幾個類似的軟件包,軟件壓縮包名,軟件部署目錄,都類似
    zip_package_name = models.CharField(max_length=255, default='demo.zio', verbose_name="應(yīng)用壓縮包")
    # 如果app有腳本端口,則可能用于狀態(tài)檢測,增加部署的成功判斷率
    service_port = models.IntegerField(default=0, verbose_name="服務(wù)端口")
    # 使用哪個用戶名和哪個用戶組啟動,
    service_username = models.CharField(max_length=24,
                                        blank=True,
                                        null=True,
                                        verbose_name="執(zhí)行用戶名")
    service_group = models.CharField(max_length=24,
                                     blank=True,
                                     null=True,
                                     verbose_name="執(zhí)行用戶組")
    # 如果bifang增加獨立的服務(wù)器啟停功能,日志單獨數(shù)據(jù)表保存,但這里可以保存最近啟停次數(shù),用于定義日志
    op_log_no = models.IntegerField(default=0, verbose_name="啟停日志次數(shù)")

    class Meta:
        db_table = 'App'
        verbose_name = 'App應(yīng)用'
        verbose_name_plural = 'App應(yīng)用'
  • cn_name和app_id,和Project字段同義。
  • 每個app屬于一個project。所以它們之間有一對多的關(guān)系。
  • 使用django model orm的外鍵定義,主要是在查詢方便,有很強大的語法支持。在更新時,orm更易和python的數(shù)據(jù)結(jié)構(gòu)交互。
  • git和git_repo用于定義此app的git地址,方便bifang對此代碼庫進行ci/cd操作。
  • service_username和service_group,用于調(diào)用salt進行遠程部署時,指定用戶名和用戶組。一般來說,用戶名足夠了。但,萬一有用戶的要求呢?留有擴展。
  • op_log_no 字段,用于定位此app組件的啟停批次,方便查看日志時的定義。當然,也可以在此app組件下的每個server中,使用此字段。但那樣ms管理有點不集中了。待優(yōu)化~

七,server_models

from django.db import models
from .base_models import BaseModel
from .app_models import App
from .release_models import Release

SYSTEM_CHOICES = (
    ('WINDOWS', 'WINDOWS'),
    ('LINUX', 'LINUX'),
)


# 部署服務(wù)器,保證ip和port結(jié)合起來的唯一性,可以一個服務(wù)器上部署多個應(yīng)用
# 當然,能在同一個服務(wù)器上,部署多個相同的應(yīng)用,這得益于將部署腳本讓開發(fā)自己維護。
# 真正的devops團隊,是需要都有開發(fā)和運維的跨界實力的啦~
class Server(BaseModel):
    # GenericIPAddressField的字段,讓這里只存儲ip地址
    ip = models.GenericIPAddressField(max_length=64, verbose_name="服務(wù)器Ip")
    # 服務(wù)端口,其實,這里是需要優(yōu)化的,
    # 如果有的服務(wù)啟動,不提供端口服務(wù)呢?亂寫么?如何保證多個無端口服務(wù)不沖突?
    port = models.IntegerField(verbose_name="服務(wù)器端口")
    system_type = models.CharField(max_length=16,
                                   choices=SYSTEM_CHOICES,
                                   default='LINUX',
                                   verbose_name="操作系統(tǒng)")
    # 此服務(wù)器與哪一個app關(guān)聯(lián)
    app = models.ForeignKey(App,
                            related_name='ra_server',
                            blank=True,
                            null=True,
                            on_delete=models.SET_NULL,
                            verbose_name='應(yīng)用服務(wù)')
    # 保存在此服務(wù)器正在運行的app的最近發(fā)布單
    main_release = models.ForeignKey(Release,
                                     related_name='ra_server_main',
                                     blank=True,
                                     null=True,
                                     on_delete=models.SET_NULL,
                                     verbose_name='主發(fā)布單')
    # 保存在此服務(wù)器正在運行的app的次新發(fā)布單,主要用于回滾,bifang只支持最近一次回滾,不支持無限回滾
    back_release = models.ForeignKey(Release,
                                     related_name='ra_server_back',
                                     blank=True,
                                     null=True,
                                     on_delete=models.SET_NULL,
                                     verbose_name='備份發(fā)布單')

    class Meta:
        db_table = 'Server'
        # 一個服務(wù)器上,部署多個應(yīng)用,保證Ip加port的唯一性。
        unique_together = ('ip', 'port')
        verbose_name = 'Server服務(wù)器'
        verbose_name_plural = 'Server服務(wù)器'

  • unique_together,聯(lián)合約束,這樣,在同一個服務(wù)器上,就可以部署多個app應(yīng)用。
  • main_release 和back_release ,主要用來顯示服務(wù)器最新應(yīng)用,以及回滾發(fā)布單。在每次部署時,都會影響這兩個字段。但只是停啟服務(wù)時,則不會更新這兩個字段。

八,release_models

from django.db import models
from .base_models import BaseModel
from .app_models import App
from .env_models import Env


# 發(fā)布單狀態(tài),為了能動態(tài)管理,我覺得加個單獨的表,有必要。
class ReleaseStatus(BaseModel):
    # ['Create', 'Building', 'BuildFailed', 'Build', 'Ready', 'Ongoing', 'Success', 'Failed']
    # ['創(chuàng)建', '編譯中', '編譯失敗', '編譯成功', '準備部署', '部署中', '部署成功', '部署失敗']
    status_value = models.CharField(max_length=1024, blank=True, verbose_name="狀態(tài)值")

    class Meta:
        db_table = 'ReleaseStatus'
        verbose_name = 'ReleaseStatus發(fā)布單狀態(tài)'
        verbose_name_plural = 'ReleaseStatus發(fā)布單狀態(tài)'


# 發(fā)布單, bifang部署平臺中的靈魂和紐帶
# 各種配置經(jīng)由它作融合,各種動態(tài)數(shù)據(jù)經(jīng)由它啟動發(fā)散
class Release(BaseModel):
    # app關(guān)聯(lián)
    app = models.ForeignKey(App,
                            related_name='ra_release',
                            blank=True,
                            null=True,
                            on_delete=models.SET_NULL,
                            verbose_name="應(yīng)用")
    # 環(huán)境關(guān)聯(lián),這個在新建和編譯發(fā)布單過程中,是沒有數(shù)據(jù)的,在環(huán)境流轉(zhuǎn)之后才有
    env = models.ForeignKey(Env,
                            related_name="ra_release",
                            blank=True,
                            null=True,
                            on_delete=models.SET_NULL,
                            verbose_name="環(huán)境")
    # 如果一個發(fā)布單部署了多次,或是分批在服務(wù)器部署,就有這個記錄的必要性了。
    deploy_no = models.IntegerField(blank=True,
                                    null=True,
                                    default=0,
                                    verbose_name="部署次數(shù)")
    # 自定義需要部署的git分支
    git_branch = models.CharField(max_length=255,
                                  blank=True,
                                  null=True)
    # pipeline_id和pipeline_url在編譯軟件包的過程中生成,用于獲取編譯狀態(tài)及定位編譯輸出
    pipeline_id = models.IntegerField(default=0)
    pipeline_url = models.URLField(default='http://www.demo.com')
    # 這個部署腳本,在git代碼中的一般會有自己獨立的目錄,而在制品倉庫時,可能就有軟件包并列在同一個目錄了,清晰。
    deploy_script_url = models.URLField(default=None,
                                        blank=True,
                                        null=True,
                                        verbose_name="部署腳本路徑")
    # 這里生成軟件包之后,直接記錄url軟件包路徑,這樣在部署腳本中,就可以直接使用wget下載了。
    # 為什么不使用salt?其實也行,但wget不是更穩(wěn)定和簡單么?
    zip_package_url = models.URLField(default=None,
                                      blank=True,
                                      null=True,
                                      verbose_name="壓縮制品庫路徑")
    # 記錄各種狀態(tài)用于前端顯示
    deploy_status = models.ForeignKey(ReleaseStatus,
                                      related_name='ra_release',
                                      blank=True,
                                      null=True,
                                      on_delete=models.SET_NULL,
                                      verbose_name="發(fā)布單狀態(tài)")

    class Meta:
        db_table = 'Release'
        verbose_name = 'Release發(fā)布單'
        verbose_name_plural = 'Release發(fā)布單'

  • ReleaseStatus表,用于定義發(fā)布單的狀態(tài)。用外表定義,比普通的字段,更有統(tǒng)一性。
  • Release發(fā)布單數(shù)據(jù)表,需和app,env關(guān)聯(lián)。但其狀態(tài)隨時間的變化,則放到后面的ReleaseHistory表中。這樣就可以在時間上串聯(lián)起操作歷史,而在空間上唯一定位當前發(fā)布單。這對于我之前的設(shè)計來說,是個改進(唯一尷尬的是,這種ReleaseHistory數(shù)據(jù)表,本身要不是記錄操作歷史?:))---更新,服務(wù)器的操作歷史,記錄在ServerHistory數(shù)據(jù)表中,完美解決。
  • git_branch 和git_commit,用于追蹤此個發(fā)布單 的代碼分支。
  • deploy_no 用于跟蹤此發(fā)布單在不同環(huán)境的部署日志。
  • salt_path和nginx_url 用于定位此app組件的制品庫路徑。此路徑下,一般會有兩個文件,一個制品包(war, zip...),另一個部署腳本。salt會調(diào)用這個部署腳本,操作制品包的部署全過程。這種集成的東東,對于之前的部署來說,也是一個新思路。

九,history_models

from django.db import models
from .base_models import BaseModel
from .env_models import Env
from .release_models import Release, ReleaseStatus
from .server_models import Server

# 用于在發(fā)布單的部署代碼層次內(nèi)的記錄,是部署新代碼,還是回滾老代碼?
# 用于發(fā)布單歷史
DEPLOY_CHOICES = (
    ('deploy', '部署'),
    ('rollback', '回滾'),
)

# 用于大的方向,是在部署代碼,還是單純在維護服務(wù)器啟停?
# 用于服務(wù)器操作歷史,區(qū)分發(fā)布單和服務(wù)器操作歷史是有意義的,維護不一樣,但也可以一追到底
OP_CHOICES = (
    ('deploy', '部署'),
    ('maintenance', '啟停維護'),
)

# 在服務(wù)器上操作的第一步作記錄,后期根據(jù)需要,有可能作維護改動
# 它主要要能包括所有deploy過程中的action_list列表項目
ACTION_CHOICES = (
    ('fetch', '獲取軟件'),
    ('stop', '停止'),
    ('stop_status', '停止狀態(tài)檢測'),
    ('deploy', '部署'),
    ('rollback', '回滾'),
    ('start', '啟動'),
    ('start_status', '啟動狀態(tài)檢測'),
    ('health_check', '服務(wù)健康檢測'),
)


# 發(fā)布單歷史,記錄發(fā)布單的生命周期,新建,編譯,流轉(zhuǎn),部署,回滾部署
class ReleaseHistory(BaseModel):
    release = models.ForeignKey(Release,
                                related_name='ra_release_history',
                                blank=True,
                                null=True,
                                on_delete=models.SET_NULL,
                                verbose_name='發(fā)布單')
    env = models.ForeignKey(Env,
                            related_name="ra_release_history",
                            blank=True,
                            null=True,
                            on_delete=models.SET_NULL,
                            verbose_name="環(huán)境")
    deploy_status = models.ForeignKey(ReleaseStatus,
                                      related_name='ra_release_history',
                                      blank=True,
                                      null=True,
                                      on_delete=models.SET_NULL,
                                      verbose_name="發(fā)布單狀態(tài)")
    deploy_type = models.CharField(max_length=255,
                                   choices=DEPLOY_CHOICES,
                                   blank=True,
                                   null=True,
                                   verbose_name="部署類型")
    log = models.TextField(verbose_name="日志內(nèi)容")

    class Meta:
        db_table = 'ReleaseHistory'
        verbose_name = 'ReleaseHistory發(fā)布單歷史'
        verbose_name_plural = 'ReleaseHistory發(fā)布單歷史'


# 服務(wù)器變更歷史,記錄服務(wù)器上的部署,停止,回滾
class ServerHistory(BaseModel):
    server = models.ForeignKey(Server,
                               related_name='ra_server_history',
                               blank=True,
                               null=True,
                               on_delete=models.SET_NULL,
                               verbose_name='服務(wù)器')
    release = models.ForeignKey(Release,
                                related_name='ra_server_history',
                                blank=True,
                                null=True,
                                on_delete=models.SET_NULL,
                                verbose_name='發(fā)布單')
    env = models.ForeignKey(Env,
                            related_name="ra_server_history",
                            blank=True,
                            null=True,
                            on_delete=models.SET_NULL,
                            verbose_name="環(huán)境")
    op_type = models.CharField(max_length=255,
                               choices=OP_CHOICES,
                               blank=True,
                               null=True,
                               verbose_name="操作類型")
    action_type = models.CharField(max_length=255,
                                   choices=ACTION_CHOICES,
                                   blank=True,
                                   null=True,
                                   verbose_name="服務(wù)器操作類型")
    log = models.TextField(verbose_name="日志內(nèi)容")

    class Meta:
        db_table = 'ServerHistory'
        verbose_name = 'ServerHistory服務(wù)器歷史'
        verbose_name_plural = 'ServerHistory服務(wù)器歷史'

  • 維護兩個歷史表,一個記錄發(fā)布單歷史,一個記錄服務(wù)器歷史。待完善。

十,permission_models

from django.db import models
from .base_models import BaseModel
from django.contrib.auth.models import User
from .app_models import App


# 創(chuàng)建編譯發(fā)布單, 環(huán)境流轉(zhuǎn),部署發(fā)布單三個大的權(quán)限,
# bifang暫不支持基于各個具體環(huán)境的細致權(quán)限
# 大家可以自己基于書上教授的技能,自行實現(xiàn)
class Action(BaseModel):
    action_id = models.IntegerField(unique=True, verbose_name="權(quán)限序號")

    class Meta:
        db_table = 'Action'
        verbose_name = 'Action權(quán)限'
        verbose_name_plural = 'Action權(quán)限'


# 具體的權(quán)限數(shù)據(jù)表
# 如果要獲取某個服務(wù)的所有權(quán)限,或是某一應(yīng)用的指定權(quán)限的用戶列表,都是OK的。
class Permission(BaseModel):
    # 權(quán)限與app應(yīng)用級別關(guān)聯(lián)
    app = models.ForeignKey(App,
                            related_name="ra_permission",
                            blank=True,
                            null=True,
                            on_delete=models.SET_NULL,
                            verbose_name="App應(yīng)用")
    # 權(quán)限與具體的權(quán)限動作(創(chuàng)建編譯,環(huán)境流轉(zhuǎn),部署發(fā)布)關(guān)聯(lián)
    action = models.ForeignKey(Action,
                               related_name="ra_permission",
                               blank=True,
                               null=True,
                               on_delete=models.SET_NULL,
                               verbose_name="操作權(quán)限")
    # 權(quán)限關(guān)聯(lián)到用戶
    pm_user = models.ForeignKey(User,
                                related_name="ra_permission",
                                blank=True,
                                null=True,
                                on_delete=models.SET_NULL,
                                verbose_name="權(quán)限用戶")

    class Meta:
        db_table = 'Permission'
        verbose_name = 'Permission應(yīng)用權(quán)限'
        verbose_name_plural = 'Permission應(yīng)用權(quán)限'

  • casbin感覺一般般,可能不太適用于我這種情況,或是動靜開大,直接手擼。
  • 將權(quán)限分為三個,一個是新建編譯權(quán)限,一個為環(huán)境流轉(zhuǎn)權(quán)限,一個為部署各個環(huán)境的權(quán)限(不再區(qū)分),自己的東東自己負責(zé)。
  • 能編輯權(quán)限的,只有三種用戶,一個是系統(tǒng)admin,一種是project的創(chuàng)建用戶,一個是app的創(chuàng)建用戶。
  • 小而美的權(quán)限,也不過如彼。

十一,mock數(shù)據(jù)

from django.core.management.base import BaseCommand, CommandError
from django.contrib.auth.models import Group
from cmdb.models import *
import string
import random
import time
import datetime
from django.contrib.auth import get_user_model

User = get_user_model()

username = 'admin'
group_name = 'admin'


# 自定義命令,用于建立測試數(shù)據(jù),很多ORM語句會使用
class Command(BaseCommand):
    help = 'create test data for BiFang back server.'

    def add_arguments(self, parser):
        self.stdout.write(self.style.SUCCESS('沒有額外參數(shù),新建全部模擬測試數(shù)據(jù),刪除所有舊記錄'))

    def handle(self, *args, **options):
        self.add_user()
        self.add_git()
        self.add_salt()
        self.add_env()
        self.add_project()
        self.add_app()
        self.add_server()
        self.add_release_status()
        self.add_release()
        self.add_action()
        self.add_permission()
        self.stdout.write(self.style.SUCCESS('數(shù)據(jù)重建完成,一把梭哈~~~'))
        # raise CommandError('Ok!')

    # 新建一個用戶
    def add_user(self):
        User.objects.all().delete()
        Group.objects.all().delete()
        print('delete all user and group data')
        User.objects.create_user(username='Dylan', email='user@demo.com', password="password")
        User.objects.create_user(username='Tyler', email='user@demo.com', password="password")
        User.objects.create_user(username='Kyle', email='user@demo.com', password="password")
        User.objects.create_user(username='Dakota', email='user@demo.com', password="password")
        User.objects.create_user(username='Marcus', email='user@demo.com', password="password")
        User.objects.create_user(username='Samantha', email='user@demo.com', password="password")
        User.objects.create_user(username='Kayla', email='user@demo.com', password="password")
        User.objects.create_user(username='Sydney', email='user@demo.com', password="password")
        User.objects.create_user(username='Courtney', email='user@demo.com', password="password")
        User.objects.create_user(username='Mariah', email='user@demo.com', password="password")
        User.objects.create_user(username='tom', email='user@demo.com', password="password")
        User.objects.create_user(username='mary', email='user@demo.com', password="password")
        admin = User.objects.create_superuser(username, 'admin@demon.com', 'password')
        root = User.objects.create_superuser('root', 'root@demon.com', 'password')
        admin_group = Group.objects.create(name=group_name)
        Group.objects.create(name='test')
        Group.objects.create(name='dev')
        Group.objects.create(name='operate')
        admin_users = [admin, root]
        admin_group.user_set.set(admin_users)
        self.stdout.write('用戶和用戶組重建完成。')

    # 新建一個Git倉庫
    def add_git(self):
        GitTb.objects.all().delete()
        print('delete all GitTb data')
        create_user = User.objects.get(username=username)
        GitTb.objects.create(name='MainGit',
                             description='主要git庫',
                             create_user=create_user,
                             git_url='http://192.168.1.211:8180',
                             git_token='RbCcuLssPekyVgy24Nui')
        self.stdout.write('GitTb重建完成。')

    # 新建一個SaltApi
    def add_salt(self):
        SaltTb.objects.all().delete()
        print('delete all SaltTb data')
        create_user = User.objects.get(username=username)
        SaltTb.objects.create(name='MainSalt',
                              description='主要SaltApi',
                              create_user=create_user,
                              salt_url='https://192.168.1.211:8000',
                              salt_user='saltapi',
                              salt_pwd='saltapipwd',
                              eauth='pam',
                              trust_host=True)
        self.stdout.write('SaltTb重建完成。')

    # 新建一個環(huán)境
    def add_env(self):
        Env.objects.all().delete()
        print('delete all Env data')
        create_user = User.objects.get(username=username)
        salt = SaltTb.objects.order_by('?').first()
        env_list = ['dev', 'prd']
        for index, env in enumerate(env_list):
            Env.objects.create(name=env,
                               description=env,
                               create_user=create_user,
                               env_id=index,
                               salt=salt)
        self.stdout.write('Env重建完成。')

    # 新建demo項目
    def add_project(self):
        Project.objects.all().delete()
        print('delete all Project data')
        create_user = User.objects.get(username=username)
        project_name_list = ['User', 'Service', 'Store', 'Card', 'Support']
        project_cn_name_list = ['用戶管理', '服務(wù)', '庫存', '購物車', '客服']
        for project_name, project_cn_name in zip(project_name_list, project_cn_name_list):
            Project.objects.create(name=project_name,
                                   description=project_name,
                                   cn_name=project_cn_name,
                                   create_user=create_user,
                                   project_id=random.randint(1000, 10000))
        self.stdout.write('Project重建完成。')

    # 新建demo應(yīng)用
    def add_app(self):
        App.objects.all().delete()
        print('delete all App data')
        create_user = User.objects.get(username=username)
        app_name_list = ['User-Login', 'Service-724', 'Store-Address', 'Card-Adjust', 'Support-Admin', 'go-demo']
        app_cn_name_list = ['用戶登陸', '全天服務(wù)', '庫存地址', '購物車調(diào)配', '客服后管', '畢方演示go示例']
        for app_name, app_cn_name in zip(app_name_list, app_cn_name_list):
            git = GitTb.objects.order_by('?').first()
            project = Project.objects.order_by('?').first()
            App.objects.create(name=app_name,
                               description=app_name,
                               cn_name=app_cn_name,
                               create_user=create_user,
                               app_id=random.randint(10000, 100000),
                               git=git,
                               git_app_id=1,
                               git_trigger_token='559fbd3381bc39100811bd00e499a7',
                               project=project,
                               build_script='script/build.sh',
                               deploy_script='script/deploy.sh',
                               zip_package_name='go-demo.tar.gz',
                               service_port=9090,
                               service_username='sky',
                               service_group='operate')
        self.stdout.write('App重建完成。')

    # 新建server服務(wù)器
    def add_server(self):
        Server.objects.all().delete()
        print('delete all Server data')
        create_user = User.objects.get(username=username)
        for number in range(100):
            ip = '192.168.1.{}'.format(number)
            app = App.objects.order_by('?').first()
            Server.objects.create(name=ip,
                                  description=ip,
                                  create_user=create_user,
                                  ip=ip,
                                  port=random.randint(10000, 100000),
                                  app=app,
                                  system_type=random.choice(['WINDOWS', 'LINUX']))
        self.stdout.write('Server重建完成。')

    # 新建發(fā)布單狀態(tài)
    def add_release_status(self):
        ReleaseStatus.objects.all().delete()
        print('delete all ReleaseStatus data')
        create_user = User.objects.get(username=username)
        status_list = ['Create', 'Building', 'BuildFailed', 'Build', 'Ready', 'Ongoing', 'Success', 'Failed']
        status_value_list = ['創(chuàng)建', '編譯中', '編譯失敗', '編譯成功', '準備部署', '部署中', '部署成功', '部署失敗']
        for status_name, status_value_name in zip(status_list, status_value_list):
            ReleaseStatus.objects.create(name=status_name,
                                         description=status_name,
                                         create_user=create_user,
                                         status_value=status_value_name)
        self.stdout.write('ReleaseStatus重建完成。')

    # 新建demo發(fā)布單
    def add_release(self):
        Release.objects.all().delete()
        print('delete all Release data')
        create_user = User.objects.get(username=username)
        for number in range(100):
            app = App.objects.order_by('?').first()
            env = Env.objects.order_by('?').first()
            deploy_status = ReleaseStatus.objects.order_by('?').first()
            random_letter = ''.join(random.sample(string.ascii_letters, 2))

            name = datetime.datetime.now().strftime("%Y%m%d%H%M%S%f") + random_letter.upper()
            Release.objects.create(name=name,
                                   description=name,
                                   create_user=create_user,
                                   app=app,
                                   env=env,
                                   git_branch='master',
                                   pipeline_id=0,
                                   pipeline_url='http://www.demo.com',
                                   deploy_script_url='http://192.168.1.213:8080/a/b/bifang.sh',
                                   zip_package_url='http://192.168.1.213:8080/a/b/go-demo.zip',
                                   deploy_status=deploy_status)
        self.stdout.write('Release重建完成。')

    # 新建權(quán)限
    def add_action(self):
        Action.objects.all().delete()
        print('delete all Action data')
        create_user = User.objects.get(username=username)
        Action.objects.create(name='Create',
                              description='創(chuàng)建編譯權(quán)限',
                              create_user=create_user,
                              action_id=100)
        Action.objects.create(name='Env',
                              description='環(huán)境權(quán)限',
                              create_user=create_user,
                              action_id=1000)
        Action.objects.create(name='Deploy',
                              description='部署權(quán)限',
                              create_user=create_user,
                              action_id=10000)
        self.stdout.write('Action重建完成。')

    # 新建demo應(yīng)用權(quán)限用戶表
    def add_permission(self):
        Permission.objects.all().delete()
        print('delete all Permission data')
        create_user = User.objects.get(username=username)
        for number in range(5):
            app = App.objects.order_by('?').first()
            action = Action.objects.order_by('?').first()
            pm_user = User.objects.order_by('?').first()
            name = '{}-{}-{}'.format(app.name, action.name, pm_user.username)
            Permission.objects.create(name=name,
                                      description=name,
                                      create_user=create_user,
                                      app=app,
                                      action=action,
                                      pm_user=pm_user)
        self.stdout.write('Permission重建完成。')

  • 使用(venv) D:\Code\bifang\bifangback>python manage.py mockdb自定義命令,可把這些mock數(shù)據(jù)導(dǎo)入數(shù)據(jù)庫。
  • 這里也使用了不同django orm和Python隨機選擇的技巧。

Look Django Admin

2021-01-03 14_51_03-Cmdb 管理 _ 登錄畢方(BiFang)系統(tǒng)后臺.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容