1. 程式人生 > >Horizon 原始碼閱讀(二)—— Horizon 模組註冊機制

Horizon 原始碼閱讀(二)—— Horizon 模組註冊機制

一、寫在前面

這篇文章主要介紹一下OpenStack Horizon — juno專案模組註冊機制,本文將通過對Horizon原始碼解析瞭解各個模組註冊載入機制。全文穿插了Horizon組建的程式碼塊以及個人理解註釋,由於能力和時間有限,錯誤之處在所難免,歡迎指正!

       如果轉載,請保留作者資訊。
       郵箱地址:[email protected]

二、Horizon模組註冊機制

       Horizon/base.py中實現了一套dashboard/panel註冊、動態載入機制,使得Horizon面板上所有的dashboard都是”可插拔”的,所有的panel都是”動態載入”的,在以下的內容裡將通過原始碼解析具體分析horizon/base.py檔案,揭開這神祕的面紗。

       流程圖: 

       

整個Horizon模組的載入流程如上圖所示,horizon專案在引入的時候自動通過horizon.__init__()自動Load類屬性方法def_lazy_urls(self),載入LazyURLPattern(url_patterns)其中url_patterns是一個巢狀方法,LazyURLPattern繼承自Django SimpleLazyObject類,個人理解這是把_lazy_urls()方法轉換成一個懶惰方法,這裡只加載不執行,等正真用到的時候在執行。

       當用戶通過Request請求訪問的時候,根據Django Urls對映機制,會去openstack_dashboard.urls.py匹配,urls.py檔案中url(

r’', include(horizon.urls)),觸發執行_lazy_urls()方法。_lazy_urls()方法返回def_urls(self)處理結果的第一個引數urlpatterns,整個Horizon模組的註冊Urls編譯都是在這個方法內完成,通過_autodiscover()方法,從“settings.INSTALLED_APPS發現模組,包含dashboard.py檔案的模組進行註冊,並新增到self._registry登錄檔中,然後通過迴圈遍歷登錄檔,呼叫每個註冊dashboard的_autodiscover()方法,註冊每個dashboard下面的panel,完成整個horizon模組的註冊,最終返回一個urlpatterns值,urls匹配呼叫相應的views模組。

 注意點:

          1、settings.INSTALLED_APPS在這裡拉出來講解一下,settings.INSTALLED_APPS是一個字串tuple ,內容是專案中引入的應用,在Horizon專案中有兩部分組成:

第一部分:settings.py

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
INSTALLED_APPS = ['openstack_dashboard', 'django.contrib.contenttypes', 'django.contrib.auth', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.humanize', 'django_pyscss', 'openstack_dashboard.django_pyscss_fix', 'compressor', 'horizon', 'openstack_auth',]
horizon(2-1).py

第二部分:settings.py

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
# Load the pluggable dashboard settingsimport openstack_dashboard.enabledimport openstack_dashboard.local.enabledfrom openstack_dashboard.utils import settingsINSTALLED_APPS = list(INSTALLED_APPS) # Make sure it's mutablesettings.update_dashboards([ openstack_dashboard.enabled, openstack_dashboard.local.enabled,], HORIZON_CONFIG, INSTALLED_APPS)
horizon(2-2).py

openstack_dashboard.enabled/openstack_dashboard.local.enabled:配置啟用和顯示Daishboard,用來配置Horizon左側導航顯示的dashboard。自己開發一個dashboard需要在openstack_dashboard/enabled 新增一個類似 _50_mydashboard.py檔案,這樣才會被注入到settings.INSTALLED_APPS配置項中。整個settings.INSTALLED_APPS中引入的APP由這兩部分組拼在一起。

2、每個dashboard模組下都有一個dasbboard.py檔案裡面定義了屬於當前dashboard的PanelGroup,和各個PanelGroup下的Panel,在Horizon模組被匯入的時候會去依次遍歷Dasboard→PanelGroup→Panel,所有的dashboard註冊到Horizon名稱空間下,各個panel註冊到自己的dashboard名稱空間下。

三、Horizon模組註冊原始碼解析

       接下去剛通過分析Juno 中Horizon關於這部分的原碼來講解。

       細粒度的資料流圖: 

       

上圖是關於Horizon中關於模組註冊的資料流圖。

1)horizon匯入

settings.py-> 匯入horizon

      INSTALLED_APPS = [
                                       ‘horizon',

                                        ……
       ]

Python中在匯入一個包時,實際上匯入了它的__init__.py檔案,當我們匯入Horizon這個包的時候,__init__.py檔案自動執行,在__init__.py 檔案中再匯入其他的包,或者模組。其中在horizon包的__init__.py檔案中:

    from horizon.baseimport Horizon  # noqa

    ……
    urls = Horizon._lazy_urls

    ……

匯入了Horizon.base._lazy_urls方法,_lazy_urls()是完成整個模組註冊的入口,Horizon.base._lazy_urls方法增加了@property, @property 可以將python定義的函式“當做”屬性訪問。__init__.py將Horizon.base._lazy_urls匯入的時候自動執行該方法體。

注:horizon提供的Site類、Dashboard類、Panel類,負責整個基本架構,實現單例的設計模式,全域性只有一個只有一個horizon物件。 對於這個檔案程式碼架構的分析放到以後。

       
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
@propertydef _lazy_urls(self): """ 延遲載入URL模式。 這種方法避免試圖載入URLconf值之前設定模組已經載入的問題。 """ def url_patterns(): return self._urls()[0] # LazyURLPattern(url_patterns) 猜測這是一個懶惰方法,load的時候把方法傳入,當需要使用的使用才執行方法。 # request 請求進來的時候,回去Openstack.urls.py 進行配置,include('openstack_auth.urls')這個時候會執行url_patterns() # self.namespace = "horizon" # self.slug = "horizon" return LazyURLPattern(url_patterns), self.namespace, self.slug
horizon(2-4).py

其中LazyURLPattern(url_patterns)沒有繼續執行下去,LazyURLPattern()繼承自Djano SimpleLazyObject理解成將一個方法轉換成延遲執行的方法,只有正真呼叫的時候才會正真執行,顯然這裡沒有正真呼叫,這裡只是一個載入,url_patterns作為一個方法引用當作引數。

2)ruquest請求

      當用戶通過瀏覽器發起request請求,根據Django的框架結構,使用URLconf進行連線請求和後端處理(view)的繫結,使用view進行後端處理,使用template進行頁面渲染。

     
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
urlpatterns = patterns( '', # 匹配網站根目錄的URL,對映到openstack_dashboard.views.splash檢視。 url(r'^$', 'openstack_dashboard.views.splash', name='splash'), # 任何以/auth/開頭的URL將會匹配,引入openstack_auth.urls url(r'^auth/', include('openstack_auth.urls')), # 任何以/api/開頭的URL將會匹配,引入openstack_dashboard.api.rest.urls url(r'^api/', include('openstack_dashboard.api.rest.urls')), # 任何訪問URL將會匹配,都引用horizon.urls url(r'', include(horizon.urls)), )
horizon(2-5).py

       openstack_dashboard.urls.py:

    url(r'', include(horizon.urls)): horizon.urls對應的是horizon.baise._lazy_urls(),執行之前匯入horizon包載入_lazy_urls()方法中的

            def url_patterns():
                 
return self._urls()[0]

接著呼叫 horizon.base.Site._urls(),完成所有Dashboard和Panel的註冊變編譯URLconf:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
def _urls(self): """ 從註冊的Dashboards構造Horizon的Urlconf """ # 獲取horizon.site_urls urlpatterns 值 urlpatterns = self._get_default_urlpatterns() # 從“settings.INSTALLED_APPS發現模組,包含dashboard.py、panel.py的模組註冊,新增到登錄檔中self._registry,沒有的丟擲異常。 self._autodiscover()--------------------標註(1 # 發現每個Dashboards裡的Panels # 從登錄檔self._registry 取出註冊dashboard,註冊每個dashboard中的panel # self._registry:{<class 'openstack_dashboard.dashboards.identity.dashboard.Identity'>: <Dashboard: identity>, # <class 'openstack_dashboard.dashboards.project.dashboard.Project'>: <Dashboard: project>, # <class 'openstack_dashboard.dashboards.admin.dashboard.Admin'>: <Dashboard: admin>, # <class 'openstack_dashboard.dashboards.settings.dashboard.Settings'>: <Dashboard: settings>} #self._registry.values():[<Dashboard: identity>, <Dashboard: project>, <Dashboard: admin>, <Dashboard: settings>] for dash in self._registry.values(): dash._autodiscover() -----------------標註(2 # 載入基於外掛的面板配置 self._load_panel_customization() # 允許覆蓋模組 if self._conf.get("customization_module", None): customization_module = self._conf["customization_module"] bits = customization_module.split('.') mod_name = bits.pop() package = '.'.join(bits) mod = import_module(package) try: before_import_registry = copy.copy(self._registry) import_module('%s.%s' % (package, mod_name)) except Exception: self._registry = before_import_registry if module_has_submodule(mod, mod_name): raise # 編譯動態URL配置。 for dash in self._registry.values(): urlpatterns += patterns('', url(r'^%s/' % dash.slug, include(dash._decorated_urls))) # 返回三個引數,django.conf.urls.include "" 返回的urlpattern引數值 ([<RegexURLPattern user_home ^home/$>, <RegexURLPattern jsi18n ^i18n/js/(?P<packages>\S+?)/$>, <RegexURLPattern set_language ^i18n/setlang/$>, <RegexURLResolver <module 'django.conf.urls.i18n' from '/usr/lib/python2.7/dist-packages/django/conf/urls/i18n.pyc'> (None:None) ^i18n/>, <RegexURLResolver <RegexURLResolver list> (settings:settings) ^settings/>, <RegexURLResolver <RegexURLResolver list> (identity:identity) ^identity/>, <RegexURLResolver <RegexURLResolver list> (project:project) ^project/>, <RegexURLResolver <RegexURLResolver list> (admin:admin) ^admin/>, <RegexURLResolver <RegexURLResolver list> (settings:settings) ^settings/>, <RegexURLResolver <RegexURLResolver list> (identity:identity) ^identity/>, <RegexURLResolver <RegexURLResolver list> (project:project) ^project/>, <RegexURLResolver <RegexURLResolver list> (admin:admin) ^admin/>], 'horizon', 'horizon') """ return urlpatterns, self.namespace, self.slug
horizon(2-6).py

標記(1)從“settings.INSTALLED_APPS發現模組,包含dashboard.py、panel.py的模組註冊,新增到登錄檔中self._registry,沒有的丟擲異常。

                horizon.Site._autodiscover():

               
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24

            
           

相關推薦

Horizon 原始碼閱讀—— Horizon 模組註冊機制

一、寫在前面 這篇文章主要介紹一下OpenStack Horizon — juno專案模組註冊機制,本文將通過對Horizon原始碼解析瞭解各個模組註冊載入機制。全文穿插了Horizon組建的程式碼塊以及個人理解註釋,由於能力和時間有限,錯誤之處在所難免,歡迎指正!

Horizon 原始碼閱讀—— Horizon 整體介紹

一、寫在前面        這篇文章主要介紹一下Openstack Horizon — juno專案的整體情況,關於這方面的內容網上已經有很多相關的介紹,我在這裡只作為一個知識的搬運工,把一些分散的內

Horizon 原始碼閱讀—— 呼叫Novaclient流程

一、寫在前面 這篇文章主要介紹一下OpenStack(Kilo)關於horizon 呼叫NovaClient的一個分析。         如果轉載,請保留作者資訊。 原文地址:http://blog.csdn.net/u011521019/a

Koa原始碼閱讀上下文ctx

上篇提到,this.callback() 返回一個回撥函式,其實是以閉包的形式返回了一個區域性函式變數 handleRequest,供 Server 呼叫來處理 HTTP 請求。 callback() { const fn = compose(this.middleware); const han

java原始碼閱讀

#include <stdio.h> #define JRT_ENTRY(result_type , header) \ JRT_NO(result_type , header) #define JRT_NO(result_type , header

ORB-SLAM2的原始碼閱讀:ORB特徵提取

怎麼讀一個工程?程式碼菜鳥不敢妄言。LZ也就嘗試化整為零,逐個擊破,再化零為整,全域性理解。下面程式碼來自ORB_SLAM2的ORBextractor.h和ORBextractor.cc.為什麼要寫這個部落格,因為笨,看程式碼怕忘了。自己寫一遍加深下記憶。為什麼

Caddy原始碼閱讀啟動流程與 Event 事件通知

Caddy原始碼閱讀(二)啟動流程與 Event 事件通知 Preface Caddy 是 Go 語言構建的輕量配置化伺服器。https://github.com/caddyserver/caddy Caddy 整個軟體可以說是由不同的 外掛 堆砌起來的。自己本身僅提供 Plugin 的註冊執行邏輯和 Se

Flume NG原始碼分析支援執行時動態修改配置的配置模組

在上一篇中講了Flume NG配置模組基本的介面的類,PropertiesConfigurationProvider提供了基於properties配置檔案的靜態配置的能力,這篇細說一下PollingPropertiesFileConfigurationProvider提供的執行時動態修改配置並生效的

surefire 拉起testng單元測試類的原始碼流程閱讀

這裡是基於surefire 2.19.1版本分析的。 還是根據surefire 拉起單元測試執行報錯的日誌展示的執行過程分析 java.lang.instrument.IllegalClassFormatException: Error while instrumenti

Spring源碼閱讀

函數 source fin gin pri remove relation boolean only 我們先看AbstractBeanFactory.getBean方法,這個方法通過bean名稱類型等信息獲取類實例,如果實例不存在則生產。 //----------

vue源碼閱讀

new wiki eth 工具 download 分配 避免 其中 aid 一 一個實例 如果簡單了解過些Vue的API的話,肯定會對一下這個特別熟悉,在上一篇裏,分析了Vue的核心文件core的index.js構造vue函數執行的流程。 那麽下邊這個則是實

GCC原始碼分析——前端

原文連結:http://blog.csdn.net/sonicling/article/details/6706152   從這一篇開始,我們將從原始碼的角度來分析GCC如何完成對C語言原始檔的處理。GCC的內部構架在GCC Internals(搜“gccint.pdf”,或者見[

【筆記】ThreadPoolExecutor源碼閱讀

同步 mage 不用 start 技術分享 ted err 斷線 情況 AQS在Worker中的應用 我對這個上鎖一直搞不懂,雖然有註釋說是允許中斷啥的,但是還是一頭霧水,就打算直接看代碼分析。第一眼看到這個lock的時候,我就嚇到了。啥,一上鎖,多個線程不是就要同步排隊了

Spring原始碼解析——元件註冊2

    import com.ken.service.BookService; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.

【筆記】ThreadPoolExecutor原始碼閱讀

執行緒數量的維護 執行緒池的大小有兩個重要的引數,一個是corePoolSize(核心執行緒池大小),另一個是maximumPoolSize(最大執行緒大小)。執行緒池主要根據這兩個引數對執行緒池中執行緒的數量進行維護。 需要注意的是,執行緒池建立之初是沒有任何可用執行緒的。只有在有任務到達後,才開始建立

Android Hook框架adbi原始碼淺析

二、libbase 其實上面載入完SO庫後,hook的功能我們完全可以自己在動態庫中實現。而adbi作者為了方便我們使用,編寫了一個通用的hook框架工具即libbase庫。libbase依然在解決兩個問題:1.獲取要hook的目標函式地址;2.給函式打二進位制補丁即inline hook。 關於獲取ho

Glide原始碼分析——從用法來看之load&into方法

上一篇,我們分析了with方法,文章連結: https://blog.csdn.net/qq_36391075/article/details/82833260 在with方法中,進行了Glide的初始化,建立了RequesManger,並且綁定了生命週期,最終返回了一個Reques

YOLOv2原始碼分析

文章全部YOLOv2原始碼分析 接著上一講沒有講完的make_convolutional_layer函式 0x01 make_convolutional_layer //make_convolutional_laye

elas原始碼賞析sobel運算元3*3行列分解快速卷積

sobel3*3運算元的計算過程 _mm_malloc() convolve_cols_3x3 列卷積 convolve_101_row_3x3 101的行卷積 convolve_121_row_3x3 121的行卷積

zigbee 之ZStack-2.5.1a原始碼分析 無線接收控制LED

本文描述ZStack-2.5.1a 模板及無線接收移植相關內容。 main HAL_BOARD_INIT // HAL_TURN_OFF_LED1 InitBoard HalDriverInit HalAdcInit