以kilo版本nova为例
创建自定义的API有三种方式:
- 在原有的资源上增加函数,例如在servers上增加一个接口,查看虚拟机的资源利用情况
- 添加扩展资源,定义新的扩展资源
- 添加核心资源,定义新的核心资源
method 1
对于第一种情况,具体可以参照
@wsgi.response(202)
@wsgi.action('revertResize')
def _action_revert_resize(self, req, id, body):
context = req.environ['nova.context']
instance = self._get_server(context, req, id)
try:
self.compute_api.revert_resize(context, instance)
except exception.MigrationNotFound:
msg = _("Instance has not been resized.")
raise exc.HTTPBadRequest(explanation=msg)
except exception.FlavorNotFound:
msg = _("Flavor used by the instance could not be found.")
raise exc.HTTPBadRequest(explanation=msg)
except exception.InstanceIsLocked as e:
raise exc.HTTPConflict(explanation=e.format_message())
except exception.InstanceInvalidState as state_error:
common.raise_http_conflict_for_instance_invalid_state(state_error,
'revertResize', id)
return webob.Response(status_int=202)
访问它的curl为:
curl -XPOST http://192.168.138.122:8774/v2/321c7446162e431f91c69b60e64d605f/servers/97f532c4-9335-47e9-8d1c-9ba1da3666bf/action -H "Content-type: application/json" -H "X-Auth-Token: 4bd43818ff7547d0ad92bae071bd4973" -d '{"revertResize": null}'
@wsgi.action装饰器里的名字与http请求body中的数据key对应
mothod 2
添加新的扩展资源,我们需要写一个py文件,定义一个class,将其放在nova.api.openstack.compute.contrib目录下面,文件名小写,然后再文件中定义一个class,类名和文件一样,只是首字母大写,该class要继承于ExtensionDescriptor,并实现get_resources 或者get_controller_extensions
2.1 创建对应的objects
# nova/objects/documents.py
from nova import db
from nova.objects import base
from nova.objects import fields
class Documents(base.NovaPersistentObject, base.NovaObject,
base.NovaObjectDictCompat):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'id': fields.IntegerField(),
'name': fields.StringField(nullable=False),
}
def __init__(self, *args, **kwargs):
super(Documents, self).__init__(*args, **kwargs)
self.obj_reset_changes()
@base.remotable_classmethod
def get_by_id(cls, context, id):
return db.document_get_by_id(
context, id)
@base.remotable_classmethod
def get_all(cls, context):
return db.document_get_all(context)
@base.remotable_classmethod
def delete_by_id(cls, context, id):
db.document_delete_by_id(context, id)
@base.remotable_classmethod
def update(cls, context, id, names):
db.document_update(context, id, names)
@base.remotable_classmethod
def create(self, context, values):
db_document = db.document_create(context, values)
2.2 创建Models
# nova/db/sqlalchemy/models.py
class Documents(BASE, NovaBase):
__tablename__ = 'documents'
id = Column(Integer, primary_key=True)
name = Column(String(45), nullable=False)
2.3 创建对数据库的实现
# nova/db/api.py
def document_get_by_id(context, id):
"""Get document by id."""
return IMPL.document_get_by_id(context, id)
def document_get_all(context):
"""Get all documents."""
return IMPL.document_get_all(context)
def document_delete_by_id(context, id):
"""Delete a document record."""
return IMPL.document_delete_by_id(context, id)
def document_update(context, id, name):
"""Update a document record."""
return IMPL.document_update(context, id, name)
def document_create(context, values):
"""Create a document record."""
return IMPL.document_create(context, values)
# nova/db/sqlalchemy/api.py
def document_get_by_name(context, name):
document_ref = model_query(context, models.Documents).\
filter_by(name=name).\
first()
if not document_ref:
raise exception.DocumentsNotFoundByName(name=name)
return document_ref
def document_get_by_id(context, id):
document_ref = model_query(context, models.Documents).\
filter_by(id=id).\
first()
if not document_ref:
raise exception.DocumentsNotFoundById(id=id)
return document_ref
def document_get_all(context):
return model_query(context, models.Documents).all()
def document_delete_by_id(context, id):
result = model_query(context, models.Documents).\
filter_by(id=id).\
soft_delete()
if not result:
raise exception.DocumentsNotFoundById(id=id)
def document_update(context, id, name):
values = {}
query = model_query(context, models.Documents, session=None,
read_deleted="no").\
filter_by(id=id)
values['name'] = name
values['updated_at'] = timeutils.utcnow()
query.update(values)
def document_create(context, values):
document_ref = models.Documents()
document_ref.update(values)
document_ref.save()
2.4 数据库升级
# nova/db/sqlalchemy/migrate_repo/versions/306_add_documents.py
from migrate.changeset import UniqueConstraint
from sqlalchemy import Column
from sqlalchemy import DateTime
from sqlalchemy import Integer
from sqlalchemy import MetaData
from sqlalchemy import String
from sqlalchemy import Table
def upgrade(migrate_engine):
meta = MetaData()
meta.bind = migrate_engine
columns = [
(('created_at', DateTime), {}),
(('updated_at', DateTime), {}),
(('deleted_at', DateTime), {}),
(('deleted', Integer), dict(default=0)),
(('id', Integer), dict(primary_key=True, nullable=False)),
(('name', String(length=45)), {})
]
basename = 'documents'
_columns = [Column(*args, **kwargs) for args, kwargs in columns]
table = Table(basename, meta, *_columns,
mysql_engine='InnoDB',
mysql_charset='utf8')
table.create()
def downgrade(migrate_engine):
meta = MetaData()
meta.bind = migrate_engine
table_name = 'documents'
if migrate_engine.has_table(table_name):
instance_extra = Table(table_name, meta, autoload=True)
instance_extra.drop()
执行命令: su -s /bin/sh -c "nova-manage db sync" nova
验证:
root@localhost:(none) 11:20:57>use nova;
Database changed
root@localhost:nova 11:21:09>desc documents;
+------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+----------------+
| created_at | datetime | YES | | NULL | |
| updated_at | datetime | YES | | NULL | |
| deleted_at | datetime | YES | | NULL | |
| deleted | int(11) | YES | | NULL | |
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(45) | YES | | NULL | |
+------------+-------------+------+-----+---------+----------------+
6 rows in set (0.01 sec)
2.5 添加异常
# nova/exception.py
class DocumentsNotFoundByName(NotFound):
msg_fmt = _("Documents %(name)s not found.")
class DocumentsNotFoundById(NotFound):
msg_fmt = _("Documents %(id)s not found.")
2.6 添加documents.py
import webob
from nova import db
from nova import exception
from nova.objects import documents
from nova.api.openstack import extensions
from nova.api.openstack import wsgi
authorize = extensions.extension_authorizer('compute', 'documents')
class DocumentsController(wsgi.Controller):
"""the Documents API Controller declearation"""
def index(self, req):
documents_dict = {}
documents_list = []
context = req.environ['nova.context']
authorize(context)
document = documents.Documents.get_all(context)
if document:
for single_document in document:
id = single_document['id']
name = single_document['name']
document_dict = dict()
document_dict['id'] = id
document_dict['name'] = name
documents_list.append(document_dict)
documents_dict['document'] = documents_list
return documents_dict
def create(self, req, body):
values = {}
context = req.environ['nova.context']
authorize(context)
id = body['id']
name = body['name']
values['id'] = id
values['name'] = name
try:
documents.Documents.create(context, values)
except :
raise webob.exc.HTTPNotFound(explanation="Document not found")
return webob.Response(status_int=202)
def show(self, req, id):
documents_dict = {}
context = req.environ['nova.context']
authorize(context)
try:
document = documents.Documents.get_by_id(context, id)
except :
raise webob.exc.HTTPNotFound(explanation="Document not found")
documents_dict['document'] = document
return documents_dict
def update(self, req, id, body):
context = req.environ['nova.context']
authorize(context)
name = body['name']
try:
documents.Documents.update(context, id, name)
except :
raise webob.exc.HTTPNotFound(explanation="Document not found")
return webob.Response(status_int=202)
def delete(slef, req, id):
context = req.environ['nova.context']
authorize(context)
try:
document = documents.Documents.delete_by_id(context, id)
except :
raise webob.exc.HTTPNotFound(explanation="Document not found")
return webob.Response(status_int=202)
class Documents(extensions.ExtensionDescriptor):
"""Documents ExtensionDescriptor implementation"""
name = "documents"
alias = "os-documents"
namespace = "www.www.com"
updated = "2017-06-14T00:00:00+00:00"
def get_resources(self):
"""register the new Documents Restful resource"""
resources = [extensions.ResourceExtension('os-documents',
DocumentsController())
]
return resources
验证:
创建documents记录
curl -XPOST http://10.160.57.85:8774/v2/4e4bed4036b446e996ad99fc5522f086/os-documents -H "Content-Type: application/json" -H "X-Auth-Token: 6e336e1b3121440891c06150fb3bd757" -d '{"id": 1, "name": "test1"}'
curl -XPOST http://10.160.57.85:8774/v2/4e4bed4036b446e996ad99fc5522f086/os-documents -H "Content-Type: application/json" -H "X-Auth-Token: 6e336e1b3121440891c06150fb3bd757" -d '{"id": 2, "name": "test2"}'
查看数据库
root@localhost:nova 05:33:55>select * from documents;
+---------------------+------------+------------+---------+----+-------+
| created_at | updated_at | deleted_at | deleted | id | name |
+---------------------+------------+------------+---------+----+-------+
| 2017-06-14 09:33:36 | NULL | NULL | 0 | 1 | test1 |
| 2017-06-14 09:34:20 | NULL | NULL | 0 | 2 | test2 |
+---------------------+------------+------------+---------+----+-------+
查看所有documents记录
[root@controller1 contrib]# curl -XGET http://10.160.57.85:8774/v2/4e4bed4036b446e996ad99fc5522f086/os-documents -H "Content-Type: application/json" -H "X-Auth-Token: 6e336e1b3121440891c06150fb3bd757"
{"document": [{"id": 1, "name": "test1"}, {"id": 2, "name": "test2"}]
修改一条documents记录
[root@controller1 contrib]# curl -XPUT http://10.160.57.85:8774/v2/4e4bed4036b446e996ad99fc5522f086/os-documents/1 -H "Content-Type: application/json" -H "X-Auth-Token: 6e336e1b3121440891c06150fb3bd757" -d '{"name": "new-test1"}'
# 确认是否修改
[root@controller1 contrib]# curl -XGET http://10.160.57.85:8774/v2/4e4bed4036b446e996ad99fc5522f086/os-documents -H "Content-Type: application/json" -H "X-Auth-Token: 6e336e1b3121440891c06150fb3bd757"
{"document": [{"id": 1, "name": "new-test1"}, {"id": 2, "name": "test2"}]}
删除一条documents记录
[root@controller1 contrib]# curl -XDELETE http://10.160.57.85:8774/v2/4e4bed4036b446e996ad99fc5522f086/os-documents/1 -H "Content-Type: application/json" -H "X-Auth-Token: 6e336e1b3121440891c06150fb3bd757"
# 确认是否删除
[root@controller1 contrib]# curl -XGET http://10.160.57.85:8774/v2/4e4bed4036b446e996ad99fc5522f086/os-documents -H "Content-Type: application/json" -H "X-Auth-Token: 6e336e1b3121440891c06150fb3bd757"
{"document": [{"id": 2, "name": "test2"}]}[root@controller1 contrib]#