1. 程式人生 > >通過neutron的extension新增restful介面和程式碼跨版本遷移

通過neutron的extension新增restful介面和程式碼跨版本遷移

    公司最初開發用的openstack版本是Kilo版,在neutron中開發的extension遷移到最新的Newton版本中就不適用了,需要做一些調整。
    原先開發的內容是要實現虛實結合,即在程式碼層面實現物理機和虛擬機器通訊,大體思路就是增加neutron的extension來新增一個restful
介面,通過呼叫介面來控制SDN交換機與各個節點間建立vxlan的tunnel,然後用另一個微服務管理物理機的註冊和刪除等等步驟,下面
來記錄下怎樣寫extension和遷移的問題。

一、編寫neutron的extension
1.在neutron/extensions程式碼目錄下有許多neutron自帶的extension,可以借鑑這些entension來寫自己的extension,
    比如在apitest.py中有如下程式碼:

from neutron.api import extensions
from neutron.api.v2 import attributes as attr
from neutron.api.v2 import base
from neutron import manager


# Attribute Map
RESOURCE_NAME = 'apitest'
RESOURCE_ATTRIBUTE_MAP = {
    RESOURCE_NAME + 's': {
        'id': {'allow_post': False, 'allow_put': False,
               'validate': {'type:uuid': None},
               'is_visible': True},
        'name': {'allow_post': True, 'allow_put': True,
                 'validate': {'type:string': None},
                 'default': '',
                 'is_visible': True},
        'tenant_id': {'allow_post': True, 'allow_put': False,
                      'is_visible': False},
        'address': {'allow_post': True, 'allow_put': False,
                    'default': attr.ATTR_NOT_SPECIFIED,
                    'validate': {'type:ip_address_or_none': None},
                    'is_visible': True}
    },
}


class Apitest(extensions.ExtensionDescriptor):
    """Agent management extension."""

    @classmethod
    def get_name(cls):
        return "apitest"

    @classmethod
    def get_alias(cls):
        return "apitest"

    @classmethod
    def get_description(cls):
        return "The centec switch extension."

    @classmethod
    def get_namespace(cls):
        return "http://docs.openstack.org/ext/agent/api/v2.0"

    @classmethod
    def get_updated(cls):
        return "2016-05-09T10:00:00-00:00"

    @classmethod
    def get_resources(cls):
        """Returns Ext Resources."""
        my_plurals = [(key, key[:-1]) for key in RESOURCE_ATTRIBUTE_MAP.keys()]
        attr.PLURALS.update(dict(my_plurals))
        plugin = manager.NeutronManager.get_plugin()
        params = RESOURCE_ATTRIBUTE_MAP.get(RESOURCE_NAME + 's')
        controller = base.create_resource(RESOURCE_NAME + 's',
                                          RESOURCE_NAME,
                                          plugin, params
                                          )

        ex = extensions.ResourceExtension(RESOURCE_NAME + 's',
                                          controller)

        return [ex]

    def get_extended_resources(self, version):
        if version == "2.0":
            return RESOURCE_ATTRIBUTE_MAP
        else:
            return {}

在這段程式碼中主要就兩點:(1)RESOURCE_ATTRIBUTE_MAP字典是對新擴充套件的屬性的定義。字典第一層的key為RESOURCE_NAME + 's',其實就是檔名加上一個 ‘s'。第二層的 keys ’id‘, ’name‘, ’tenant_id‘ , ‘address’就是這個擴充套件的三個屬性。第三層的 keys 在 neutron/api/v2/attributes.py 中一百多行有比較詳細的解釋,大家可以多翻原始碼。

(2)Apitest類的名稱與包含這個類的檔名字母必須一樣,只是類名首字母大寫。具體原因可以參考 neutron/api/extensions.py 中 ExtensionManager 的_load_all_extensions_from_path 方法的實現。Apitest這個類必須繼承 neutron/api/extensions.py 這個檔案中的一個類:ExtensionDescriptor,不然過不了N版neutron程式碼neutron/api/extensions.py檔案中ExtensionManager類中_check_extension方法中return isinstance(extension, ExtensionDescriptor)驗證這關。

2.這個檔案到這就算寫完了,然後把檔案放到neutron/extensions/下,接下來去寫個操作neutron資料庫的py檔案,為restful介面用。

比如檔名為apitest_db.py,內容如下:

import sqlalchemy as sa

from neutron import context as rpc_context
from neutron.db import model_base
from neutron.db import models_v2
from neutron.extensions import apitest as ext_sw
from oslo_utils import uuidutils


class Apitest(model_base.BASEV2, models_v2.HasId):
    name = sa.Column(sa.String(255))
    address = sa.Column(sa.String(64))
    tunnel_type = sa.Column(sa.String(32))
    description = sa.Column(sa.String(255))


class Apitest_db_mixin(ext_sw.CentecSWBase):
    def __init__(self):
        pass

    def create_apitest(self, context, apitest):
        pass

    def tunnel_sync(self):
        pass

具體內容根據自己公司的業務邏輯來寫就好了,這個檔案寫好後放到neutron/db/下,下一步來配置下restful介面,在/etc/neutron/policy.json檔案里加入以下內容:
    "update_apitest": "rule:admin_only",
    "delete_apitest": "rule:admin_only",
    "get_apitest": "rule:admin_only",

3.然後編輯neutron/plugins/ml2/plugin.py,新增from neutron.db import apitest_db,Ml2Plugin增加父類apitest_db.Apitest_db_mixin,Ml2Plugin的_supported_extension_aliases 屬性增加元素apitest。

4.最後重啟neutron-server就行了,因為我是用kolla部署的openstack單節點測試環境,所以直接重啟neutron-server的映象就行了,重啟前tail下日誌沒有報錯就可以訪問新加的restful介面了,介面名大家也看出來了,就是apitest,不過呼叫前先需要取下keystone的token

curl -X POST -d '{"auth": {"passwordCredentials": {"username": "admin", "password": "password"}, "tenantName": "admin"}}' -H "Content-type: application/json" http://127.0.0.1:5000/v2.0/tokens | python -m json.tool

如下圖:
 

token取到後就可以呼叫新新增的介面了:
curl "http://127.0.0.1:9696/v2.0/apitest" -H 'X-Auth-Token:9e1c09b8fd1d43d19465cfb45ebe999d' -H 'content-type:applicatin/json' -X POST -d '{"apitest":{"name":"sdn1_switch","address":"10.2.3.254","tunnel_type":"vxlan"}}'

5.如果需要修改neutron資料庫請看這篇文章點選開啟連結,這個問題到這就完成了。

二、再說說從K版遷移到N版的問題,我遇到的問題就兩點。
1. apitest.py檔案裡的class Apitest原先是繼承的object,在K版是可以的,但是在N版就必須繼承extensions.ExtensionDescriptor了,原因上文已經說明了。
2. apitest_db.py檔案中from oslo_utils import uuidutils程式碼是被替換後的程式碼,替換前為from neutron.openstack.common import uuidutils,這個也是在K版可以,在N版不行,這個問題原先的報錯只是這樣:ImportError: Plugin 'ml2' not found.


 除錯了半天才知道真正的錯誤被多層的try except吃掉了,沒有打印出來,現在解決完畢!