nova自定义API

以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]#
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,302评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,232评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,337评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,977评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,920评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,194评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,638评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,319评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,455评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,379评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,426评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,106评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,696评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,786评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,996评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,467评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,043评论 2 341

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,566评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,714评论 6 342
  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,836评论 6 13
  • 分析需要储备以下知识点: Java注解知识 Java反射知识 javapoet库(在注释里也有简单分析) auto...
    Mapleslong阅读 1,491评论 0 3
  • 当秋风开始呼啸 树叶开始落下 你在何方? 是否和我一样期盼。 曾经的彼此 无奈是人走茶凉 往事已成过往 蓦然回首 ...
    曹琦琦阅读 345评论 2 1