horizon源码分析(二)

一、简要回顾

对于请求:

地址:/dashboard/admin/instances/

方式:POST

参数:

instances_filter_q:

action:instances__soft_reboot__89a8849b-a3cd-4ce0-9158-c3dd69e8508e

URL绑定为:

openstack_dashboard/dashboards/admin/instances/urls.py

二、目录结构

接下来主要分析

openstack_dashboard/dashboards/admin/instances/views.py

views.AdminIndexView.as_view()

从Django的generic view说起....

generic view中的as_view()可以返回一个Django的view函数,该view函数会构造出类实例,将as_view()中传入的参数设置为该实例的属性,然后调用dispatch函数,dispatch函数通常会将request请求转给类中的post或get函数。

generic view的主要使用方法是用子类重写其中的属性或方法。

详细情况可以参考Django官方文档:

https://docs.djangoproject.com/en/1.7/topics/class-based-views/

【小杰鱼说】对Django框架的深入了解对于理解Horizon十分必要,as_view函数最终达到的效果还是将处理逻辑放入post函数或get函数中,这点和其他网络框架类似。

openstack_dashboard/dashboards/admin/instances/views.py

分析AdminIndexView.as_view(),其会调用该类的post函数

class AdminIndexView(tables.DataTableView):

table_class = project_tables.AdminInstancesTable

template_name = ‘admin/instances/index.html‘

由于AdminIndexViewà DataTableViewàMultiTableView,如下图所示。追踪到MultiTableView.post,该post函数会调用该类的get函数。

三、此处插入对DataTable、DataTableView、Action三者的简要概括:

DataTableView簇:

_data={

表名:data(通过get_data函数获得)

...

}

_tables={

表名:table实例

}

table=table实例

说明:本例中data为一个包含instance的list

可以通过table_class绑定具体的DataTable

通过get_data函数获取data,该函数通常调用openstack_dashboard/api模块获取数据

handle_table函数负责将data和table挂钩

综上,DataTableView正如其名字一样,拥有table和data,负责处理data的获取,Table的创建,以及二者的绑定等

DataTable簇:

规定table的column和action

可以处理和table绑定的data

take_action函数负责处理action

Action簇:

利用action函数进行处理

继续分析get函数...

horizon/tables/views.py

MultiTableView类:

def get(self, request, *args, **kwargs):

handled = self.construct_tables()

if handled:

return handled

"""此处暂且不管 """

context = self.get_context_data(**kwargs)

return self.render_to_response(context)

def construct_tables(self):

"""根据类中的table_class属性绑定的DataTable类,创建或返回DataTable对象,此处为AdminInstancesTable对象 """

tables = self.get_tables().values()

# Early out before data is loaded

for table in tables:

"""预先处理,此处暂且不管 """

preempted = table.maybe_preempt()

if preempted:

return preempted

# Load data into each table and check for action handlers

for table in tables:

handled = self.handle_table(table)

if handled:

return handled

# If we didn‘t already return a response, returning None continues

# with the view as normal.

return None

四、此处插入AdminInstanceTable的创建过程,解释如下:

其中使用了metaclass对DataTable及其子类进行修改

先观察AdminInstancesTable类和DataTableOptions类:

class AdminInstancesTable(tables.DataTable):

...

class Meta:

name = "instances"

verbose_name = _("Instances")

status_columns = ["status", "task"]

table_actions = (project_tables.TerminateInstance,

AdminInstanceFilterAction)

row_class = AdminUpdateRow

row_actions = (project_tables.ConfirmResize,

project_tables.RevertResize,

AdminEditInstance,

project_tables.ConsoleLink,

project_tables.LogLink,

project_tables.CreateSnapshot,

project_tables.TogglePause,

project_tables.ToggleSuspend,

MigrateInstance,

project_tables.SoftRebootInstance,

project_tables.RebootInstance,

project_tables.TerminateInstance)

class DataTableOptions(object):

def __init__(self, options):

self.name = getattr(options, ‘name‘, self.__class__.__name__)

verbose_name = getattr(options, ‘verbose_name‘, None) \

or self.name.title()

self.verbose_name = verbose_name

self.columns = getattr(options, ‘columns‘, None)

self.status_columns = getattr(options, ‘status_columns‘, [])

self.table_actions = getattr(options, ‘table_actions‘, [])

self.row_actions = getattr(options, ‘row_actions‘, [])

self.row_class = getattr(options, ‘row_class‘, Row)

self.column_class = getattr(options, ‘column_class‘, Column)

self.pagination_param = getattr(options, ‘pagination_param‘, ‘marker‘)

...

接着分析metaclass对类的修改...

class DataTable(object):

__metaclass__ = DataTableMetaclass

class DataTableMetaclass(type):

def __new__(mcs, name, bases, attrs):

# Process options from Meta

class_name = name

"""将类中的Meta转变为DataTableOptions,添加为类的_meta属性"""

attrs["_meta"] = opts = DataTableOptions(attrs.get("Meta", None))

# Gather columns; this prevents the column from being an attribute

# on the DataTable class and avoids naming conflicts.

"""将类中的column属性聚集作为新的列属性,阻止其作为类属性"""

columns = []

for attr_name, obj in attrs.items():

if issubclass(type(obj), (opts.column_class, Column)):

column_instance = attrs.pop(attr_name)

column_instance.name = attr_name

column_instance.classes.append(‘normal_column‘)

columns.append((attr_name, column_instance))

columns.sort(key=lambda x: x[1].creation_counter)

# Iterate in reverse to preserve final order

for base in bases[::-1]:

if hasattr(base, ‘base_columns‘):

columns = base.base_columns.items() + columns

attrs[‘base_columns‘] = SortedDict(columns)

...

"""收集row_action和table_action对象"""

actions = list(set(opts.row_actions) | set(opts.table_actions))

actions.sort(key=attrgetter(‘name‘))

actions_dict = SortedDict([(action.name, action())

for action in actions])

attrs[‘base_actions‘] = actions_dict

if opts._filter_action:

# Replace our filter action with the instantiated version

opts._filter_action = actions_dict[opts._filter_action.name]

# Create our new class!

return type.__new__(mcs, name, bases, attrs)

概况如下图:

【小杰鱼说】使用metaclass对类进行修改,这样极大地增加了程序的可扩展性和灵活性,但同时复杂度也增大。

metaclass的理解可以参考:

http://blog.csdn.net/psh2009/article/details/10330747

http://jianpx.iteye.com/blog/908121

继续分析handle_table函数...

MultiTableMixin类:

def handle_table(self, table):

name = table.name

"""获取数据"""

data = self._get_data_dict()

"""获取与该DataTable相关的数据,并将数据和该DataTable挂钩"""

self._tables[name].data = data[table._meta.name]

"""暂且不管"""

self._tables[name]._meta.has_more_data = self.has_more_data(table)

"""此处为调用AdminInstancesTable.maybe_handle函数"""

handled = self._tables[name].maybe_handle()

return handled

horizon/tables/base.py

DataTable类:

def maybe_handle(self):

"""

Determine whether the request should be handled by any action on this

table after data has been loaded.

"""

request = self.request

"""获取request中的数据,这里为

table_name=’instances’

action_name=’soft_reboot’

obj_id=’89a8849b-a3cd-4ce0-9158-c3dd69e8508e’

"""

table_name, action_name, obj_id = self.check_handler(request)

if table_name == self.name and action_name:

action_names = [action.name for action in

self.base_actions.values() if not action.preempt]

# do not run preemptive actions here

if action_name in action_names:

return self.take_action(action_name, obj_id)

return None

五、此处插入Action簇的关系,如下图所示:

继续分析take_action函数...

horizon/tables/base.py

DataTable类:

"""

action_name=’soft_reboot’

obj_id=’89a8849b-a3cd-4ce0-9158-c3dd69e8508e’

"""

def take_action(self, action_name, obj_id=None, obj_ids=None):

# See if we have a list of ids

obj_ids = obj_ids or self.request.POST.getlist(‘object_ids‘)

"""得到SoftRebootInstance实例"""

action = self.base_actions.get(action_name, None)

if not action or action.method != self.request.method:

# We either didn‘t get an action or we‘re being hacked. Goodbye.

return None

# Meanhile, back in Gotham...

if not action.requires_input or obj_id or obj_ids:

if obj_id:

obj_id = self.sanitize_id(obj_id)

if obj_ids:

obj_ids = [self.sanitize_id(i) for i in obj_ids]

"""SoftRebootInstanceàRebootInstanceàBatchActionàAction,由于BatchAction有handle函数,所以在Action的__init__()中将属性handles_multiple设置为True"""

# Single handling is easy

if not action.handles_multiple:

response = action.single(self, self.request, obj_id)

# Otherwise figure out what to pass along

else:#进入此项

# Preference given to a specific id, since that implies

# the user selected an action for just one row.

if obj_id:

obj_ids = [obj_id]

response = action.multiple(self, self.request, obj_ids)

return response

elif action and action.requires_input and not (obj_id or obj_ids):

messages.info(self.request,

_("Please select a row before taking that action."))

return None

这里使用了一个trick

horizon/tables/actions.py

Action类:

def __init__(...)

...

if not has_multiple and self.handles_multiple:

def multiple(self, data_table, request, object_ids):

return self.handle(data_table, request, object_ids)

"""为该实例动态绑定multiple方法,其实质为调用handle方法"""

self.multiple = new.instancemethod(multiple, self)

所以,接下来分析BatchAction中的handle函数...

horizon/tables/actions.py

BatchAction类:

def handle(self, table, request, obj_ids):

action_success = []

action_failure = []

action_not_allowed = []

for datum_id in obj_ids:

datum = table.get_object_by_id(datum_id)

datum_display = table.get_object_display(datum) or _("N/A")

if not table._filter_action(self, request, datum):

action_not_allowed.append(datum_display)

LOG.info(‘Permission denied to %s: "%s"‘ %

(self._conjugate(past=True).lower(), datum_display))

continue

try:

self.action(request, datum_id)

#Call update to invoke changes if needed

self.update(request, datum)

action_success.append(datum_display)

self.success_ids.append(datum_id)

LOG.info(‘%s: "%s"‘ %

(self._conjugate(past=True), datum_display))

except Exception as ex:

# Handle the exception but silence it since we‘ll display

# an aggregate error message later. Otherwise we‘d get

# multiple error messages displayed to the user.

if getattr(ex, "_safe_message", None):

ignore = False

else:

ignore = True

action_failure.append(datum_display)

exceptions.handle(request, ignore=ignore)

...

return shortcuts.redirect(self.get_success_url(request))

openstack_dashboard/dashboards/project/instances/tables.py

SoftRebootInstance类:

class SoftRebootInstance(RebootInstance):

name = "soft_reboot"

action_present = _("Soft Reboot")

action_past = _("Soft Rebooted")

def action(self, request, obj_id):

api.nova.server_reboot(request, obj_id, soft_reboot=True)

参考文档:

http://docs.openstack.org/developer/horizon/

时间: 2024-10-19 13:31:13

horizon源码分析(二)的相关文章

horizon源码分析(一)

一.源码分析环境: 本来应该搭建horizon的development环境的,这样方便debug,但是由于各种报错,本人没有搭建成功,这也导致有很多源码疑问没有解决,后续可以继续补充这一部分. 官方搭建方法参考网址:http://docs.openstack.org/developer/horizon/quickstart.html openstack版本:stable-havana 二.源码分析过程中使用的软件: SourceInsight:这个当然是源码分析第一利器,可以进行全局关键字查找,

netty 源码分析二

以服务端启动,接收客户端连接整个过程为例分析, 简略分为 五个过程: 1.NioServerSocketChannel 管道生成, 2.NioServerSocketChannel 管道完成初始化, 3.NioServerSocketChannel注册至Selector选择器, 4.NioServerSocketChannel管道绑定到指定端口,启动服务 5.NioServerSocketChannel接受客户端的连接,进行相应IO操作 Ps:netty内部过程远比这复杂,简略记录下方便以后回忆

[Android]Volley源码分析(二)Cache

Cache作为Volley最为核心的一部分,Volley花了重彩来实现它.本章我们顺着Volley的源码思路往下,来看下Volley对Cache的处理逻辑. 我们回想一下昨天的简单代码,我们的入口是从构造一个Request队列开始的,而我们并不直接调用new来构造,而是将控制权反转给Volley这个静态工厂来构造. com.android.volley.toolbox.Volley: public static RequestQueue newRequestQueue(Context conte

哇!板球 源码分析二

游戏主页面布局 创建屏下Score标签 pLabel = CCLabelTTF::create("Score", "Arial", TITLE_FONT_SIZE); //分数标签 //设置标签字体的颜色 pLabel->setColor (ccc3(0, 0, 0)); //设置文本标签的位置 pLabel->setPosition ( ccp ( SCORE_X, //X坐标 SCORE_Y //Y坐标 ) ); //将文本标签添加到布景中 this

baksmali和smali源码分析(二)

这一节,主要介绍一下 baksmali代码的框架. 我们经常在反编译android apk包的时候使用apktool这个工具,其实本身这个工具里面对于dex文件解析和重新生成就是使用的baksmali 和smali这两个jar包其中 baksmali是将 dex文件转换成便于阅读的smali文件的,具体使用命令如下:java -jar baksmali.jar classes.dex -o myout其中myout是输出的文件夹 而smali是将smali文件重新生成回 dex文件的具体使用的命

【梦幻连连连】源码分析(二)

转载请注明出处:http://blog.csdn.net/oyangyufu/article/details/24736711 GameLayer场景界面效果: 源码分析: GameLayer场景初始化,主要是初始化加载界面及背景音乐 bool GameLayer::init() { float dt=0.0f; if ( !CCLayerColor::initWithColor(ccc4(255, 255, 255, 255))) { return false; } this->initLoa

[Android]Fragment源码分析(二) 状态

我们上一讲,抛出来一个问题,就是当Activity的onCreateView的时候,是如何构造Fragment中的View参数.要回答这个问题我们先要了解Fragment的状态,这是Fragment管理中非常重要的一环.我们先来看一下FragmentActivity提供的一些核心回调: @Override protected void onCreate(Bundle savedInstanceState) { mFragments.attachActivity(this, mContainer,

JAVA Collection 源码分析(二)之SubList

昨天我们分析了ArrayList的源码,我们可以看到,在其中还有一个类,名为SubList,其继承了AbstractList. // AbstractList类型的引用,所有继承了AbstractList都可以传进来 private final AbstractList<E> parent; // 这个是其实就是parent的偏移量,从parent中的第几个元素开始的 private final int parentOffset; private final int offset; int s

Tomcat源码分析二:先看看Tomcat的整体架构

Tomcat源码分析二:先看看Tomcat的整体架构 Tomcat架构图 我们先来看一张比较经典的Tomcat架构图: 从这张图中,我们可以看出Tomcat中含有Server.Service.Connector.Container等组件,接下来我们一起去大致的看看这些组件的作用和他们之间的相互联系.在这之前,我们先补充一个知识点,也就是Tomcat它实现的功能点是什么呢?通过查找一些资料,这里参考下极客时间<深入拆解Tomcat_Jetty>中的总结,即Tomcat 要实现 2 个核心功能: