CMDB 資產管理
環境 : Linux 7.4 Django 1.11+ Python 3.6
CMDB
代碼的發布/監控/堡壘機
資產管理。
- 代替execl,將execl表里的內容導入我們系統中
- 和其他系統交互 通過 client端進行采集數據。salt-stack/ ansible/ 阿里雲封裝的API
后台管理系統需要一個接受工具層發送的數據的邏輯(API),CMDB最重要的就是選擇采集工具的功能
每天的數據更改需要記錄日志,如果沒變化就不記錄,變化及記錄日志,放入定時任務里。
1.針對雲服務 -> 包括以下這些內容
硬件類型, --> 服務器,交換機
環境, --> 測試,上線,灰度 等到
運行的應用, --> nginx, apache,docker, 等等
來源, --> 阿里雲,騰訊雲,物理機
上線狀態, --> 上線,關機, 下線,待機
內網IP, --> 內網IP
外網IP, --> 外網IP
主機名, --> hostname
內存, --> memcache
CPU, --> CPUINFO
硬盤, --> DISKS
內核, --> 內核版本
操作系統, --> OS:centos,Redhat,Debian,
ecs_name, --> 雲主機的標識
標識名, --> 標識名
區域/城市, --> 華南,華北,東北 等
機房/機櫃 --> 機房/機櫃 屬性
這些內容都是作為models.py里的表信息
class Host(models.Model): # 最基礎的主機表 '''主機,阿里雲eth0 內網網卡, eth1 公網網卡''' hostname = models.CharField(max_length=64, blank=True, null=True, verbose_name='主機名') ecsname = models.CharField(max_length=64, blank=True, null=True, verbose_name='實例名') login_port = models.CharField(max_length=16, default='22', blank=True, null=True, verbose_name='登錄端口') cpu = models.CharField(max_length=8, blank=True, null=True, verbose_name='CPU') mem = models.CharField(max_length=8, blank=True, null=True, verbose_name='內存') speed = models.CharField(max_length=8, blank=True, default='5', null=True, verbose_name='帶寬') eth1_network = models.CharField(max_length=32, blank=True, null=True, verbose_name='公網IP') eth0_network = models.CharField(max_length=32, verbose_name='私網IP') sn = models.CharField(max_length=64, blank=True, null=True, verbose_name='SN') kernel = models.CharField(max_length=64, blank=True, null=True, verbose_name='內核版本') # 內核+版本號 remarks = models.CharField(max_length=2048, blank=True, null=True, verbose_name='備注') createtime = models.CharField(max_length=32, blank=True, null=True, verbose_name='創建時間') expirytime = models.CharField(max_length=32, blank=True, null=True, verbose_name='到期時間') #ForeignKey lab = models.ForeignKey(to='Lable',default=1,blank=True, null=True, verbose_name='標簽') os = models.ForeignKey(to='Os',default=1,blank=True, null=True, verbose_name='操作系統') # os+版本號 # the_upper = models.ForeignKey(to='Host', blank=True, null=True, verbose_name='宿主機', related_name='upper') source = models.ForeignKey(to='Source',default=1,blank=True, null=True, verbose_name='來源IP') # ManyToManyField logining = models.ManyToManyField(to='Login',default=1, verbose_name='所屬用戶') disks = models.ManyToManyField(to='Disk',default=1, verbose_name='磁盤') #這個字段只運行再內存里。這些信息不占磁盤的信息。 state_choices = ( (1, 'Running'), (2, '下線'), (3, '關機'), (4, '刪除'), (5, '故障'), ) state = models.SmallIntegerField(verbose_name='主機狀態', choices=state_choices, blank=True, null=True, ) def __str__(self): return self.hostname class Meta: verbose_name_plural = "主機表"
管理系統 list
注意 手動創建APP 時,是不會自動創建url.py文件的,需要手動來創建
form django.views import View class List(View): def post(self, *args, **kwargs): pass def get(self, *args, **kwargs): # 這里的 queryset_list 可以切分來取值。數據太多需要分頁,分頁也是用到這種方式 host_list = models.Host.object.all() return render(request, 'host.html', locals())
通過 template 模板渲染可以將 queryset_list內的數據點出來
比如{{ host_list.hostname }}-> 數據就出來了
models.py
class 里面定義的__str__方法,
當你查詢使用的時候不用點字段信息
def __str__(self): return self.hostname
直接輸出時,直接return指定字段信息為hostname的屬性
關於 取state_choices的值。在template里的新寫法get_state_display即可
管理系統增改刪
class Add(View): def post(self, *args, **kwargs): pass def get(self, *args, **kwargs): # 這里的 queryset_list 可以切分來取值。數據太多需要分頁,分頁也是用到這種方式 host_list = models.Host.object.all() return render(request, 'host.html', locals()) class Delete(View): def post(self, *args, **kwargs): id = request.GET.get('id') # 通過get ID 來直接刪除你需要刪除的主機 delete = models.Host.object.filter(id=int(id)).delete() return render(request,'host.html',locals()) def get(self, *args, **kwargs): return render(request, 'host.html', locals())
基於FORM 表單來做渲染
class Update(View): # POST def post(self, request, pk): #request 后面的值 pk 是 url傳入的值,類似於 ->url 里 host.id print (pk) #這里傳入的就是ID 值 form = form_class.HostForm(data=request.POST) if form.is_valid(): # 驗證信息 print(form.cleaned_data) models.Host.object.filter(id=pk).update(**form.cleaned_data) #修改的信息 # print ('提交正常') return redirect('/host/list') else: print (form.errors) return render(request, 'edit.html',locals()) #這里是錯誤信息 # GET def get(self, request,pk): # get_id = int(request.GET.get('id')) obj = models.Host.objects.filter(id=pk).first() # queryset 類型 通過ID 取值 # obj = models.Host.objects.filter(id=get_id) # queryset_list 類型,但是只有一個值 form = from_class.HostForm( #這里可以渲染到前端的內容, initial = {'hostname': obj.hostname, # initial 固定寫法!這里的值是直接渲染到前端的 'ecsname':obj.ecsname, #需要增加或者刪除都修改這里 'cpu':obj.cpu, 'mem':obj.mem, 'speed':obj.speed, 'network':obj.network, 'source_id':obj.source_id, 'region_id':obj.region_id, } )
通過上面的initial 的內容傳入template模板渲染出
<form method='post' role='form' > {% csrf_token %} <p id='hsotname'>阿里主機名: {{form.hostname}} {{form.errors.hostname.0}}</p> # 對應上面的 initial 內的值 <p id='ecsname'>實例ID: {{form.ecsname}} {{form.errors.ecsname.0}}</p> <p id='cpu'>CPU: {{form.cpu}} {{form.errors.cpu.0}}</p> <p id='mem'>內存/G: {{form.mem}} {{form.errors.mem.0}}</p> <p id='speed'>帶寬/M: {{form.speed}} {{form.errors.speed.0}}</p> <p id='network'>IP: {{form.network}} {{form.errors.network.0}}</p> <p id='source'>來源類型: {{form.source_id}} {{form.errors.source_id.0}}</p> <p id='region'>所屬區域: {{form.region_id}} {{form.errors.region_id.0}}</p> # 還沒增加 M2M 類型的 <input type='submit' value='提交' </form>
管理系統 (后台):
增刪改查-> form ---> 完成
樣式使用 bootstrap --> 最后寫
form-M2M ---> 最后寫
分頁 ---> 數據多了要使用分頁
form 表單問題
from django.forms import Form from hc import models class HostForm(Form): hostname = fields.CharField( required = True, # error_messages = ['required':'不能為空'], widget = widgets.TextInput=(attrs={'class':'form-control'}) ) ecsname = fields.CharField( required = True, # error_messages = ['required':'不能為空'], widget = widgets.TextInput=(attrs={'class':'form-control'}) # 加樣式是 通過 form-control 修改 ) cpu = fields.CharField( required = True, # error_messages = ['required':'不能為空'], widget = widgets.TextInput=(attrs={'class':'form-control'}) ) mem = fields.CharField( required = True, # error_messages = ['required':'不能為空'], widget = widgets.TextInput=(attrs={'class':'form-control'}) ) speed = fields.CharField( required = True, # error_messages = ['required':'不能為空'], widget = widgets.TextInput=(attrs={'class':'form-control'}) ) network = fields.CharField( required = True, # error_messages = ['required':'不能為空'], widget = widgets.TextInput=(attrs={'class':'form-control'}) ) # 一對多關系 的問題 source_id = fields.CharField( # 一對多的 source_id 字段不加_id 時會報錯 required = True, # 因為 fields.CharField 渲染的input的框是,輸入的都是str()類型,ID 又是唯一的所有這里 choices = [], # 所有插數據的時候 str() 和int() 數據類型不一樣會報錯 # choices = models.Sourec.objects.values_list('id','name'), # values_list這里取到的還是int() # values_list 通過這個方法可以拿到元組 即 ('id','name') # form.cleaned_data['source'] = int(form.cleaned_data['source']) # 可以這么理解。但是最好別這樣用,一對多數據多了的時候 就很麻煩 # 所有最好的方法就是 在source 后面 加個 _id 即可 -> source_id 插入數據時就不會報錯 widget = widgets.Select={'class':'form-control'} ) region_id = fields.CharField( required = True, choices = [], widget = widgets.Select={'class':'form-control'} ) # 初始化方法 -> 每次實例化時都要執行這個 def __init__(self, *args, **kwargs): super(HostForm, self).__init__(*args, **kwargs) # 先執行 父類的 __init__方法,也就是 View類的__init__方法 self.fields['source_id'].choices = models.Sourec.objects.values_list('id','name') self.fields['region_id'].choices = models.Region.objects.values_list('id','name') # 這個 self.fields 值 是執行父類初始化__init__時產生的,會把當前這個子類的所有字段值,當成屬性做了次深度copy。 # 成一個字典,然后用過切片取值。重新將.choices 賦值,賦值的是后面一對多查表的值。 #這種方法 不用重啟服務,當DB數據更新時,也會自動刷入表單渲染
form 表單問題 2
template
<form method='post' novalidate role='form' > # novalidate 是不希望瀏覽器自動渲染出樣式,加了后就可以顯示自定義的樣式 {% csrf_token %} <p id='hsotname'>阿里主機名: {{form.hostname}} {{form.errors.hostname.0}}</p> # 對應上面的 initial 內的值 <p id='ecsname'>實例ID: {{form.ecsname}} {{form.errors.ecsname.0}}</p> # 點0 是取列表的第一個值 <p id='cpu'>CPU: {{form.cpu}} {{form.errors.cpu.0}}</p> #錯誤信息直接都集成到 form里面了 <p id='mem'>內存/G: {{form.mem}} {{form.errors.mem.0}}</p> <p id='speed'>帶寬/M: {{form.speed}} {{form.errors.speed.0}}</p> <p id='network'>IP: {{form.network}} {{form.errors.network.0}}</p> <p id='source'>來源類型: {{form.source_id}} {{form.errors.source_id.0}}</p> <p id='region'>所屬區域: {{form.region_id}} {{form.errors.region_id.0}}</p> # 還沒增加 M2M 類型的 <input type='submit' value='提交' </form>
cmdb流程小結
API 等到 客戶端完成了再說
API
- 入庫。
- 客戶端上傳的格式進行解析。
按鈕
和add 操作一樣。url去獲取工具拿到的數據
客戶端
底層 -> 封裝 + 結合 -> 優化
客戶端底層模式執行方式講解
yum 安裝 salt-stack
客戶端:
底層:
服務端 控制salt-master : 兩種方案 # 一般用第二種,別人封裝好的API 用起來就很方便 1:CMDB 服務端和 salt-master permiko 模塊 -> 執行 salt-master 命令 --> 然后獲取結果 2:salt-master 使用源生的salt-api CMDB 服務端 -> requests模塊(POST.GET)請求salt-api 執行需要的命令 salt-api 就是 ip:port 的形式 salt-api 必須加 安全認證 token,提高安全性
salt-stack 安裝
salt-stack
官網下載最新版本
http://www.cnblogs.com/onda/p/7929609.html
yum -y install salt-stack 要通過官網的最新包去安裝
salt-stack
yum 安裝的路徑 /etc/salt
-
master -> 部署 服務端 1次
-
api -> 部署 服務端 1次
-
minion -> 部署 客戶端 很多次
首先去官網選擇 下載的版本。
https://repo.saltstack.com/#rhel
根據現有的系統判斷出 我需要的使用的版本
centos 6 + py2 yum install -y https://repo.saltstack.com/yum/redhat/salt-repo-latest-2.el6.noarch.rpm yum clean expire-cache yum install -y salt-master salt-minion salt-ssh salt-syndic salt-cloud salt-api service salt-minion restart
centos 7 + py 2 官網地址 yum install -y https://repo.saltstack.com/yum/redhat/salt-repo-latest-2.el7.noarch.rpm yum install -y salt-master salt-minion salt-ssh salt-syndic salt-cloud salt-api systemctl restart salt-minion
master 的IP是: 192.168.1.8 master 基本不用動
操作 minion端
vim /etc/salt/minion
# Set the location of the salt master server. If the master server cannot be # resolved, then the minion will fail to start. #master: salt master: 192.168.1.8 # : 冒號后面必須有空格 不然報錯 #id: #這個ID 很重要 記住,不允許重復! id: cmdb_test
保存后
啟動服務systemctl restart salt-minion
salt-key -L # 查看所有客戶端的 ID salt-key -D # 刪除所有授權用戶 -d 指定 salt-key -A # 授權所有客戶端 salt 'cmdb_test' cmd.run 'df -h' # 讓所有客戶端執行`df -h`命令 #'cmdb_test' 這里是通過ID 控制, 支持正則 salt '*' test.ping # ping 命令測試是否連通
salt--master 和 salt-minion
控制端 被控制端
通過 salt-api 訪問 salt-master 來控制salt-minion 執行 命令 返回結果
服務端監聽4505和4506兩個端口,4505為消息發布的端口,4506為和客戶端通信的端口
總結
1. 總結 學會看報錯 -> salt-minion debug 或者 systemctl status salt-minion -l
2. 通信不正常 都是秘鑰的問題,不行就刪掉秘鑰重啟自動生成密碼。
ID 也是對應秘鑰的,ID不一致也會影響秘鑰的正確性
3. 秘鑰位置 /etc/salt/pki/ 不開心就刪刪刪 rm -rf /etc/salt/pki/*
4. 關掉你的防火牆 和 selinux 否則也會影響正常的通信
master --- API 安裝
yum install -y salt-master
yum install -y salt-api pyOpenSSL
# pip install salt-api --> yum 裝過了salt-api , pip 就不用了 pip install cherrypy==3.2.3 cd /etc/pki/tls/certs/ make testcert --> 密碼要記住 '123123' 是用這個密碼來獲取 token的 -->設置秘鑰密碼,(3次) ,剩下回車 cd ../private/ openssl rsa -in localhost.key -out localhost_nopass.key chmod 755 /etc/pki/tls/certs/localhost.crt chmod 755 /etc/pki/tls/private/localhost.key chmod 755 /etc/pki/tls/private/localhost_nopass.key useradd -M -s /sbin/nologin saltapi passwd saltapi sed -i '/#default_include/s/#default/default/g' /etc/salt/master mkdir -p /etc/salt/master.d cd /etc/salt/master.d
vim api.conf
rest_cherrypy:
port: 8001 # 這個端口可以 改, 需要重啟 salt-api ssl_crt: /etc/pki/tls/certs/localhost.crt ssl_key: /etc/pki/tls/private/localhost_nopass.key
vim eauch.conf
# 注意空格 external_auth: pam: saltapi: # 用戶名 - .* # 該配置文件給予saltapi用戶所有模塊使用權限,出於安全考慮一般只給予特定模塊使用權限 - '@runner' - '@wheel'
systemctl restart salt-master
systemctl start salt-api
netstat -tlnp #查看 8001 端口是否啟動,啟動了證明 運行正常 Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:4505 0.0.0.0:* LISTEN 1153/python tcp 0 0 0.0.0.0:4506 0.0.0.0:* LISTEN 1159/python tcp 0 0 0.0.0.0:8001 0.0.0.0:* LISTEN 1340/python
python調用salt-api執行命令
python調用salt-api執行命令
import json
salt_api = 'https://192.168.1.8:8001' class SaltApi: def __init__(self, url): # 初始化 參數 self.url = url self.username = 'saltapi' # 設置的用戶名和密碼 salt-api的 self.password = '123213' self.headers = { #headers 寫死的 請求的頭部 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0' 'Content-type': 'application/json' # 固定的寫法,Agent的瀏覽器參數 } self.params = {'client':'local', 'fun':'', 'tgt':''} # 固定key值 self.login_url = salt_api + 'login' # 拼接 登錄 self.login_params = {'username': self.username, 'password': self.password, 'eauth': 'pam'} # 登錄的字典信息,參數的封裝 self.token = self.get_data(self.login_url, self.login_params).get('token') # 這是個字典還可以切片取值['token']。 推薦get方式取值,取不到只有空,不會報錯。切片取值就能獲取到報錯信息 # token 值 調用get_data 取值,傳過去了 url 和 登錄信息相關的參數 self.headers['X-Auth-Token'] = self.token def get_data(self, url, params): # get 數據 + 格式轉換 send_data = json.dumps(params) # 先將字典 通過json 變成 str格式 request = requests.post(url, data=send_data, headers=self,headers, verify=False) #發送請求 response = request.json() # 將返回的數據 字符串 str 轉成 json格式 給 response result = dict(response) # 再轉換成字典 return result['retrun'][0] #最好return出去需要的值 def salt_command(self, tat, method, arg=None): #執行發送請求, 傳入的時候有地三個參數就傳arg,沒有就none if arg: #params = {'client': 'local', 'fun': method, 'tgt': tgt, 'arg':arg, 'expr_form':'list'} params = {'client': 'local', 'fun': method, 'tgt': tgt, 'arg':arg,} else: params = {'client': 'local', 'fun': method, 'tgt': tgt} #這是salt的規則,必須是這樣 result = self.get_data(self.url, params) # 往 get_data方法里傳入兩個值,url和params 的字典 return result def main(): salt = SaltApi(salt_api) #實例化對象 salt_client = 'hc-02' # 這里是 minion端的ID 多個值可以是 ['*'] selt_test = 'test.ping' # 這是 測試主機連通性,是salt執行的命令 #selt_test = 'grains.items' # 這個 items 抓了很多數據 selt_method = 'grains.get' # 抓主機信息 #salt_method = 'cmd.run' # 執行shell命令 #salt_method = 'disk.usage' # salt 'hc-02' disk.usage 取磁盤信息 #salt_method = 'svn.update' # 要使用SVN的模塊 salt 'hc-02' svn.update selt_params = ['ip_interfaces', ] # ip網卡信息 #salt_params = ['ip_interfaces', 'hwaddr_interfaces'] # 取 IP網卡 和 MAC地址 #salt_params = 'free -m' # salt 'hc-02' cmd.run 'free -m' 取內存信息 #salt_params = ['free -m', ] #salt_params = ['/', '/data/www', 'root', ] result1 = salt.salt_command(salt_client, salt_test) # 傳入兩個值, 一個是minion的ID值 'hc-02' ,二個是salt執行的命令 'test.ping' print (result1) # result1 傳入的是 test.ping ,返回的是命令的返回值 result2 = salt.salt_command(salt_client, salt_method, salt_params) print (result2) # result2 傳入的是 grains.get ip_interfaces ,返回的我主機的ip網卡信息 if __name__ == '__main__': main()
測試命令 salt 'hc-03' grains.get ip_interfaces
[root@hc master.d]# salt 'hc-03' grains.get ip_interfaces hc-03: ---------- eth0: - 192.168.1.15 eth1: lo: - 127.0.0.1 # 做了橋接網卡的 需要特殊處理 [root@hc master.d]# salt 'hc-02' grains.get ip_interfaces hc-02: ---------- br0: - 192.168.1.9 - fe80::ca60:ff:fe8e:6f93 eth0: - fe80::ca60:ff:fe8e:6f93 eth1: lo: - 127.0.0.1 - ::1 vnet0: - fe80::fc54:ff:fe1d:dab6 #返回的我主機的ip網卡信息
importlib反射
唯一標識問題
IP 和 minion 端的ID 對應
minion 端的ID 是在master里 是唯一的
salt 'hc-02' grains.items -> 返回的是一個字典。
通過 grains.get key 取到KEY的值
salt 'hc-02' grains.get ip_interfaces -> 通過 grains.get -> key 是 ip_interfaces 取到KEY的值
satl 'hc-02' cmd.run 'ls /root' -> 通過執行shell命令執行
中國SaltStack用戶組 網站 學習使用salt-stack 命令
http://www.saltstack.cn/
面向對象python2-3的區別
-
python 2 類 不寫 object 就是 經典類 ,寫 object 就是 新式類
-
python 3 類 寫不寫object 都是 新式類
-
他們不一樣的地方在 繼承時候的順序不一樣
優先級 深度優先 和 廣度優先
python 2 類
- 不繼承 object 經典類 -> 深度優先
- 繼承 object 新式類 -> 並非廣度優先 通過C3算法 變成類似於廣度優先
python 3 類 : 無論繼不繼承 object 都是 新式類
-
新式類,繼承優先級:c3算法,非即從左到右,去繼承
-
所有不管是python2 還是python3 寫類的時候都要寫 object
importlib 反射代碼
import importlib
import os, sys
Host_func_dic = {
'disk': 'func.hosts.disk.Disk', 'cpu': 'func.hosts.cpu.Cpu', 'mem': 'func.hosts.mem.Men', } # 寫個字典 path = Host_func_dic.get('disk') #path == 'func.hosts.disk.Disk' module_path, class_name = path.resplit('.', maxsplit=1) # resplit右邊開始分割,只分割1次, print ( module_path ) -> func.hosts.disk print ( class_name ) -> Disk module = importlib.import_module(module_path) # 這里相當於 form ... import # from func.hosts import disk 上的代碼執行完 就是這樣的 導入 # module 實質上就是導入了這個模塊 disk_class = getattr(module, class_name) # getattr 反射 還有 hasattr反射判斷 setattr 反射賦值 delattr 反射刪值 # disk_class = getattr(disk, 'Disk') #getattr 反射的作用就是, # 這里的module 就是具體導入的文件名, disk #如果在disk文件里或者這個變量里,有后面這個字符串'Disk'的內容,就會反射出來這個'Disk', # Disk是可以當命令來執行的 JG = disk_class() #實例化 JG.run() # 執行這個對象方法
1. 這樣 就是批量執行的方法
2. 基於 importlib 和 getattr 方法
3. 把字符串 通過一定的格式用 importlib 導入,再用getattr 反射取值的方法 那對對應的類名。
4. 最后通過實例化對象 即可執行 他們統一的方法!
import importlib import os, sys Host_func_dic = { 'disk': 'func.hosts.disk.Disk', 'cpu': 'func.hosts.cpu.Cpu', 'mem': 'func.hosts.mem.Men', 'ip': 'func.hosts.ip.IP', } # 寫個字典 #path = Host_func_dic.get('disk') # path == 'func.hosts.disk.Disk' host_list = ['cpu', 'disk', 'mem', 'ip'] for i in host_list: path = Host_func_dic.get( i ) module_path, class_name = path.resplit('.', maxsplit=1) # resplit右邊開始分割,只分割1次, module = importlib.import_module(module_path) # 這里相當於 form ... import # from func.hosts import disk 上的代碼執行完 就是這樣的 導入 # module 實質上就是導入了這個模塊 disk_class = getattr(module, class_name) # getattr 反射 還有 hasattr反射判斷 setattr 反射賦值 delattr 反射刪值 # disk_class = getattr(disk, 'Disk') # getattr 反射的作用就是, # 這里的module 就是具體導入的文件名, disk # 如果在disk文件里或者這個變量里,有后面這個字符串'Disk'的內容,就會反射出來這個'Disk', # Disk是可以當命令來執行的 JG = disk_class() #實例化 JG.run() # 執行這個對象方法 ,執行他們通用方法,方法必須統一
客戶端
客戶端批量執行實例
settins.py
# 把配置文件,同一放在一個文件中,好做修改, Host_func_dic = { 'disk': 'func.hosts.disk.Disk', 'cpu': 'func.hosts.cpu.Cpu', 'mem': 'func.hosts.mem.Men', 'ip': 'func.hosts.ip.IP', } # 寫個字典 #path = Host_func_dic.get('disk') # path == 'func.hosts.disk.Disk' host_list = ['cpu', 'disk', 'mem', 'ip']
demo_run.py
import importlib improt os, sys # 將文件加入環境變量, BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(BASEDIR) #再 from ... import ... 導入 form settings import Host_func_dic , host_list for i in host_list: path = Host_func_dic.get( i ) module_path, class_name = path.resplit('.', maxsplit=1) # resplit右邊開始分割,只分割1次, module = importlib.import_module(module_path) # 這里相當於 form ... import # from func.hosts import disk 上的代碼執行完 就是這樣的 導入 # module 實質上就是導入了這個模塊 disk_class = getattr(module, class_name) # getattr 反射, hasattr反射判斷,setattr 反射賦值,delattr 反射刪值 # disk_class = getattr(disk, 'Disk') #getattr 反射的作用就是, # 這里的module 就是具體導入的文件名, disk #如果在disk文件里或者這個變量里,有后面這個字符串'Disk'的內容 #就會反射出來這個'Disk',Disk是可以當命令來執行的 JG = disk_class() #實例化 JG.run() # 執行這個對象方法 ,執行他們通用方法,方法必須統一
func/main.py
# 做總入口繼承,當沒有run方法時,直接報錯 Class Main(object): def __init__(self) self.method = '' self.tgt = '' selg.arg = '' def run(self): #當沒有run方法時,直接報錯 raise KeyError('this class not run functions') #raise NotImplementedError('this class not run functions') def windows(self): pass def linux(self): pass
func/mem.py
# 下面的都做salt命令的拼接,並return出去 from func import Main Class Mem(Mian) def run(self) self.method = 'cmd.run' self.tgt = 'hc-02' self.agr = 'free -m' #print ('run...mem') retrun {'client':'local', 'func':self.method, 'tgt':self.tgt, 'arg':self.arg}
func/cpu.py
from func import Main
Class Cpu(Mian)
def run(self) self.method = 'cmd.run' self.tgt = 'hc-02' self.agr = 'cat /proc/cpuinfo' #print ('run...cpu') retrun {'client':'local', 'func':self.method, 'tgt':self.tgt, 'arg':self.arg}
func/disk.py
from func import Main
Class Disk(Mian)
def run(self) self.method = 'cmd.run' self.tgt = 'hc-02' self.agr = 'df -h' #print ('run...disk') retrun {'client':'local', 'func':self.method, 'tgt':self.tgt, 'arg':self.arg}
func/ip.py
from func import Main
Class Ip(Mian)
pass
def run(self) self.method = 'cmd.run' self.tgt = 'hc-02' self.agr = 'ip addr' #print ('run...ip') retrun {'client':'local', 'func':self.method, 'tgt':self.tgt, 'arg':self.arg}
客戶端請求數據流程圖
后續還要不斷優化代碼
到時再更新.....
