系统整体页面:
![](https://app.yinxiang.com/shard/s59/res/b2967ad8-4fab-4fcf-977a-534bdca8a699.png)
![](https://app.yinxiang.com/shard/s59/res/b2967ad8-4fab-4fcf-977a-534bdca8a699.png)
代码结构:
![](https://app.yinxiang.com/shard/s59/res/5da65263-9c21-405d-b956-24e9c55e3776.png)
horizon采用django框架编写(django是一个强大的mvc 框架。具体参考djangobook中文版 http://djangobook.py3k.cn/2.0/。)
左侧面板布局:
![](https://app.yinxiang.com/shard/s59/res/04a86145-96b6-477b-8f25-f2d3ba20c595.png)
代码:
- vim /usr/share/openstack-dashboard/openstack_dashboard/dashboards/admin/dashboard.py
![](https://app.yinxiang.com/shard/s59/res/960761a9-77d6-4c59-a456-cea42b9ffa4f.png)
实例列表布局:
![](https://app.yinxiang.com/shard/s59/res/544eed97-1f89-4bec-b183-2550576618fb.png)
- vim /usr/share/openstack-dashboard/openstack_dashboard/dashboards/project/instances/tables.py
![](https://app.yinxiang.com/shard/s59/res/3222f1e6-513a-40bf-8bf3-32222df1e479.png)
上述三个按钮在table_actions中定义。
![](https://app.yinxiang.com/shard/s59/res/a9b12e7d-fd8d-4bd2-ac1e-c70abe31e98d.png)
上述按钮在row_actions中定义。
第一部分:openstack_dashboard调用流程
接下来以resize instance 为例:
![](https://app.yinxiang.com/shard/s59/res/9395e639-0600-4ee3-90d6-c3cb8c7d767a.png)
重点关注下图url链接:
![](https://app.yinxiang.com/shard/s59/res/32e56383-1ef9-422a-8578-d88fc21fa362.png)
1、urls.py层代码分析:
所有页面的url链接都由urls.py处理:
- vim /usr/share/openstack-dashboard/openstack_dashboard/dashboards/project/instances/urls.py
![](https://app.yinxiang.com/shard/s59/res/11c9acc1-c002-4104-939f-90184490531d.png)
找到risize对应的url行。调用views.ResizeView.as_view().
2、views.py层代码分析详解--跟进到views层的ResizeView:
- #说明resize功能使用workflow工作,下一步详解,请参考:workflow-1.1
- workflow_class = project_workflows.ResizeInstance
- #执行成功跳转到horizon:project:instances:index链接文件:下一步详解,请参考:模板页面-1.1
- success_url = reverse_lazy("horizon:project:instances:index")
- #这个函数返回的值给context这个字典,这个字典里的值可以在html中取到
- @memoized.memoized_method
- def get_object(self, *args, **kwargs):
- instance_id = self.kwargs['instance_id']
- try:#根据实例id获取实例,下一步详解,下一步api调用流程请参考:“horizon中实例resize的API调用步骤详解“(与之类似)。
- instance = api.nova.server_get(self.request, instance_id)
- flavor_id = instance.flavor['id']
- flavors = self.get_flavors()
- if flavor_id in flavors:
- instance.flavor_name = flavors[flavor_id].name
- else:
- flavor = api.nova.flavor_get(self.request, flavor_id)
- instance.flavor_name = flavor.name
- except Exception:
- redirect = reverse("horizon:project:instances:index")
- msg = _('Unable to retrieve instance details.')
- exceptions.handle(self.request, msg, redirect=redirect)
- return instance
- #这个函数主要用来填充context字典,该字典可以和对应的html模板传参
- def get_context_data(self, **kwargs):
- context = super(ResizeView, self).get_context_data(**kwargs)
- context["instance_id"] = self.kwargs['instance_id']
- return context
- #获取flavors list
- @memoized.memoized_method
- def get_flavors(self, *args, **kwargs):
- try:
- #获取数据库中已存在的flavors,下一步api调用流程请参考:“horizon中实例resize的API调用步骤详解“(与之类似)。
- flavors = api.nova.flavor_list(self.request)
- return SortedDict((str(flavor.id), flavor) for flavor in flavors)
- except Exception:
- redirect = reverse("horizon:project:instances:index")
- exceptions.handle(self.request,
- _('Unable to retrieve flavors.'), redirect=redirect)
- #获取初始化数据,为对应的表单forms.py提供数据。(譬如下拉框数据如下图1。)
- def get_initial(self):
- initial = super(ResizeView, self).get_initial()
- _object = self.get_object()
- if _object:
- initial.update({'instance_id': self.kwargs['instance_id'],
- 'name': getattr(_object, 'name', None),
- 'old_flavor_id': _object.flavor['id'],
- 'old_flavor_name': getattr(_object, 'flavor_name', ''),
- 'flavors': self.get_flavors()}) #此处调用上处get_flavors函数
- return initial
图1:
![](https://app.yinxiang.com/shard/s59/res/3f96c615-5f7d-49f7-9dc4-8e2041f6c62f.png)
1、详解resize功能的workflow机制:
- class ResizeView类中:workflow_class = project_workflows.ResizeInstance
- vim /usr/share/openstack-dashboard/openstack_dashboard/dashboards/project/instances/workflows/resize_instance.py
![](https://app.yinxiang.com/shard/s59/res/09469bef-99db-4585-8f48-7aee6d49585d.png)
- #成功与失败的弹出消息及成功之后的跳转url地址。
- success_message = _('Scheduled resize of instance "%s".')
- failure_message = _('Unable to resize instance "%s".')
- success_url = "horizon:project:instances:index"
- #完成resize工作流两个步骤:(如下图2)
- default_steps = (SetFlavorChoice, create_instance.SetAdvanced)
- #格式化状态消息输出
- def format_status_message(self, message):
- return message % self.context.get('name', 'unknown instance')
- #完成resize工作流两个步骤:(如下图2)
- @sensitive_variables('context')
- def handle(self, request, context):
- #从views.py层中的context字典获取数据
- instance_id = context.get('instance_id', None)
- flavor = context.get('flavor', None)
- disk_config = context.get('disk_config', None)
- try: #页面点击确认resize按钮,调用api resize 实例。本步骤将重点分析。下一步详情,请参考horizon中实例resize的API调用步骤详解:
- api.nova.server_resize(request, instance_id, flavor, disk_config)
- return True
- except Exception:
- exceptions.handle(request)
- return False
图2:
![](https://app.yinxiang.com/shard/s59/res/e3ecadee-31ef-4eb3-b6d4-2fdb1bbce017.png)
模板页面-1.1:
模板页面文件位置:
- vim /usr/share/openstack-dashboard/openstack_dashboard/dashboards/project/instances/templates/instances/index.html
![](https://app.yinxiang.com/shard/s59/res/bf32f361-4922-46c5-926a-023cd6c292e6.png)
其中%%代表要替换掉的变量(若有疑问自行参考djangobook文档)。
table.render:表示将model层的数据渲染到页面。
horizon中实例resize的API调用步骤详解:
接着上述workflow中resize
- api.nova.server_resize(request, instance_id, flavor, disk_config)
根据import原则找到api文件:
- vim /usr/share/openstack-dashboard/openstack_dashboard/api/__init__.py
![](https://app.yinxiang.com/shard/s59/res/53cba2d9-c9b1-4365-a761-524875447a64.png)
此处将base、ceilometer、keystone等 都导入进来了。
跟踪到nova.api代码中:找到server_resize方法:
- def server_resize(request, instance_id, flavor, disk_config=None, **kwargs):
- novaclient(request).servers.resize(instance_id, flavor,
- disk_config, **kwargs)
- #拼装client,组装url参数(包括keystone的token,以及调用v1_1、还是v3版本的novaclient参数等等)
- def novaclient(request):
- insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)
- cacert = getattr(settings, 'OPENSTACK_SSL_CACERT', None)
- LOG.debug('novaclient connection created using token "%s" and url "%s"' %
- (request.user.token.id, base.url_for(request, 'compute')))
- #根据返回的c知道接下来调用的是novaclient代码:因此上述方法server_resize调用novaclient中server.py中的resize方法
下述流程则参考以下的novaclientAPI调用流程。
- c = nova_client.Client(request.user.username,
- request.user.token.id,
- project_id=request.user.tenant_id,
- auth_url=base.url_for(request, 'compute'),
- insecure=insecure,
- cacert=cacert,
- http_log_debug=settings.DEBUG)
- c.client.auth_token = request.user.token.id
- c.client.management_url = base.url_for(request, 'compute')
- return c
第二部分:novaclientAPI调用流程
从dashboard代码入口开始:
- def server_resize(request, instance_id, flavor, disk_config=None, **kwargs):
- novaclient(request).servers.resize(instance_id, flavor,
- disk_config, **kwargs)
下图为novaclient的代码结构:
接着调用novaclient模块中v1_1里面的services.py文件resize方法。
![](https://app.yinxiang.com/shard/s59/res/468205be-c1d6-4c0f-ba9b-f0c4547bcaed.png)
manage里面的resize方法:
![](https://app.yinxiang.com/shard/s59/res/94abe5d1-5f0f-468b-acde-8a5102cc01e8.png)
调用action方法:
![](https://app.yinxiang.com/shard/s59/res/cbdd50dc-c4f7-4ec9-b013-81a74ad4e415.png)
#下面两句代码拼接一个url地址
- url = '/servers/%s/action' % base.getid(server)
- return self.api.client.post(url, body=body)
- #发送url,通过wsgi调用nova代码。