nova自定義API

以kilo版本nova為例

創(chuàng)建自定義的API有三種方式:

  • 在原有的資源上增加函數(shù)艇肴,例如在servers上增加一個(gè)接口底靠,查看虛擬機(jī)的資源利用情況
  • 添加擴(kuò)展資源壤短,定義新的擴(kuò)展資源
  • 添加核心資源,定義新的核心資源

method 1

對(duì)于第一種情況,具體可以參照

    @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請(qǐng)求body中的數(shù)據(jù)key對(duì)應(yīng)

mothod 2

添加新的擴(kuò)展資源螃宙,我們需要寫一個(gè)py文件测摔,定義一個(gè)class置济,將其放在nova.api.openstack.compute.contrib目錄下面,文件名小寫锋八,然后再文件中定義一個(gè)class浙于,類名和文件一樣,只是首字母大寫挟纱,該class要繼承于ExtensionDescriptor,并實(shí)現(xiàn)get_resources 或者get_controller_extensions

2.1 創(chuàng)建對(duì)應(yīng)的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 創(chuàng)建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 創(chuàng)建對(duì)數(shù)據(jù)庫(kù)的實(shí)現(xiàn)

# 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 數(shù)據(jù)庫(kù)升級(jí)

# 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()          

執(zhí)行命令: su -s /bin/sh -c "nova-manage db sync" nova

驗(yàn)證:


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

驗(yàn)證:

創(chuàng)建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"}'

查看數(shù)據(jù)庫(kù)

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"}'

# 確認(rèn)是否修改
[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"

# 確認(rèn)是否刪除
[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]#
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末羞酗,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子紊服,更是在濱河造成了極大的恐慌檀轨,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件欺嗤,死亡現(xiàn)場(chǎng)離奇詭異参萄,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)煎饼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門讹挎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人吆玖,你說我怎么就攤上這事筒溃。” “怎么了衰伯?”我有些...
    開封第一講書人閱讀 164,704評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵铡羡,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我意鲸,道長(zhǎng)烦周,這世上最難降的妖魔是什么尽爆? 我笑而不...
    開封第一講書人閱讀 58,702評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮读慎,結(jié)果婚禮上漱贱,老公的妹妹穿的比我還像新娘。我一直安慰自己夭委,他們只是感情好幅狮,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著株灸,像睡著了一般崇摄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上慌烧,一...
    開封第一講書人閱讀 51,573評(píng)論 1 305
  • 那天逐抑,我揣著相機(jī)與錄音,去河邊找鬼屹蚊。 笑死厕氨,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的汹粤。 我是一名探鬼主播命斧,決...
    沈念sama閱讀 40,314評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼嘱兼!你這毒婦竟也來了国葬?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,230評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤遭京,失蹤者是張志新(化名)和其女友劉穎胃惜,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體哪雕,經(jīng)...
    沈念sama閱讀 45,680評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡船殉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了斯嚎。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片利虫。...
    茶點(diǎn)故事閱讀 39,991評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖堡僻,靈堂內(nèi)的尸體忽然破棺而出糠惫,到底是詐尸還是另有隱情,我是刑警寧澤钉疫,帶...
    沈念sama閱讀 35,706評(píng)論 5 346
  • 正文 年R本政府宣布硼讽,位于F島的核電站,受9級(jí)特大地震影響牲阁,放射性物質(zhì)發(fā)生泄漏固阁。R本人自食惡果不足惜壤躲,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望备燃。 院中可真熱鬧碉克,春花似錦、人聲如沸并齐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)况褪。三九已至撕贞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間测垛,已是汗流浹背麻掸。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留赐纱,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,158評(píng)論 3 370
  • 正文 我出身青樓熬北,卻偏偏與公主長(zhǎng)得像疙描,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子讶隐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理起胰,服務(wù)發(fā)現(xiàn),斷路器巫延,智...
    卡卡羅2017閱讀 134,657評(píng)論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,811評(píng)論 6 342
  • 國(guó)家電網(wǎng)公司企業(yè)標(biāo)準(zhǔn)(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報(bào)批稿:20170802 前言: 排版 ...
    庭說閱讀 10,970評(píng)論 6 13
  • 分析需要儲(chǔ)備以下知識(shí)點(diǎn): Java注解知識(shí) Java反射知識(shí) javapoet庫(kù)(在注釋里也有簡(jiǎn)單分析) auto...
    Mapleslong閱讀 1,497評(píng)論 0 3
  • 當(dāng)秋風(fēng)開始呼嘯 樹葉開始落下 你在何方效五? 是否和我一樣期盼。 曾經(jīng)的彼此 無奈是人走茶涼 往事已成過往 驀然回首 ...
    曹琦琦閱讀 359評(píng)論 2 1