ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

OpenStack(kilo)界面dashboard的二次开发(一)-增加Panel

2021-05-07 09:55:31  阅读:384  来源: 互联网

标签:tables name -- project admin dashboard kilo 二次开发 openstack


入行到现在对openstack的界面进行过一些定制,现在进行个梳理总结。博文中如有说得不对的地方请大家谅解,同时欢迎大家指正,共同提升。

  • 基本认识

  • 增加Panel

一、基本认识:

openstack的dashboard是允许用户管理openstack资源和服务的一个web接口。openstack的界面相关的代码有三部分:

1、各类控件的基类,页面的通用模板等

1/usr/lib/python2.7/site-packages/horizon

2、界面具体样式,数据获取等

1/usr/share/openstack-dashboard

3、还有个容易被忽略的,登录认证界面

1/usr/lib/python2.7/site-packages/openstack_auth

openstack的菜单总共分为三级,Dashboard、Panelgroup、Panel,分别如下图标记1/2/3所示:

图片

有了以上认识,接下来就看看如何增加Panel

二、增加Panel

要增加一个panel首先得简单看看代码结构,进入/usr/share/openstack-dashbaord/openstack_dashboard/dashboards目录,会看到如下的结构:

1|--__init__.py
2|--admin
3|--project
4|--identity
5|--router
6|--settings

以上几个文件夹就代表了openstack界面上的几个一级菜单(Dashboard),分别是admin(管理员)、project(项目)、identity(Identity)、settings(设置,这个一级菜单需要点击右上角下拉中的设置才会显示出来)、router(配置文件中将profile_support打开可见,ciso nexus 1000v的管理面板)。现在选择admin进去看看它的目录结构。 

admin

 1admin
2|--__init__.py
3|--aggregates
4|--dashboard.py
5|--defaults
6|--flavors
7|--hypervisors
8|--images
9|--info
10|--instances
11|--metadta_defs
12|--metering
13|--models.py
14|--networks
15|--overview
16|--routers
17|--volumes


仔细研究可以发现这些文件夹都一一对应了管理员(admin)下的各个Panel,首先看看Dashboard的类似于配置文件的东西dashboard.py,它就是描述Dashboard的python文件。


 1|-dashboard.py
2from django.utils.translation import ugettext_lazy as _
3
4import horizon
5
6
7class SystemPanels(horizon.PanelGroup):
8    slug = "admin"
9    name = _("System")
10    panels = ('overview', 'metering', 'hypervisors', 'aggregates',
11              'instances', 'volumes', 'flavors', 'images',
12              'networks', 'routers', 'defaults', 'metadata_defs', 'info')
13
14
15class Admin(horizon.Dashboard):
16    name = _("Admin")
17    slug = "admin"
18    panels = (SystemPanels,)
19    default_panel = 'overview'
20    permissions = ('openstack.roles.admin',)
21
22
23horizon.register(Admin)

浏览一遍该文件可以看到代码结构还是非常清晰的。其中class Admin即是描述管理员Dashboard的类,它继承了horizon(/usr/lib/python2.7/site-packages/horizon)中的Dashboard基类(描述一级菜单的基类),class Admin中有几个属性,分别为name、slug、panels、default_panel、permissions,根据基类提供的信息可以知道它们分别代表的是名称、id、该Dashboard下的panels、默认panel、权限。除此之外,还有一个class SystemPanels,可以看到它是继承horizon中的PanelGroup,根据openstack三级菜单结构可以知道它应该就是描述的二级菜单了,class SystemPanels有slug、name、panels三个属性,分别代表的是id、名称、属于它的panel。最后有一个horizon.register就是注册Dashboard了。现在看来,如果要增加一个panel到Admin这个菜单下面,无非需要做两步:

1、一个描述panel的文件夹;

2、在这个class SystemPanels下的panels属性中增加一个panel的id。

接下来就要看看代表panel的文件夹下有什么东西了。进入instances(实例)文件夹,目录结构如下:

1|--instances
2  |--forms.py
3  |--__init__.py
4  |--panel.py
5  |--tables.py
6  |--templates
7  |--tests.py
8  |--urls.py
9  |--views.py

该文件夹下的东西就是描述Panel instances(实例)的相关文件了。看这文件结构应该就是一个django app了。分别看看各个文件:

forms.py是表单,描述的是弹出框之类的,现在主要目的是增加一个panel,暂且不看。

panel.py应该就是描述这个panel基本信息的文件了,这个必须看看

 1from django.utils.translation import ugettext_lazy as _
2
3import horizon
4
5from openstack_dashboard.dashboards.admin import dashboard
6
7class Instances(horizon.Panel):
8    name = _("Instances")
9    slug = 'instances'
10    permissions = ('openstack.roles.admin', 'openstack.services.compute')
11
12
13dashboard.Admin.register(Instances)

可以看到以上代码结构与dashboard.py是非常类似的。这里class Instances继承了horzion中的Panel这个基类,很明显这就是描述panel的基类了。这Instances现在有三个属性,分别是name(名称)、slug(id)、permissions(权限)。注意最后一行代码,这行代码是import的前面我所说到的dashboard.py。最后一行也就是说Admin的Dashboard类用register方法注册了该panel,层级关系非常清晰。

tables.py顾名思义是描述表格之类的,看看它的代码,(这里省略了部分代码)


 1...
2class AdminInstancesTable(tables.DataTable):
3    TASK_STATUS_CHOICES = (
4        (None, True),
5        ("none", True)
6    )
7    STATUS_CHOICES = (
8        ("active", True),
9        ("shutoff", True),
10        ("suspended", True),
11        ("paused", True),
12        ("error", False),
13        ("rescue", True),
14        ("shelved", True),
15        ("shelved_offloaded", True),
16    )
17    tenant = tables.Column("tenant_name", verbose_name=_("Project"))
18    # NOTE(gabriel): Commenting out the user column because all we have
19    # is an ID, and correlating that at production scale using our current
20    # techniques isn't practical. It can be added back in when we have names
21    # returned in a practical manner by the API.
22    # user = tables.Column("user_id", verbose_name=_("User"))
23    host = tables.Column("OS-EXT-SRV-ATTR:host",
24                         verbose_name=_("Host"),
25                         classes=('nowrap-col',))
26    name = tables.Column("name",
27                         link="horizon:admin:instances:detail",
28                         verbose_name=_("Name"))
29    image_name = tables.Column("image_name",
30                               verbose_name=_("Image Name"))
31    ip = tables.Column(project_tables.get_ips,
32                       verbose_name=_("IP Address"),
33                       attrs={'data-type': "ip"})
34    size = tables.Column(project_tables.get_size,
35                         verbose_name=_("Size"),
36                         attrs={'data-type': 'size'})
37    status = tables.Column(
38        "status",
39        filters=(title, filters.replace_underscores),
40        verbose_name=_("Status"),
41        status=True,
42        status_choices=STATUS_CHOICES,
43        display_choices=project_tables.STATUS_DISPLAY_CHOICES)
44    task = tables.Column("OS-EXT-STS:task_state",
45                         verbose_name=_("Task"),
46                         empty_value=project_tables.TASK_DISPLAY_NONE,
47                         status=True,
48                         status_choices=TASK_STATUS_CHOICES,
49                         display_choices=project_tables.TASK_DISPLAY_CHOICES)
50    state = tables.Column(project_tables.get_power_state,
51                          filters=(title, filters.replace_underscores),
52                          verbose_name=_("Power State"),
53                          display_choices=project_tables.POWER_DISPLAY_CHOICES)
54    created = tables.Column("created",
55                            verbose_name=_("Time since created"),
56                            filters=(filters.parse_isotime,
57                                     filters.timesince_sortable),
58                            attrs={'data-type': 'timesince'})
59    class Meta(object):
60        name = "instances"
61        verbose_name = _("Instances")
62        status_columns = ["status", "task"]
63        table_actions = (project_tables.TerminateInstance,
64                         AdminInstanceFilterAction)
65        row_class = AdminUpdateRow
66        row_actions = (project_tables.ConfirmResize,
67                       project_tables.RevertResize,
68                       AdminEditInstance,
69                       project_tables.ConsoleLink,
70                       project_tables.LogLink,
71                       project_tables.CreateSnapshot,
72                       project_tables.TogglePause,
73                       project_tables.ToggleSuspend,
74                       MigrateInstance,
75                       LiveMigrateInstance,
76                       project_tables.SoftRebootInstance,
77                       project_tables.RebootInstance,
78                       project_tables.TerminateInstance)
79

因为该文件是描述表格的,在这省略了部分代码,只看关键描述表格的这部分。class AdminInstancesTable继承的是DataTable这个基类,看看代码的结构可以很容易看出tables.Column就是描述表格的每一列的,其中class Meta顾名思义是描述该表格的元数据的类。它有表格的名字、动作、表动作等属性。

templates是静态模板,先只看看index.html

1{% extends 'base.html' %}
2{% load i18n %}
3{% block title %}{% trans "Instances" %}{% endblock %}
4
5{% block main %}
6    {{ table.render }}
7{% endblock %}

该模板继承了base.html,然后一个标题块block title、一个主要部分block main,table.render是传过来的值,它就是代表的表格那一块了。

tests.py为测试代码;

urls.py是决定了界面的url;


 1from django.conf.urls import patterns
2from django.conf.urls import url
3
4from openstack_dashboard.dashboards.admin.instances import views
5
6
7INSTANCES = r'^(?P<instance_id>[^/]+)/%s$'
8
9
10urlpatterns = patterns(
11    'openstack_dashboard.dashboards.admin.instances.views',
12    url(r'^$', views.AdminIndexView.as_view(), name='index'),
13    url(INSTANCES % 'update', views.AdminUpdateView.as_view(), name='update'),
14    url(INSTANCES % 'detail', views.DetailView.as_view(), name='detail'),
15    url(INSTANCES % 'console', 'console', name='console'),
16    url(INSTANCES % 'vnc', 'vnc', name='vnc'),
17    url(INSTANCES % 'spice', 'spice', name='spice'),
18    url(INSTANCES % 'rdp', 'rdp', name='rdp'),
19    url(INSTANCES % 'live_migrate', views.LiveMigrateView.as_view(),
20        name='live_migrate'),
21)

该文件每个url对应了views的一个函数或者类,index是描述主页的,其它url是相关功能的url。

views.py处理用户请求,从urls.py中反应过来,获取数据。


 1...
2class AdminIndexView(tables.DataTableView):
3    table_class = project_tables.AdminInstancesTable
4    template_name = 'admin/instances/index.html'
5    page_title = _("Instances")
6
7    def has_more_data(self, table):
8        return self._more
9
10    def get_data(self):
11        ...
12

省略了views.py的部分代码,以后的博文中会对其它的进行描述,现在只是增加panel,只看看描述index页面的类。class AdminIndexView继承的是DataTableView这个类,这个类有table_class、template_name等几个属性,可以比较明显的看出它应该就是为那个表格类服务的了,主要功能就是获取数据,设置页面title、指定静态模板了。

那么现在就可以模仿以上这个结构自行增加一个panel了,构造一个类似的文件夹:


1mypanel
2|--__init__.py
3|--panel.py
4|--tables.py
5|--templates
6  |--mypanel
7    |--index.html
8|--urls.py
9|--views.py

它们代码如下:

1、panel.py

 1import horizon
2
3from openstack_dashboard.dashboards.admin import dashboard
4
5
6class Mypanel(horizon.Panel):
7    name = "mypanel"
8    slug = 'mypanel'
9    permissions = ('openstack.roles.admin', 'openstack.services.compute')
10
11
12dashboard.Admin.register(Mypanel)

2、tables.py

1from horizon import tables
2
3class MypanelTable(tables.DataTable):
4    column1 = tables.Column("column1", verbose_name="column1")
5    class Meta(object):
6        name = "mypaneltable"
7        verbose_name = "mypaneltable"

3、index.html

1{% extends 'base.html' %}
2{% load i18n %}
3{% block title %}{% trans "mypanel" %}{% endblock %}
4
5{% block main %}
6    {{ table.render }}
7{% endblock %}

4、urls.py

 1from django.conf.urls import patterns
2from django.conf.urls import url
3
4from openstack_dashboard.dashboards.admin.mypanel import views
5
6
7
8
9urlpatterns = patterns(
10    'openstack_dashboard.dashboards.admin.mypanel.views',
11    url(r'^$', views.MypanelIndexView.as_view(), name='index'),
12)

5、views.py

 1from horizon import tables
2
3from openstack_dashboard.dashboards.admin.mypanel \
4    import tables as project_tables
5
6class MypanelIndexView(tables.DataTableView):
7    table_class = project_tables.MypanelTable
8    template_name = 'admin/mypanel/index.html'
9    page_title = "mypanel"
10
11    def get_data(self):
12        data = []
13        return data

除此之外,在admin中的dashboard.py加上这个panel的slug。 

- dashboard.py

 1from django.utils.translation import ugettext_lazy as _
2
3import horizon
4
5
6class SystemPanels(horizon.PanelGroup):
7    slug = "admin"
8    name = _("System")
9    panels = ('overview', 'metering', 'hypervisors', 'aggregates',
10              'instances', 'volumes', 'flavors', 'images',
11              'networks', 'routers', 'defaults', 'metadata_defs', 'info','mypanel')  #加到属性panels下
12
13
14class Admin(horizon.Dashboard):
15    name = _("Admin")
16    slug = "admin"
17    panels = (SystemPanels,)
18    default_panel = 'overview'
19    permissions = ('openstack.roles.admin',)
20
21
22horizon.register(Admin)

然后重启httpd服务,查看页面 

图片


标签:tables,name,--,project,admin,dashboard,kilo,二次开发,openstack
来源: https://blog.51cto.com/u_15127625/2758242

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有