openstack plugin 之(三)怎樣寫 OpenStack Neutron 的 Extension

前兩篇文章討論了怎麼寫一個 Neutron 的插件。但是最基本的插件只包括 Network, Port,和 Subnet 三種資源。如果需要引入新的資源,比如一個二層的 gateway 的話,就需要在插件的基礎上再寫一個 extension, 也就是擴展。

Neutron 已經預定義了很多擴展,可以參看 neutron/extensions 下面的文件,我在這裏就不一一列舉了。如果正好有一種是你需要的,那直接拿過來用就好了。如果需要自己從頭搭起的話,可以現在 自己的 plugin 文件夾下面創建一個 extensions 文件夾,然後在這個文件夾下面創建兩個文件: __init__.py 和 myextension.py:

- neutron/

  - plugins/

    - myplugin/

      - __init__.py

      - plugin.py

      - extensions/

        - __init__.py

        - myextension.py

 

__init__.py 是空的就行了。在 myextension.py 中需要定義兩個東西:一個叫RESOURCE_ATTRIBUTE_MAP 的詞典和一個叫 Myextension 的類。RESOURCE_ATTRIBUTE_MAP裏面放的就是你的這個新擴展的屬性,例如:

複製代碼
RESOURCE_ATTRIBUTE_MAP = {
    'myextensions': {
        'id': {'allow_post': False, 'allow_put': False,
               'is_visible': True},
        'name': {'allow_post': True, 'allow_put': True,
                          'is_visible': True},
        'tenant_id': {'allow_post': True, 'allow_put': False,
                      'validate': {'type:string': None},
                      'required_by_policy': True,
                      'is_visible': True}
        }
    }
複製代碼

 

需要注意的是在詞典中,第一層的 key ‘myextensions’ 就是文件名 ’myextension‘ 加上一個 ‘s'。第二層的 keys ’id‘, ’name‘, ’tenant_id‘ 就是這個擴展的三個屬性。第三層的 keys 在 neutron/api/v2/attributes.py 中有比較詳細的解釋,我把它搬到這裏來了:

複製代碼
# The following is a short reference for understanding attribute info:
# default: default value of the attribute (if missing, the attribute
# becomes mandatory.
# allow_post: the attribute can be used on POST requests.
# allow_put: the attribute can be used on PUT requests.
# validate: specifies rules for validating data in the attribute.
# convert_to: transformation to apply to the value before it is returned
# is_visible: the attribute is returned in GET responses.
# required_by_policy: the attribute is required by the policy engine and
# should therefore be filled by the API layer even if not present in
# request body.
# enforce_policy: the attribute is actively part of the policy enforcing
# mechanism, ie: there might be rules which refer to this attribute.
複製代碼

 

定義新類的時候需要注意一點,這個類的名字與包含這個類的文件名的唯一區別必須是一個首字母大寫,另一個首字母小寫。也就是說把MyExtension當做類的名字可能就會導致出錯。具體原因可以參考 neutron/api/extensions.py 中 ExtensionManager 的_load_all_extensions_from_path 方法的實現。Myextension 這個類可以繼承 neutron/api/extensions.py 這個文件中的一個類:ExtensionDescriptor,也可以自己定義。下面給出一個繼承了該類的定義:

複製代碼
from neutron.api import extensions
from neutron import manager
from neutron.api.v2 import base

class
Myextension(extensions.ExtensionDescriptor): # The name of this class should be the same as the file name # The first letter must be changed from lower case to upper case # There are a couple of methods and their properties defined in the # parent class of this class, ExtensionDescriptor you can check them @classmethod def get_name(cls): # You can coin a name for this extension return "My Extension" @classmethod def get_alias(cls): # This alias will be used by your core_plugin class to load # the extension return "my-extensions" @classmethod def get_description(cls): # A small description about this extension return "An extension defined by myself. Haha!" @classmethod def get_namespace(cls): # The XML namespace for this extension return "http://docs.openstack.org/ext/myextension/api/v1.0" @classmethod def get_updated(cls): # Specify when was this extension last updated, # good for management when there are changes in the design return "2014-08-07T00:00:00-00:00" @classmethod def get_resources(cls): # This method registers the URL and the dictionary of # attributes on the neutron-server. exts = [] plugin = manager.NeutronManager.get_plugin() resource_name = 'myextension' collection_name = '%ss' % resource_name params = RESOURCE_ATTRIBUTE_MAP.get(collection_name, dict()) controller = base.create_resource(collection_name, resource_name, plugin, params, allow_bulk=False) ex = extensions.ResourceExtension(collection_name, controller) exts.append(ex) return exts
複製代碼

 

到這一步爲止,這個 myextension.py 文件基本就算是大功告成了,接下來需要去配置 /etc/neutron/neutron.conf 文件,告訴 Neutron 去哪裏找到這個擴展。那麼在該文件的[DEFAULT]下面,我們可以找到一個選項叫做api_extensions_path,並且把剛剛創建的 extensions 文件夾的位置賦給它,例如:

api_extensions_path = /usr/lib/python2.7/dist-packages/neutron/plugins/myplugin/extensions

 

然後在自己的 MyPlugin 類的定義中還要加一句話,告訴 Neutron 我的插件支持這個擴展。需要注意的是這裏的方括號中的名字應該與上面 get_alias() 方法獲得的名字一致。

複製代碼
class MyPlugin(db_base_plugin_v2.NeutronDbPluginV2):
  ...
  supported_extension_aliases = ['my-extensions']
  
  def __init__(self):
    ...
  ...
複製代碼

 

最後重啓一下 neutron server, “service neutron-server restart”, 如果看到 /var/log/neutron/server.log 裏面有 Loaded extension: my-extensions 的字樣就說明成功了。

在接下來的一些文章中,我會繼續討論一下如何實現一個擴展的不同操作,如何在 CLI 中加入對自定義擴展的命令支持等內容。

發佈了4 篇原創文章 · 獲贊 38 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章