本章我们将写一个自己的core plugin 和一个resource extension来加深理解。(阅读本文的前提是你已经理解了restful以及stevedore等内容)

什么是 core plugin

neutron的plugin有core和service两种。core plugin实现core resource的增删改查,service plugin我们在本文暂不讨论。

core resource 有network/subnet/port/subnet-pool。每种资源对应CURD和index5种操作。以network资源为例,相关操作和HTTP请求的对应关系如下

create_network    POST networks
delete_network DELETE networks/network_id
update_network PUT networks/network_id
get_network GET networks/network_id
get_networks GET networks

因此,core plugin中对各种核心资源都有如下的函数对应:

create_{resource_name}
update_{resource_name}
delete_{resource_name}
get_{resource_name}
get_{resource_names}

how to write core plugin

既然是plugin,也就是我们前面说过的third-party code,那就应该定义一些接口,好让第三方的组织可以参与开发。

neutron中定义的接口在neutron.neutron_plugin_base_v2.NeutronPluginBaseV2。要实现一个core plugin,你需要实现这个class。

下面是我写的一个core plugin 代码

from neutron import neutron_plugin_base_v2
from oslo_log import log
LOG = log.getLogger(__name__) class MyNeutronPlugin(neutron_plugin_base_v2.NeutronPluginBaseV2):
supported_extension_aliases = ['gold']
def __init__(self):
super(MyNeutronPlugin,self).__init__()
#### network ####
def create_network(self, context, network):
LOG.info("network is %s" %network)
return network
def update_network(self, context, id, network):
return network
def get_network(self, context, id, fields=None):
network = {}
return network
def get_networks(self, context, filters=None, fields=None):
network = {}
LOG.info("return network %s" %network)
return network
def delete_network(self, context, id):
return id
...
#### gold ####
def create_gold(self, context, gold):
LOG.info("gold is %s" %gold)
return gold
def update_gold(self, context, id, gold):
return gold
def get_gold(self, context, id, fields=None):
gold = {}
return gold
def get_golds(self, context, filters=None, fields=None):
gold = {}
LOG.info("return gold %s" %gold)
return gold
def delete_gold(self, context, id):
return id

请注意,除了network等核心资源,我们还实现了一个gold资源。 上面粘贴的内容省略了subnet/port/subnet-pool的相关代码。 既然是thirty-party code,就应该可以独立安装。所以我们的代码结构如下:

myPlugin/
├── myPluginPKG
│   ├── __init__.py
│   └── myPluginModule.py
└── setup.py

init.py的内容为

import myPluginModule

setup.py内容如下:

from setuptools import setup, find_packages

setup(
name='myPluginPKG',
version='1.0', packages=find_packages(), entry_points={
'neutron.core_plugins': [
'myNeutronPlugin = myPluginPKG.myPluginModule:MyNeutronPlugin',
],
},
)

这样在运行python setup.py install 后,我们的plugin就注册到了neutron.core_plugins这个namespace下。这部分内容其实就是前面stevedore中driver开发的内容。neutron通过stevedore在这个namespace下加载core_plugin

OK。在python setup.py install 后,我们的core plugin安装完成了,这时要修改/etc/neutron/neutron.conf,让neutron使用我们的core plugin。

[default]
core_plugin = myNeutronPlugin

然后重启neutron服务

systemctl restart neutron-server

接下来你就可以通过API 尝试了, 我们创建一个network 如下:

curl -g -i -X POST "http://liberty-controller01:9696/v2.0/networks" -H "Content-Type: application/json" -H "Accept: application/json" -H "X-Auth-Token:$token" -d '{"network": {"name": "n2", "admin_state_up": true}}'
HTTP/1.1 201 Created
Content-Type: application/json; charset=UTF-8
Content-Length: 15
X-Openstack-Request-Id: req-a8cfde05-6425-42fe-b516-0bb2d264cf61
Date: Tue, 07 Feb 2017 08:01:47 GMT {"network": {}}

可以看到成功返回,因为我们的plugin中什么也没做,所以返回的其实是一个空的字典。但至少证明该api成功了。

之前我们还在plugin中增加了gold资源对应的函数,我们试试访问gold资源:

curl -g -i  "http://liberty-controller01:9696/v2.0/golds" -H "Content-Type: application/json" -H "Accept: application/json" -H "X-Auth-Token:$token"
HTTP/1.1 404 NOT FOUND

为什么gold资源的API不好使呢。这是因为要想在neutron core plugin中定义一个资源,不仅要提供该资源的CURD 函数,还要有对应的RESOURCE_ATTRIBUTE_MAP.

RESOURCE_ATTRIBUTE_MAP

RESOURCE_ATTRIBUTE_MAP是neutron/api/v2/attribute.py中的一个字典,其结构大概如下:

RESOURCE_ATTRIBUTE_MAP = {
NETWORKS: {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True,
'primary_key': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': NAME_MAX_LEN},
'default': '', 'is_visible': True},
'subnets': {'allow_post': False, 'allow_put': False,
'default': [],
'is_visible': True},
'admin_state_up': {'allow_post': True, 'allow_put': True,
'default': True,
'convert_to': convert_to_boolean,
'is_visible': True},
'status': {'allow_post': False, 'allow_put': False,
'is_visible': True},
'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': TENANT_ID_MAX_LEN},
'required_by_policy': True,
'is_visible': True},
SHARED: {'allow_post': True,
'allow_put': True,
'default': False,
'convert_to': convert_to_boolean,
'is_visible': True,
'required_by_policy': True,
'enforce_policy': True},
},

该字典定义了资源以及资源对应的属性,我们这里只列出了network。所以,要想定义gold资源,除了在core plugin中添加对应的函数,还有在这个attribute map中添加相关的信息。不过,我们不建议直接修改这里的代码,正确的方法是通过resource extension来实现。

how to code an extension (resource extension)

之前提到了extension有3种类型,resource,action,request。我们这里要实现一个新的资源,就是用resource extension。无论是哪种extension 都要遵守一些特定的规则,或者说接口,这些规则如下:

1. extension应该放在neutron/extensions文件夹下,或者在配置文件中设置api_extensions_path
2. extension的class名应该和文件同名,当然首字母应该大写
3. 应该实现neutron.api.extensions.py中ExtensionDescriptor定义的接口
4. 在对应的plugin的supported_extension_aliases 中增加我们extension的别名。前面我们写的core plugin就有这个属性,当时没有做说明,其实是这里应该添加的。所谓别名是该extension要实现的一个接口,后面会看到。

另外很重要的一点是,因为我们要实现的是resource extension,所以还要实现

get_resources

这个接口。

下面我们实现一个自己的resource extension来增加 gold 资源。

from neutron.api import extensions
from neutron import manager
from neutron.api.v2 import base # You have to specify the attributes neutron-server should expect when
# someone invokes this plugin. Let's say you want
# 'name', 'priority', 'credential' for your extension /golds
# then following dictionary must be declared.
# I am following the naming convention used by other extensions. RESOURCE_ATTRIBUTE_MAP = {
'golds': {
'name': {'allow_post': True, 'allow_put': True,
'is_visible': True},
'priority': {'allow_post': True, 'allow_put': True,
'is_visible': True},
'credential': {'allow_post': True, 'allow_put': True,
'is_visible': True},
# tenant_id is the user id used by keystone for authorisation
# It's good to use the following as it is and it is necessary
# for every extension
'tenant_id': {'allow_post': True, 'allow_put': False,
'required_by_policy': True,
'validate': {'type:string': None},
'is_visible': True}
}
} # Great! Now you have the defined the attributes that you need for your
# extensions. You need to store this dictionary in the neutron-server
# by the following class class Golds(extensions.ExtensionDescriptor):
# The name of this class should be the same as the file name
# 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 "Name of golds" @classmethod
def get_alias(cls):
# This alias will be used by your core_plugin class to load
# the extension
return "gold" @classmethod
def get_description(cls):
# A small description about this extension
return "A quick brown fox jumped over a lazy dog" @classmethod
def get_namespace(cls):
# The XML namespace for this extension
# but as we move on to use JSON over XML based request
# this is not that important, correct me if I am wrong.
return "namespace of xml" @classmethod
def get_updated(cls):
# Specify when was this extension last updated,
# good for management when there are changes in the design
return "2017-02-07T10:00:00-00:00" @classmethod
def get_resources(cls):
# This method registers the URL and the dictionary of
# attributes on the neutron-server.
exts = list()
plugin = manager.NeutronManager.get_plugin()
resource_name = 'gold'
collection_name = resource_name + 's'
params = RESOURCE_ATTRIBUTE_MAP.get(resource_name + 's', 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

代码的大部分解释都包含在注释里,因此请详细阅读每一行。这里只重点说一下RESOURCE_ATTRIBUTE_MAP 和 get_resources。 我们的extension就是通过这个函数来在resource attribute map中增加了gold资源。

另外要注意的是get_alias,该函数返回extension的别名,plugin的supported_extension_alias中用该别名来找extension。

ok,我们现在尝试一下访问gold 资源

curl -g -i http://liberty-controller01:9696/v2.0/golds.json -H "Content-Type: application/json" -H "Accept: application/json" -H "X-Auth-Token:$token"
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Content-Length: 13
X-Openstack-Request-Id: req-7b925f97-4df0-49ee-bb6a-fd7c86fda9aa
Date: Tue, 07 Feb 2017 08:29:34 GMT {"golds": []}

这次成功了。 以上就是core plugin和resource extension

how to read openstack code: Core plugin and resource extension的更多相关文章

  1. how to read openstack code: service plugin

    We have learned core plugin, service plugin and extension in last post. Now let`s review: Core Plugi ...

  2. how to read openstack code

    本文的目的不是介绍openstack.我们这里假设你已经知道了openstack是什么,能够做什么.所以目的是介绍如何阅读openstack的代码.通过读代码来进一步学习openstack. 转载要求 ...

  3. how to read openstack code: action extension

    之前我们看过了core plugin, service plugin 还有resource extension. resource extension的作用是定义新的资源.而我们说过还有两种exten ...

  4. 详解 ML2 Core Plugin(II) - 每天5分钟玩转 OpenStack(72)

    上一节我们讨论了 ML2 Plugin 解决的问题,本节将继续研究 ML2 的架构. ML2 对二层网络进行抽象和建模,引入了 type driver 和 mechansim driver. 这两类 ...

  5. 详解 ML2 Core Plugin(I) - 每天5分钟玩转 OpenStack(71)

    我们在 Neutron Server 小节学习到 Core Plugin,其功能是维护数据库中 network, subnet 和 port 的状态,并负责调用相应的 agent 在 network ...

  6. 怎样写 OpenStack Neutron 的 Plugin (一)

    鉴于不知道Neutron的人也不会看这篇文章,而知道的人也不用我再啰嗦Neutron是什么东西,我决定跳过Neutron简介,直接爆料. 首先要介绍一下我的开发环境.我没有使用DevStack,而是直 ...

  7. Neutron:ML2 Core Plugin

    两个 Core Plugin:linux bridge plugin 和 open vswitch plugin.   Moduler Layer 2(ML2)是 Neutron 在 Havana 版 ...

  8. how to read openstack code: loading process

    之前我们了解了neutron的结构,plugin 和 extension等信息.这一章我们看一下neutron如何加载这些plugin和extension.也就是neutron的启动过程.本文涉及的代 ...

  9. how to read openstack code: Neutron architecture

    今天这一章节非常重要.我们知道neutron是一个非常复杂的系统,由很多组件构成.研究这样一个复杂的系统,正确的顺序应该是现在宏观上对其整体结构有所了解,然后再由针对性的对其组件进行深入了解.本章要做 ...

随机推荐

  1. fork后子进程从哪里开始执行

    子进程和父进程都从调用fork函数的下一条语句开始执行

  2. class 写在 import的位置 类的名字第一个字母大写 后面没括号 ES6

    class 写在 import的位置 类的名字第一个字母大写 后面没括号 class ObTableDataClass {}或者 const ObTableDataClass = class { in ...

  3. 监控java进程是否正常运行

    @echo off set _task=java.exe :checkstart for /f "tokens=1" %%n in ('tasklist ^| find " ...

  4. JSON parse error: Can not construct instance of model.Class: no suitable constructor found

    reference:http://blog.csdn.net/qq_33642117/article/details/51909346 当类中没有定义构造函数时,系统会指定给该类加上一个空参数的构造函 ...

  5. python+Eclipse+pydev环境搭建1

    编辑器: Eclipse + pydev插件 1. Eclipse是写JAVA的IDE, 这样就可以通用了,学习代价小.  学会了Eclipse, 以后写Python或者JAVA 都可以. 2. Ec ...

  6. IFE春季班第一阶段任务(请仔细阅读)

    第一阶段的主要目标是帮助大家 了解.认识.学习.掌握HTML及CSS.第一阶段任务从 3月14日 开始,持续到 4月3日.当然,您也可以在这个时间以后继续自行实践练习. 第一阶段任务一共有 12 个题 ...

  7. get、post、put、delete

    form表单有两种提交方式,get和post,get没有请求体, 但是有查询字符串参数拼接在url后面,post有请求体,表单内容对用户不可见. 1.form表单以get请求提交, 2.from表单以 ...

  8. composer 设置代理

    在命令行终端中输入以下内容: export https_proxy='192.168.1.133:1080' export http_proxy='192.168.1.133:1080' 此前提是你已 ...

  9. 同一SQL语句在PLSQL Developer与SQL * PLUS工具中执行结果不一致

    背景 今天遇到如下问题,同一sql语句在PLSQL Developer与SQL*PLUS工具中执行结果不一致, sql语句如下 SELECT 'GROUPHEALTH_SEND_EMAIL' as i ...

  10. Function()构造函数与函数直接量

    Function()构造函数与函数直接量 制作人:全心全意 在JavaScript中,除了可使用基本的function语句定义函数之外,还可以使用另外两种方式来定义,即使用Function()构造函数 ...