neutron plugin 与 extension 编写流程

neutron plugin 与 extension 编写流程

关于neutron,它是openstack中管理网络相关的一个项目,它主要负责管理了openstack中的虚拟网络,它将网络作为一种服务提供给租户,它的设计遵循了SDN(soft define network)的设计原则从而实现了网络的虚拟化。

neutron采用了插件技术,关于neutron的具体技术细节,可以参考:

https://yeasy.gitbooks.io/openstack_understand_neutron/content/index.html

下面介绍plugin与extension的编写流程:

plugin

neutron的完整目录在

/usr/lib/python2.7/dist-packages/neutron

- neutron/
  - agent/
  - api/
  - cmd/
  - common/
  - db/
  - debug/
  - extensions/
  - locale/
  - notifiers/
  - openstack/
  - plugins/
  - scheduler/
  - server/
  - services/
  - manager.py
  - neutron_plugin_base_v2.py
  - service.py
  - wsgi.py
  - ...

这个目录中plugins目录下存放的就是neutron的插件,neutron在启动的时候会以此载入plugin下面的插件。当我们需要实现自己的插件的时候,首先第一步,就是在plugins目录下建立一个子目录,子目录如下:

  • neutron

      - plugins

        - myplugin

          - __init__.py

          - plugin.py

上述两个文件是最基本的,当然plugin.py也可以用不同的名字。但是__init__.py的名字是不能改的,这个用来告诉neutron myplugin可以看做是一个module。plugin.py里面可以先定义一个基本空的class 叫做MyPlugin,虽然现在这个plugin什么用都没有。

from neutron.db import db_base_plugin_v2
from neutron.openstack.common import log

LOG = log.getLogger(__name__)

class MyPlugin(db_base_plugin_v2.NeutronDbPluginV2):

    def __init__(self):
        LOG.info("MyPlugin is started.")

这个简单的插件写完之后,我们需要将其注册到neutron中,让neutron知道我们自己实现了这么一个插件。这时候我们就需要在neutron中注册这个插件,注册方法是在/etc/neutron/neutron.conf中添加一行:

core_plugin = neutron.plugins.myplugin.plugin.MyPlugin

这样,一个简单的neutron插件就添加完成了。当然,这里只是说了添加流程,具体实现上有一些细节需要注意的,接下来进行这方面说明。

plugin 实现细节

首先,Neutron对最基本的三个资源:Network, Port 和 Subnet 的基本调用都已经定义好了API接口。如果你的插件也需要用到这些资源,最好直接实现它们的接口。API接口的定义可以再 neutron/neutron_plugin_base_v2.py 这个文件中找到,其中每个参数的作用也有比较详细的介绍。对于用不着的资源,直接放任不管就好了,最多下次不小心调用了会发出“该接口没有被实现”的错误,不会有其他影响。这里是一个 Network API 实现的范例,其实它什么也没有做,但是确实是一个有效的接口实现:

from neutron import neutron_plugin_base_v2
class MyPlugin(neutron_plugin_base_v2.NeutronPluginBaseV2):
    def __init__(self):
        pass

    def create_network(self, context, network):
        # Create a network by using data from network dictionary
        # Send back a dictionary to display created network‘s info
        return network

    def update_network(self, context, id, network):
        # Update a created network matched by id with
        # data in the network dictionary. Send back a
        # dictionary to display the network‘s updated info
        return network

    def get_network(self, context, id, fields=None):
        network = {}
        # List information of a specific network matched by id
        # and return it in a form of dictionary
        return network

    def get_networks(self, context, filters=None, fields=None):
        network = {}
        # List all networks that are active
        return network

    def delete_network(self, context, id):
        # Delete a specific network matched by id
        # return back the id of the network.
        return id

如果在具体实现这些接口的过程中,需要参考的话,有两个地方非常值得参考:一个是 neutron/db/db_base_plugin_v2.py,这个是neutron官方给出的一个基于数据库的实现。它只是操作数据库中的内容,模拟各个资源的创建、修改、删除等操作,但没有在物理机器上做任何改变。第二个地方就是 neutron/plugins 里面收纳的各个公司的插件实现,我们可以从中学习到其他公司是怎么写插件的。

plugin 与 extension

neutron针对每个plugin 还提供了extension,顾名思义,extension即拓展。在neutron中,我们将plugin的资源分为network、port、subnet三类,但是在实际的应用中,很可能需要的不仅仅是这三类的基本资源,而是更多需要拓展功能的自定义的资源。针对neutron client请求这些自定义的资源时,我们会请求处理放在extension中,这样便有了extension存在的必要性。因此,这便是neutron plugin和extension的区别。

extension

这里我在之前的plugin中添加一个extension。

首先在插件目录下建立extension目录:

- neutron/
  - plugins/
    - myplugin/
      - __init__.py
      - plugin.py
      - extensions/
        - __init__.py
        - myextension.py

其中extension的名字和myextension.py都是可以自定义的。

接下来编写myextension.py中的代码,首先需要定义资源属性:

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 中有比较详细的解释。

定义新类的时候需要注意一点,这个类的名字与包含这个类的文件名的唯一区别必须是一个首字母大写,另一个首字母小写。也就是说把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

其中get_alias方法返回的是插件的名字,这个名字必须跟myplugin/plugin.py中声明的extension要一致:

class MyPlugin(db_base_plugin_v2.NeutronDbPluginV2):
  ...
  supported_extension_aliases = [‘my-extensions‘]
  
  def __init__(self):
    ...
  ...

这样extension的实现基本就算完成了,最后只需要在/etc/neutron/neutron.conf指定一下extension的位置即可。

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

neutron client

这样extension的添加大体完成。梳理一下我们所做的工作,在neutron/plugins/myplugin/plugin.py中应该是这样子的:

from neutron import neutron_plugin_base_v2

class MyPlugin(neutron_plugin_base_v2.NeutronPluginBaseV2):
    def __init__(self):
        pass

   [...]
  
  def create_myextension(self, context, myextension):
        return myextension

    def update_myextension(self, context, id, myextension):
        return myextension

    def get_myextension(self, context, id, fields=None):
        myextension = {}
        return myextension

    def get_myextensions(self, context, filters=None, fields=None):
        myextensions = {}
        return myextensions

    def delete_myextension(self, context, id):
        return id
 
  [...]

因此,neutron-server端的实现需要我们提供一个neutron client来对其进行请求操作。那么具体的client请求中,首先我们需要设置命令,在neutron同级目录中,neutronclient/shell.py中:

from neutronclient.neutron.v2_0.myextension import extension as my_ext
COMMAND_V2 = {
    ‘net-list‘: network.ListNetwork,
    ‘net-external-list‘: network.ListExternalNetwork,
    ‘net-show‘: network.ShowNetwork,
    ‘net-create‘: network.CreateNetwork,
    ‘net-delete‘: network.DeleteNetwork,
    ‘net-update‘: network.UpdateNetwork,
    ...
  ‘myextension-list‘: my_ext.ListExtension,
  ‘myextension-show‘: my_ext.ShowExtension,
  ‘myextension-create‘: my_ext.CreateExtension,
  ‘myextension-delete‘: my_ext.DeleteExtension,
  ‘myextension-update‘: my_ext.UpdateExtension,
  ...
  }

我们添加了自定义插件的命令,这样我们可以通过neutron 命令对其进行请求操作。

对于neutronclient,其目录结构是这样的:

- neutronclient/

  - neutron/

    - v2_0/

      - myextension/

        - __init__.py

        - extension.py

要实现neutron的CLI,主要是实现extension.py文件,我们需要在其添加我们在neutron extension中实现的几个命令对应的类。即List/Show/Create/Delete/UpdateExtension

具体实现参考如下:

import argparse
import logging

from neutronclient.neutron import v2_0 as neutronV20
from neutronclient.openstack.common.gettextutils import _

RESOURCE = ‘myextension‘

class ListExtension(neutronV20.ListCommand):
    """List extensions"""

    resource = RESOURCE
    log = logging.getLogger(__name__ + ‘.ListExtension‘)
    list_columns = [‘id‘, ‘name‘]

class ShowExtension(neutronV20.ShowCommand):
    """Show information of a given extension."""

    resource = RESOURCE
    log = logging.getLogger(__name__ + ‘.ShowExtension‘)

class CreatePhysicalGateway(neutronV20.CreateCommand):
    """Create an extension."""

    resource = RESOURCE
    log = logging.getLogger(__name__ + ‘.CreateExtension‘)

    def add_known_arguments(self, parser):
        parser.add_argument(
            ‘name‘, metavar=‘NAME‘,
            help=_(‘Name of extension to create‘))

    def args2body(self, parsed_args):
        body = {self.resource: {
            ‘name‘: parsed_args.name}}
        return body

class UpdateExtension(neutronV20.UpdateCommand):
    """update a given extension."""

    resource = RESOURCE
    log = logging.getLogger(__name__ + ‘.UpdateExtension‘)
class DeleteExtension(neutronV20.DeleteCommand):
    """Delete a given extension."""

    resource = RESOURCE
    log = logging.getLogger(__name__ + ‘.DeleteExtension‘)

这些 class 处在接受 CLI 命令的第一线,负责将命令转化成 API call。需要特别注意的是 CreateExtension 这个类,它有两个方法 add_known_arguments 和 args2body。前者定义了 CLI 命令接受哪些参数,后者规定如何将收到的参数打包起来。

这些参数打包之后就会发给 neutron 后台中我们自己定义的 plugin controller,但是如何发送这些参数还需要我们去 /neutronclient/v2_0/client.py 的 Client 类中设置:

首先是 uri 路径:

 myextensions_path = "/myextensions"
 myextension_path = "/myextensions/%s"

然后是每个操作所对应的传递方法:

@APIParamsCall
    def list_myextensions(self, retrieve_all=True, **_params):
        """Fetches a list of all myextensions for a tenant."""
        return self.list(‘myextensions‘, self.myextensions_path, retrieve_all,
                         **_params)

    @APIParamsCall
    def show_myextension(self, myextension, **_params):
        """Fetches information of a certain entry in myextension."""
        return self.get(self.myextension_path % (myextension), params=_params)

    @APIParamsCall
    def create_myextension(self, body=None):
        """Creates a new myextension entry."""
        return self.post(self.myextensions_path, body=body)

    @APIParamsCall
    def delete_myextension(self, myextension):
        """Deletes the specified myextension."""
        return self.delete(self.myextension_path % (myextension))

   @APIParamsCall
    def update_myextension(self, myextension, body=None):
        """Updates a myextension."""
        return self.put(self.myextension_path % (myextension), body=body)

如此一来,我们自己实现的 neutron plugin 就能够收到 CLI 发送过来的命令啦。

参考资料

本文参考资料如下:

怎样写 OpenStack Neutron 的 Plugin (一)

怎样写 OpenStack Neutron 的 Plugin (二)

怎样写 OpenStack Neutron 的 Extension (一)

怎样写 OpenStack Neutron 的 Extension (二)

怎样写 OpenStack Neutron 的 Extension (三)

怎样写 OpenStack Neutron 的 Extension (四)

时间: 2024-10-04 10:55:29

neutron plugin 与 extension 编写流程的相关文章

Dz插件编写流程1

插件实现流程 开始编写社区插件,您应当首先对插件实现的流程有一个大致的了解,以下是我们推荐的插件编写流程: 熟练使用 Discuz! 社区系统后,对希望完善或补充的个性化功能进行评估,进而提出插件的功能需求. 对插件做一个概括性的设计,例如:需要使用什么菜单.什么参数,配置哪些选项.数据结构如何设计.前后台实现哪些功能等等. 阅读本文档并在系统设置中实际体验 Discuz! 插件接口所实现的功用,例如:您的插件应当如何设计才能良好的挂接到社区系统中来.插件接口能够实现哪些功能.不能实现哪些功能,

Chrome plug-in 和Extension

"扩展"和"插件",其实都是软件组件的一种形式,Chrome 只不过是把两种类型的组件分别给与了专有名称,一个叫"扩展",另一个叫"插件". 扩展(Extension),指的是通过调用 Chrome 提供的 Chrome API 来扩展浏览器功能的一种组件,工作在浏览器层面,使用 HTML + Javascript 语言开发[*].比如著名的 Adblock plus. 插件(Plug-in),指的是通过调用 Webkit 内

26、ATM项目的编写流程

1.搭建项目的目录规范 - ATM 项目根目录 - readme.md 项目的说明书 - start.py 项目启动文件 - conf 配置文件 - settings.py - lib 公共方法文件 - common.py - core(用户视图层) 存放用户视图层代码文件 - src.py - interface(逻辑接口层) 存放核心业务逻辑代码 - user_interface.py 用户相关的接口 - bank_interface.py 银行相关的接口 - shop_interface.

qt界面编写流程记录

qt的关键在于信号与槽机制. 1. 利用qtcreator 创建   qt widgeets application 工程 main.cpp   mainwindow.cpp  mainwindow.h  文件    .ui 界面设计文件 #include "mainwindow.h" #include <QApplication> #include <QPushButton> int main(int argc, char *argv[]) { QApplic

mybatis编写流程

1.根据xml配置文件(全局配置文件)创建一个SqlSessionFactory对象 有数据源一些运行环境信息2.sql映射文件:配置了每一个sql,以及sql的封装规则等.  3.将sql映射文件注册在全局配置文件中 4.写代码: 1).根据全局配置文件得到SqlSessionFactory: 2).使用sqlSession工厂,获取到sqlSession对象使用他来执行增删改查 一个sqlSession就是代表和数据库的一次会话,用完关闭  3).使用sql的唯一标志来告诉MyBatis执行

小程序的目录结构及基本代码编写流程

微信开发者工具下载:https://dldir1.qq.com/WechatWebDev/1.0.0/201812271/wechat_devtools_1.02.1812271_ia32.exe 编辑器的布局结构: 项目的目录结构: 如何写页面布局代码: 如何获取动态数据: 原文地址:https://www.cnblogs.com/wordblog/p/10211959.html

gpio驱动编写流程

步骤1.获取设备节点 使用of_find_node_by_path获取设备树中的节点信息 步骤2.获取gpio属性 使用of_get_named_gpio获取设备树中gpio的属性信息 步骤3.设置输入输出模式 使用gpio_direction_output设置gpio为输入或输出,以及默认电平 步骤4.设置输出电平 使用gpio_set_value设置gpio输出电平 原文地址:https://www.cnblogs.com/qingyunboke/p/12639250.html

如何区分 OpenStack Neutron Extension 和 Plugin

Neutron 里面的 extension 和 plugin 是非常相似的两个概念,我花了好久才貌似搞懂了两者的区别,还不一定完全正确. 在OpenStack 的官网wiki中,可以找到它们两个的定义: Plugin: Neutron exposes a logical API to define network connectivity between devices from other OpenStack services (e.g., vNICs from Nova VMs). The

nova boot代码流程分析(三):nova与neutron的交互(2)

继续<nova boot代码流程分析(三):nova与neutron的交互(1)>的分析. #/nova/virt/libvirt/driver.py:LibvirtDriver # NOTE(ilyaalekseyev): Implementation like in multinics # for xenapi(tr3buchet) def spawn(self, context, instance, image_meta, injected_files, admin_password,