OpenStack新手开发指南
更新时间:2024-05-12 08:49:01 阅读量: 综合文库 文档下载
OpenStack新手开发指南
目录
1、nova源码架构介绍2 1.1、源码的获取2 1.2、开发环境的搭建2 1.3、nova模块调用介绍6 1.4、nova源码模块功能介绍7 2、数据库表结构的扩展8 2.1、nova表结构的扩展8
2.2、keystone表结构的扩展10 3、resetful接口服务的扩展12
3.1、nova数据库调用接口服务的扩展12 3.2、compute接口的扩展17
3.3、keystone接口服务的扩展20
3.4、基于openstack服务、配置架构自定义服务模块21 4、dashboard源码介绍25 4.1 horizon代码模块介绍26 4.2 中文化的功能实现26 4.3 页面按钮的添加27
4.4 列表中下拉菜单的添加29 4.5 列表中文字链接的添加29
1、nova源码架构介绍 1.1、源码的获取
openstack的源码可以从安装好的openstack环境上直接copy下来使用,也可以从官网下载(红帽地址:http://repos.fedorapeople.org/repos/openstack/),ubuntu环境下openstack的源码目录为:/usr/share/pyshared/,这里面的源码安装好后会被外链到
/usr/lib/python2.7/dist-packages/,这是python2.7以后的版本的做法,所有的python项目都会采取这种存储方式;centos环境下openstack一般会安装python2.6的环境,所以openstack的源码存储在/usr/lib/python2.6/site-packages/。
1.2、开发环境的搭建
第一步、从官网下载python安装包(http://www.python.org/getit/),如果是openstack的环境下开发,可以忽略这一步,因为安装openstack的时候已经自动安装了python环境。 第二步、然后安装开发python程序的IDE,python的程序一般使用PyDev作为开发工具,它可以被集成到eclipse和aptana等IDE中。PyDev的下载地址:
http://pydev.org/download.html,aptana的下载地址:http://aptana.org/,当前发布的是aptana3.0,默认已经集成了PyDev。当前我使用的是aptana,由于3.0使用的面板背景色是黑色的,我又习惯使用2.0的风格怎么办那,3.0有添加了一个颜色模板的菜单,选择一下“Aptana Studio 2.x”就可以了,如下图。
第三步、导入openstack的项目,打开IED,在左边的面板空白处右键,左键点击“import”, 如下图:
从弹出的窗口从选择“Existing Folder As New Project”,导入一个文件夹作为一个工程,如下图所示:
然后选择一个openstack项目,比如我当前选择了nova文件夹,起一个工程名,只要自己好记就行,下边的单选框可以不用选,点击完成就行了,如下图:
导入进来的nova目录结构如下图:
1.3、nova模块调用介绍
nova-api :起到Cloud Controller的作用,主要为所有的API查询提供了一个接口(比如Openstack API ,EC2 API),引发多数业务流程的活动(如运行一个实例),并实施一些政策(主要是配额检查)。
nova-schedule :接受一个消息队列的虚拟实例请求,通过算法决定该请求应该在那台主机上运行,这个算法可以由我们指定。即起到调度器(Scheduler)的作用.
nova-compute:是一个非常重要的守护进程,负责创建和终止虚拟机实例,即管理着虚拟机实例的生命周期。该模块内部非常复杂,基本原理是简单的,就是接受来自队列的动作然后执行一些列的系统操作(如启动一个KVM实例),并且更新数据库的状态。 nova-network :该守护进程跟nova-compute and nova-volume 2个模块的功能是相似的。接受来自队列的任务,然后执行相应的任务对网络进行操作(比如:安装网桥接口和改变iptable规则)
Queue:为各个模块之间的通信提供起到一个集线器的作用,即数据交换中心。目前是采用RabbitMQ ,理论上是可以采用任何的基于python ampqlib的AMPQ message queue。 SQL database: 存储云基础设施构建时和运行时状态。包括可用的实例类型,正在使用的实例类型,可用的网络和项目。理论上,OpenStack Compute是支持所有基于 SQL-Alchemy的数据库,但目前广泛使用的数据库主要是Sqlite3,Mysql,PostgreSQL。
Glance:该项目独立于Openstack Compute,起到镜像的作用。在该项目中,主要包括三个部分: glance-api, glance-registry and 镜像存储。Glance-api接受API调用,
glance-registry存储和检索镜像的元数据。镜像存储Image blobs。存储可以选择不同的存储方案,比如用Swift实现存储。
Dashboard:该项目是一个可选的项目,主要是为开发者等提供API。
1.4、nova源码模块功能介绍
2、数据库表结构的扩展 2.1、nova表结构的扩展
第一步、创建表的数据库结构,创建表结构的文件存在于目录nova.db.sqlalchemy.migrate_repo.versions,当执行nova-manage db sync命令时就会按照这个目录下文件开头的序列号顺序执行。添加表时只需要实现一个类似的文件,把名字的开头命名为最大序列值加1。比如我要创建一张表,表名为:domains,在这个versions下创建一个084_add_domains.py的文件,文件内容如下 #-*- coding: utf-8 -*-
from sqlalchemy import Boolean, Column, DateTime, Integer from sqlalchemy import MetaData, String, Table from nova import log as logging
LOG = logging.getLogger(__name__)
def upgrade(migrate_engine): ''' 创建表 '''
meta = MetaData()
meta.bind = migrate_engine #生成创建表sql语句
domains = Table('domains', meta,
Column('id', Integer(), primary_key=True, nullable=False), Column('name',
String(length=255, convert_unicode=False, assert_unicode=None,
unicode_error=None, _warn_on_bytestring=False)), Column('ttl', Integer()), Column('rdtype',
String(length=255, convert_unicode=False, assert_unicode=None,
unicode_error=None, _warn_on_bytestring=False)), Column('rdata',
String(length=255, convert_unicode=False, assert_unicode=None,
unicode_error=None, _warn_on_bytestring=False)), Column('project_id',
String(length=255, convert_unicode=False, assert_unicode=None,
unicode_error=None, _warn_on_bytestring=False)), Column('created_at', DateTime(timezone=False)), Column('updated_at', DateTime(timezone=False)), mysql_engine='InnoDB' ) try:
domains.create() except Exception, e:
LOG.exception('Exception while creating domains table, error' % str(e)) raise
#构造初始化信息
DOMAINDS = {
'NS': dict(name='test.com', rdata='ns0.test.com.', rdtype='NS', ttl=86400),
'SOA': dict(name='test.com', rdata='test.com. www.test.com. 200309181 28800 7200 86400 28800', rdtype='SOA', ttl=86400)} try:
#逐行插入初始化信息
domain_insert = domains.insert()
for name, values in DOMAINDS.iteritems():
domain_insert.execute({'name': values[\ 'rdata': values[\ 'rdtype': values[\ 'ttl': values[\ except Exception, e:
LOG.exception('Exception while seeding domains table, error: %s' % str(e)) raise
def downgrade(migrate_engine): ''' 删除表 ''' meta = MetaData()
meta.bind = migrate_engine
domains = Table('domains', meta, autoload=True) domains.drop()
第二步、生成表结构的模型,nova的表对象模型存在于
nova.db.sqlalchemy.migrate_repo.models.py文件中,需要在这里面添加一个实现类,位置无所谓,但一般新添加的都放到文件的最末尾,这里我添加一个domains的模型类,代码如下:
class Domain(BASE, PhysicalBase): \虚拟机主机名 \
__tablename__ = \
id = Column(Integer, primary_key=True) name = Column(String(255)) ttl = Column(Integer)
rdtype = Column(String(255)) rdata = Column(String(255)) project_id = Column(String(255))
第三步、实现表查询,因为nova使用的事sqlalchemy,所以查询只需要按照它的规则构造就行,数据库查询的方法都在nova.db.sqlalchemy.api.py文件中,可以实现增删改查功能,下边我实现一个创建功能的语句,代码如下:
@require_admin_context
def domain_create(context, values):
if model_query(context, models.Domain, read_deleted=\
filter(and_(models.Domain.name == values['name'], models.Domain.rdata == values['rdata'])).\\
count() == 0:
domain = models.Domain() domain.update(values)
domain.project_id = context.project_id domain.save() return domain else:
return None
第四步、添加一个表查询语句的封装结构,为了安全起见,nova的其它模块不直接调用执行数据库查询的方法,而是调用一个上层封装的方法。在nova.db.api.py文件中添加一行调用就行,nova的其它模块调用数据库接口都是引用的这个文件,代码如下: def domain_create(context, values):
return IMPL.domain_create(context, values)
2.2、keystone表结构的扩展
Keystone的表结构扩展相对容易,虽然数据库中间件使用的也是sqlalchemy,但是在
keystone中只需要在keystone.identity.backends.sql.py文件中实现一个抽象类就可以,使用命令执行keystone-manage db_sync后表结构就可以正常创建。
第一步,构建表结构,现在我添加一个表tenant_key,实现的代码如下: class TenantKey(sql.ModelBase, sql.DictBase): __tablename__ = 'tenant_key'
id = sql.Column(sql.String(64), primary_key=True) tenant_id = sql.Column(sql.String(64), unique=True) extra = sql.Column(sql.JsonBlob())
@classmethod
def from_dict(cls, tenant_dict):
# shove any non-indexed properties into extra extra = {}
for k, v in tenant_dict.copy().iteritems(): # TODO(termie): infer this somehow if k not in ['id', 'tenant_id', 'extra']: extra[k] = tenant_dict.pop(k)
tenant_dict['extra'] = extra return cls(**tenant_dict)
def to_dict(self):
extra_copy = copy.deepcopy(self.extra) extra_copy['id'] = self.id
extra_copy['tenant_id'] = self.tenant_id
return extra_copy
keystone中每个表都需要有extra这个字段,内容最多两个元素的字典数据,在接口调用时会把字典中的键值构造成表的字段,对应值的数据类型也会原封不变的恢复。比如一条记录extra的值为:{“addresses”: [“192.168.71.101”, “192.168.71.102”]},值是一个列表,存到数据库是原样存储的,接口返回时会还会被构造为一个列表。
每个实体类都有from_dict、to_dict两个方法,这个是用来接口和数据库进行数据转换时用的函数。
第二步,添加查询语句,查询语句需要在下边的Identity类中添加,下边是实现的创建tenant_key记录的代码:
def create_tenant_key(self, tenant_id): session = self.get_session() with session.begin():
tenant_key_ref = TenantKey()
tenant_key_ref.id = uuid.uuid4().hex tenant_key_ref.tenant_id = tenant_id
tenant_key_ref.extra = {'public_key': public_key} session.add(tenant_key_ref)
session.flush()
return tenant_key_ref.to_dict()
第三步,添加调用封装,和nova的类似,需要加一层封装,这里的封装更像是一个java中的接口方法的添加,在keystone.identity.core.py的Driver添加一个方法,以供上层模块调用数据库接口,如果只是在backends.sql.py中内部调用的话,此步可以省略。
3、resetful接口服务的扩展
3.1、nova数据库调用接口服务的扩展
Openstack的所有模块之间的交互都是采用reset接口的方式实现的,所以掌握模块reset接口的扩展至关重要,而接口的扩展中数据库调用的接口相对容易实现。
Nova的接口服务都是在nova.api.openstack下边实现,而对于扩展或者发行版来说,
openstack的版本升级是很大的一个隐患,所以接口的扩展一般都独立写成一个文件,或者一个单独的类、方法。
nova.api.openstack下边有两个模块,一个是compute这个主要是虚拟机和宿主机相关的接口实现,volume是快存储相关接口的实现。
第一步,实现接口的数据库调用模块,现在实现一个宿主机相关的接口文件,在nova.api.openstack.compute下边添加一个文件:machines.py,实现一个查询所有machine实例列表接口,代码如下: #-*- coding:utf-8 -*-
import webob.exc
from nova.api.openstack import common
from nova.api.openstack.compute.views import machines as views_machines from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova import exception from nova import flags from nova import db
from nova import log as logging
LOG = logging.getLogger(__name__) FLAGS = flags.FLAGS
def make_machine(elem, detailed=False): elem.set('floating_ip') elem.set('name') elem.set('id')
if detailed:
elem.set('updated')
elem.set('created')
class MinimalMachinesTemplate(xmlutil.TemplateBuilder): def construct(self):
root = xmlutil.TemplateElement('machines')
elem = xmlutil.SubTemplateElement(root, 'machine', selector='machines') make_machine(elem)
xmlutil.make_links(root, 'machines_links')
return xmlutil.MasterTemplate(root, 1, nsmap=machine_nsmap)
class MachinesTemplate(xmlutil.TemplateBuilder): def construct(self):
root = xmlutil.TemplateElement('machines')
elem = xmlutil.SubTemplateElement(root, 'machine', selector='machines') make_machine(elem, detailed=True)
return xmlutil.MasterTemplate(root, 1, nsmap=machine_nsmap)
class Controller(wsgi.Controller):
_view_builder_class = views_machines.ViewBuilder
def __init__(self, **kwargs):
super(Controller, self).__init__(**kwargs)
@wsgi.serializers(xml=MinimalMachinesTemplate) def index(self, req):
\获取machines所有实例名字信息 \ context = req.environ['nova.context']
try:
all_machines = db.machine_get_all(context) except exception.Invalid as e:
raise webob.exc.HTTPBadRequest(explanation=str(e)) return self._view_builder.index(req, all_machines)
@wsgi.serializers(xml=MachinesTemplate) def detail(self, req):
\获取machine所有列表的详细字段信息 \
context = req.environ['nova.context'] try:
all_machines = db.machine_get_all(context) except exception.Invalid as e:
raise webob.exc.HTTPBadRequest(explanation=str(e))
return self._view_builder.detail(req, all_machines)
def create_resource():
return wsgi.Resource(Controller())
MinimalMachinesTemplate、MachinesTemplate两个类用来进行接口数据输出时json格式的转化,先使用xml定义json的模板。 Index方法将在默认调用时显示,detail显示详细的数据参数,调用时在url的最后添加/detail就行。
req.environ['nova.context']用来获取请求头中的用户信息,只有符合权限的用户才能调用相应接口,返回对应用户的数据信息。
machine_get_all是nova.db.api.py中的方法,从文件最上边的模块导入中可以看到。 views_machines.ViewBuilder用来在接口返回时向模板填充数据
第二步,实现接口调用后的展示部分, 在nova.api.openstack.compute.views下添加machines.py文件,代码如下: #-*- coding:utf-8 -*-
import os.path
from nova.api.openstack import common from nova import flags from nova import utils
FLAGS = flags.FLAGS
class ViewBuilder(common.ViewBuilder):
_collection_name = \
def basic(self, request, machine): return {
\
\
\ \
\ }, }
def show(self, request, machine): machine2dict = {
\
'name': machine.get(\domain_name\
'floating_ip': machine.get(\
\ \ \ }
return dict(machine=machine2dict)
def detail(self, request, machines):
list_func = self.show
return self._list_view(list_func, request, machines)
def index(self, request, machines): list_func = self.basic
return self._list_view(list_func, request, machines)
def _list_view(self, list_func, request, machines):
machine_list = [list_func(request, machine)[\ machines_links = self._get_collection_links(request, machines) machines_dict = dict(machines=machine_list)
if machines_links:
machines_dict[\
return machines_dict
def _get_links(self, request, identifier):
\ return [{
\
\ }, {
\
\
@staticmethod
def _format_time(date_string):
\ if date_string is not None:
return date_string.strftime('%Y-%m-%d %H:%M:%S')
这个文件就属于web框架中MVC中的页面展示部分
第三步、实现访问地址和控制器的路由配置,在nova.api.openstack.compute.__init__.py文件中,添加如下代码:
from nova.api.openstack.compute import machines
self.resources['machines'] = machines.create_resource() mapper.resource(\controller=self.resources['machines'], collection={'detail': 'GET'})
第四步、重启nova-api,执行/etc/init.d/nova-api restart
第五步、调试接口是否可以正常使用,执行命令:curl -X GET -H
\http://192.168.70.201:8774/v1.1/ ... 178cd1079d/machines,将会调用nova.api.openstack.compute.machines.py的index方法
curl -X GET -H
\http://192.168.70.201:8774/v1.1/ ... 79d/machines/detail 将会调用nova.api.openstack.compute.machines.py的detail方法,正常情况下将会返回数据库中的信息
第六步、编写python-novaclient客户端程序,写好的api接口怎么让horizon使用哪,这就需要python的客户端帮忙了,这部分的代码相对比较简单。找到novaclient所在的目录,python2.6可以在/usr/lib/python2.6/site-packages/novaclient中找到源码,在novaclient.v1.1目录下添加machines.py文件,代码如下: #-*- coding:utf-8 -*-
from novaclient import base
class Machine(base.Resource): HUMAN_ID = True
def __repr__(self):
return \
class MachineManager(base.ManagerWithFind): \
Manage :class:`machine` resources. \
resource_class = Machine
def list(self, detailed=True): if detailed is True:
return self._list(\ else:
return self._list(\
然后在novaclient.v1.1.client.py中导入machines文件,from novaclient.v1_1 import machines的__init__构造函数中添加:self.machines = machines.MachineManager(self) 第七步,horizon的接口调用都会在进行一次封装,在horizon.api.nova.py中添加代码如下: def machine_list(request):
return novaclient(request).machines.list()
现在为止一个完整的nova接口就算完成了,剩下的就是horizon代码里面的调用,这个下边会将。
3.2、compute接口的扩展
Compute的接口实现是openstack当中层级最多的,因为他是属于多节点的调用,用到了rabbitMQ任务调度模块,底层又调用了libvirt接口,所以实现起来可能有点麻烦,但是理清了各个文件的职能,那就只剩下往里面填充代码了,下边是一个虚拟机操作的一个接口调用流程图:
现在我按照这个流程实现一个虚拟机快照功能,接口一般从底层往
上写,这样可以一级级调试,代码写完就可以运行,避免最后代码都写完了,一旦出错就不知道到底是哪一层出的问题。
第一步,编写快照功能的底层实现,在nova.virt.libvirt.connection.py的LibvirtConnection类中实现一个快照的方法,代码如下:
@exception.wrap_exception()
def create_vm_snapshot(self, context, instance, snapshot_name, snapshot_description): try:
#获取虚拟机实例
virt_dom = self._lookup_by_name(instance['name']) except exception.InstanceNotFound:
raise exception.InstanceNotRunning()
try:
xmldesc = '\\
' % (snapshot_name, snapshot_description, instance['uuid']) LOG.info('snapshot xml: %s' % xmldesc)
#创建虚拟机快照
snapshot_dom = virt_dom.snapshotCreateXML(xmldesc.encode('utf-8', 'ignore'), 0)
if snapshot_dom is None:
LOG.error('create instance %s snapshot %s is error' % (instance['name'], snapshot_name)) else:
LOG.info('create snapshot %s is successful' % snapshot_name) except Exception, e:
LOG.error('create snapshot is error: %s' % str(e))
raise exception.CreateSnapshotFailed(snapshot_name=snapshot_name)
第二步,在nova.compute.manager.py中构造调用底层实现的参数,manager.py这个文件在每个compute节点都会运行,所以如果多节点的环境,进行代码更新时要全部更新,实现代码如下:
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) @checks_instance_lock @wrap_instance_fault
def create_vm_snapshot(self, context, instance_uuid, snapshot_name, description): \ context = context.elevated()
instance_ref = self.db.instance_get_by_uuid(context, instance_uuid)
current_vm_state = instance_ref['vm_state'] self._instance_update(context,
instance_ref['id'],
task_state=task_states.SNAPSHOTTING, vm_state=vm_states.SNAPSHOTTING)
LOG.audit(_(\ self._notify_about_instance_usage(instance_ref, \
try:
self.driver.create_vm_snapshot(context, instance_ref, snapshot_name, description) finally:
self._instance_update(context, instance_ref['id'], task_state=None, vm_state=current_vm_state)
self._notify_about_instance_usage(instance_ref, \
第三步、完成快照功能的接口封装,在nova.compute.api.py的API类中实现方法的封装,代码如下:
def create_vm_snapshot(self, context, instance, snapshot_name, description): params = {'snapshot_name': snapshot_name, 'description': description}
self._cast_compute_message('create_vm_snapshot', context, instance, params=params) 以下的工作就是nova.api.openstack.compute层以上的工作了,3.1章节已经讲过,这里就不在描述。nova其它模块的接口实现流程类似,实现方式也是类似的,可以参考compute接口去实现。
3.3、keystone接口服务的扩展 Keystone接口扩展相对容易,一是因为底层主要是调用数据库,二是它不涉及多节点架构。在2.2章节keystone表结构扩展时,创建了一个tenant_key的表,下面就实现一个获取表数据实例的接口。
第一步、实现一个表查询接口,在keystone.identity.core.py的Driver类中添加一个抽象接口,代码如下:
def get_tenant_key(self, tenant_id): raise exception.NotImplemented()
传递一个tenant_id参数用来获取一个对象实例
第二步、在keystone.identity.backends.sql.py的Identity类中添加sql对象查询实现,代码如下:
def get_tenant_key(self, tenant_id): session = self.get_session()
tenant_key_ref = session.query(TenantKey).filter_by(tenant_id=tenant_id).first() if not tenant_key_ref: return
return tenant_key_ref.to_dict()
第三步、实现服务接口的调用封装,在keystone.identity.core.py的TenantController类中添加一个方法,代码如下:
def get_tenant_key(self, context, tenant_id): self.assert_admin(context)
tenant_key = self.identity_api.get_tenant_key(context, tenant_id) return {'tenant_key': tenant_key} 第四步、添加接口路由匹配规则,在keystone.identity.core.py的AdminRouter,add_routes方法中,添加如下代码:
mapper.connect('/tenant_keys/{tenant_id}', controller=tenant_controller, action='get_tenant_key',
conditions=dict(method=['GET']))
第五步、验证接口的可用性,执行命令curl -X GET -H
\
\http://192.168.70.201:35357/v2.0/tenant_keys/8a9b447e1f4e466ab3cc5557110e0fed,如果可以正常返回数据库中对应的数据,服务端的接口就算可以用了,剩下的工作和novaclient的做法类似,可以参考novaclient的实现来扩展keystoneclient的接口。
3.4、基于openstack服务、配置架构自定义服务模块
使用openstack中已有的代码,稍加改动就可以实现一个服务架构模块。这部分改动比较凌乱,下面讲的不能完全涵盖要做的工作,剩下的都是些python相关的代码调整,需要有一定的python基础的人自己解决。
做这个工作之前,我研究了nova、keystone、swift、glance的服务代码,发现nova部分要整合多个模块(nova-compute、nova-network、nova-volume等),架构比较复杂,keystone、swift和本身的业务集成太紧密,耦合度太强,不好分离。Glance的服务模块相对容易分离,本身glance提供的接口也不太多,代码的研究也很容易。这几个模块的服务提供机制,最底层都是一样的基于一个wsgi.py模块,里面的内容稍微有点差异,多的是在上层的封装上有些不同,下面我们讲怎么基于glance的服务提供代码,构造自己的服务模块架构。 第一步、拷贝glance相关代码到自己的模块根目录中,创建一个根目录,我这里叫“monitor”,自己的监控模块,拷贝glance. __init__.py、glance. vcsversion.py、glance. version.py、glance.api、glance.common到monitor文件夹下。
第二步、删除不相关的文件,上一步拷贝的monitor.api、monitor.common中有些文件是不需要的,也有些是glance用到的,都要清除掉。删除monitor.api.middleware、monitor.api. cached_images.py、monitor.api.policy.py、monitor.api.v1.filters.py、monitor.api.v1.images.py、monitor.api.v1.members.py、monitor.api.common.animation.py、monitor.api.common. crypt.py、monitor.api.common. policy.py。
第三步、修改源码中的包引用代码,把from glance 改为 from monitor,删除不存在的包引用,删除相关的代码调用。
第四步、拷贝相关配置、启动脚本,拷贝/etc/glance/glance-api.conf内容到
monitor.etc.monitor. monitor-api.conf,删除不相关的配置,修改bind_port的端口为9009(根据自己机器上的端口划分分配),下面是必须的配置项: [DEFAULT]
# Show more verbose log output (sets INFO log level output) verbose = True
# Show debugging output in logs (sets DEBUG log level output) debug = True
# Address to bind the API server bind_host = 0.0.0.0
# Port the bind the API server to bind_port = 9009
# Log to this file. Make sure you do not set the same log # file for both the API and registry servers! log_file = /var/log/monitor/api.log
# Send logs to syslog (/dev/log) instead of to file specified by `log_file` use_syslog = False
拷贝/etc/glance/ glance-api-paste.ini内容到monitor.etc.monitor. monitor-api-paste.ini,删除不相关的配置,在keystone中创建monitor账户,修改admin_user的值为monitor,正确的配置项如下:
[pipeline:monitor-api]
pipeline = versionnegotiation authtoken context apiv1app
[app:apiv1app]
paste.app_factory = monitor.common.wsgi:app_factory monitor.app_factory = monitor.api.v1.router:API
[filter:versionnegotiation]
paste.filter_factory = monitor.common.wsgi:filter_factory
monitor.filter_factory =
monitor.api.middleware.version_negotiation:VersionNegotiationFilter
[filter:context]
paste.filter_factory = monitor.common.wsgi:filter_factory
monitor.filter_factory = monitor.common.context:ContextMiddleware
[filter:authtoken]
paste.filter_factory = keystone.middleware.auth_token:filter_factory service_host = control.n.glodon.org service_port = 5000 service_protocol = http
auth_host = control.n.glodon.org auth_port = 35357
auth_protocol = http
auth_uri = http://control.n.glodon.org:5000/ admin_tenant_name = tenant admin_user = monitor admin_password = service
拷贝/usr/bin/glance-api内容到monitor.usr.bin.monitor-api文件,修改后的内容如下: #!/usr/bin/python
\
Monitor API Server \
action_past = _(\
data_type_singular = _(\ data_type_plural = _(\ classes = ('btn-danger', 'btn-reboot')
def allowed(self, request, instance=None):
return instance.status in ACTIVE_STATES or instance.status == 'SHUTOFF'
def action(self, request, obj_id):
api.server_reboot(request, obj_id)
第二步、在列表中添加按钮,编辑tables.py文件的ImagesTable内部类的table_actions属性。
4.4 列表中下拉菜单的添加
首先要实现一个有效的链接地址,然后添加一个按钮模板,这部分操作和上边讲的链接按钮一样的,最后在tables.py的row_actions中将这个按钮名添加进去就行了。
4.5 列表中文字链接的添加 首先实现一个有效的链接地址,然后在tables.py对应实例的属性中添加这个链接地址即可,下边是虚拟机的一个链接地址代码:
class InstancesTable(tables.DataTable):
name = tables.Column(\ \ verbose_name=_(\
正在阅读:
OpenStack新手开发指南05-12
总包单位安全生产责任制03-15
PECVD-的原理与分析10-13
伏羲虎文化与彝族八卦初探03-05
质量保证措施12-03
《中医皮肤性病学》(临床版)出版消息05-30
岗位实务知识试卷答案05-13
最新公务员申论十佳开头(免费)05-01
- 多层物业服务方案
- (审判实务)习惯法与少数民族地区民间纠纷解决问题(孙 潋)
- 人教版新课标六年级下册语文全册教案
- 词语打卡
- photoshop实习报告
- 钢结构设计原理综合测试2
- 2014年期末练习题
- 高中数学中的逆向思维解题方法探讨
- 名师原创 全国通用2014-2015学年高二寒假作业 政治(一)Word版
- 北航《建筑结构检测鉴定与加固》在线作业三
- XX县卫生监督所工程建设项目可行性研究报告
- 小学四年级观察作文经典评语
- 浅谈110KV变电站电气一次设计-程泉焱(1)
- 安全员考试题库
- 国家电网公司变电运维管理规定(试行)
- 义务教育课程标准稿征求意见提纲
- 教学秘书面试技巧
- 钢结构工程施工组织设计
- 水利工程概论论文
- 09届九年级数学第四次模拟试卷
- 开发指南
- OpenStack
- 新手
- 毕业设计
- 上海市外滩地区公有房屋置换暂行规定发展与协调
- 江苏省淮阴中学·徐州一中·连云港新海中学
- 学生导学案Unit7
- 传热学课后习题答案
- 外贸英语写作习题集及答案
- 合肥污水处理厂项目施工设计方案1
- 行政管理考试复习题及答案
- 施工班组管理制度
- CAE在船用柴油机研发中的应用 - 图文
- 平遥古城保护现状分析与对策思考
- 2017-2022年海南节能门窗特色小镇市场前景调查及投资咨询报告(
- 重庆市2013年文科含加分本科一分段表
- 基于单片机的智能数字电子秤设计
- 制冷机房设备安装施工方案 - 图文
- 门主ICEM非结构网格1--四面体网格
- 三维软件在机械制图中的运用
- 双开式模项目可行性研究报告(发改立项备案+2014年最新案例范文
- 工程保障部服务规范与工作流程
- 地基与基础试题库(含答案)