如何书写Openstack命令行
2017-07-10 11:47
99 查看
接着之前一篇文章,再来谈谈 Extension 的具体实现问题。我使用的是本地数据库加远程API调用的方法,所以先要定义一下数据库中 myextension 如何存储。首先,我们可以在自己的 plugin 根目录下新建一个 db 文件夹,以及三个文件:
- neutron/
- plugins/
- myplugin/
- __init__.py
- plugin.py
- extensions/
- db/
- __init__.py
- db.py
- models.py
db.py 用来存放数据库的 wrapper,提供一些读写数据库的方法;而 models.py 直接定义 myextension 的那些属性如何在数据库中存储。下面是 models.py 的一个例子:
import sqlalchemy as sa
from neutron.db import model_base
from neutron.db import models_v2
class MyExtension(model_base.BASEV2, models_v2.HasId,
model_v2.HasTenant):
''' Defines the data model for my extension '''
name = sa.Column(sa.String(255), nullable=False)
复制代码
db.py 的实现可以参照 db_base_plugin_v2.py 中的实现方式。然后在自己的 plugin.py 中实现 myextension 的五个操作: CREATE, UPDATE, GET, SHOW, 和 DELETE。到这里,myextension 的后台操作就全了。
但是这样还不够,因为我们还需要一些方式来从前台发布关于 myextension 的命令,使后台的 plugin controller 可以接收到,并进行相应的操作。这就需要修改 neutronclient 的相应代码,实现通过 CLI 的交互。
通过上面的介绍,我们现在的 myplugin 文件夹看上去应该是这样的:
- neutron/
- plugins/
- myplugin/
- __init__.py
- plugin.py
- extensions/
- __init__.py
- myextension.py
- db/
- __init__.py
- db.py
- models.py
我们的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
[...]
复制代码
这些方法需要进一步的定义来做一些有实际意义的事情,这里我只是做一个最简单的举例说明。在有了上述这些文件之后,其实整个 plugin 以及 extension 的后台就已经搞定了,只是这个时候还没有任何途径来使用这些定义好的方法。所以接下来我们需要在 neutronclient 中增加一些对应的方法。
neutronclient 是一个 CLI 客户端,可以用来与 neutron 互动。每一个 OpenStack 的 project 如 nova,neutron 等都有一个类似的自己的 CLI client。首先,我们需要在 CLI 中显示关于 myextension 的命令,这个可以在 neutronclient/shell.py 中设置。这个文件有一个属性叫做 COMMAND_V2,所有可以使用的命令都以 key-value pair 的形式存储在这个变量中:
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,
...
}
复制代码
下一步就需要去 neutronclient/neutron/v2_0 中新建一个文件夹 myextension,并在其中新建两个文件: __init__.py 和 extension.py,其结构如下:
- neutronclient/
- neutron/
- v2_0/
- myextension/
- __init__.py
- extension.py
然后再 extension.py 中分别定义五个class:List/Show/Create/Delete/UpdateExtension。
上面说到需要在 /neutronclient/v2_0/myextension/extension.py 中分别定义五个 class: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 发送过来的命令啦。
- neutron/
- plugins/
- myplugin/
- __init__.py
- plugin.py
- extensions/
- db/
- __init__.py
- db.py
- models.py
db.py 用来存放数据库的 wrapper,提供一些读写数据库的方法;而 models.py 直接定义 myextension 的那些属性如何在数据库中存储。下面是 models.py 的一个例子:
import sqlalchemy as sa
from neutron.db import model_base
from neutron.db import models_v2
class MyExtension(model_base.BASEV2, models_v2.HasId,
model_v2.HasTenant):
''' Defines the data model for my extension '''
name = sa.Column(sa.String(255), nullable=False)
复制代码
db.py 的实现可以参照 db_base_plugin_v2.py 中的实现方式。然后在自己的 plugin.py 中实现 myextension 的五个操作: CREATE, UPDATE, GET, SHOW, 和 DELETE。到这里,myextension 的后台操作就全了。
但是这样还不够,因为我们还需要一些方式来从前台发布关于 myextension 的命令,使后台的 plugin controller 可以接收到,并进行相应的操作。这就需要修改 neutronclient 的相应代码,实现通过 CLI 的交互。
通过上面的介绍,我们现在的 myplugin 文件夹看上去应该是这样的:
- neutron/
- plugins/
- myplugin/
- __init__.py
- plugin.py
- extensions/
- __init__.py
- myextension.py
- db/
- __init__.py
- db.py
- models.py
我们的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
[...]
复制代码
这些方法需要进一步的定义来做一些有实际意义的事情,这里我只是做一个最简单的举例说明。在有了上述这些文件之后,其实整个 plugin 以及 extension 的后台就已经搞定了,只是这个时候还没有任何途径来使用这些定义好的方法。所以接下来我们需要在 neutronclient 中增加一些对应的方法。
neutronclient 是一个 CLI 客户端,可以用来与 neutron 互动。每一个 OpenStack 的 project 如 nova,neutron 等都有一个类似的自己的 CLI client。首先,我们需要在 CLI 中显示关于 myextension 的命令,这个可以在 neutronclient/shell.py 中设置。这个文件有一个属性叫做 COMMAND_V2,所有可以使用的命令都以 key-value pair 的形式存储在这个变量中:
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,
...
}
复制代码
下一步就需要去 neutronclient/neutron/v2_0 中新建一个文件夹 myextension,并在其中新建两个文件: __init__.py 和 extension.py,其结构如下:
- neutronclient/
- neutron/
- v2_0/
- myextension/
- __init__.py
- extension.py
然后再 extension.py 中分别定义五个class:List/Show/Create/Delete/UpdateExtension。
上面说到需要在 /neutronclient/v2_0/myextension/extension.py 中分别定义五个 class: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命令行和API
- 如何基于rst书写openstack文档 ( by quqi99 )
- 如何通过OpenStack命令行管理VMs
- Dos命令行下,如何通过IP地址获取局域网内机器的MAC地址?
- 如何在命令行下远程建立隐藏的超级用户
- 如何在服务器端调用winzip命令行对上传的多个文件打包压缩?
- 如何在命令行模式下禁用或启用网卡
- .NET中如何执行命令行命令?
- 如何截获执行命令行的输出
- 如何在windows2000命令行中使用tab命令补全
- 如何用命令行执行loadrunner的脚本
- 如何截获执行命令行的输出
- 如何在代码中通过命令行创建SQL SERVER 数据库
- 如何用命令行上传/下载整个文件夹到ftp服务器?
- .NET中如何执行命令行命令?
- [转] 如何去掉有的opengl程序所具有的命令行窗口?
- .NET中如何执行命令行命令?
- 如何在命令行下编译一个asp.net项目
- 如何启动vs2003的.net命令行
- 如何在命令行下连接SQLServer2000