在openstack中,一個典型的操做是把存在的cinder volume指定給某個nova instance使用。這樣nova instance才能夠使用cinder的塊存儲服務。當咱們運行nova volume-attach <server> <volume> [<device>],到底發生了哪些事情?咱們來從源碼級別分析下整個工做流程。html
咱們先來看看這條命令的help文檔。參數的意思很明顯,就是把volume attach給instance,能夠optional指定instance 機器中device的名字,好比: /dev/vdc前端
[ray@ncvm9087109 cinder]$ nova help volume-attach usage: nova volume-attach <server> <volume> [<device>] Attach a volume to a server. Positional arguments: <server> Name or ID of server. <volume> ID of the volume to attach. <device> Name of the device e.g. /dev/vdb. Use "auto" for autoassign (if supported)
咱們來一步步的分析,看看nova command是什麼?打開nova文件發現,咱們看出nova binary其實是novaclient的shell前端。而novaclient其實是基於nova REST API的python CLI client。python
[ray@ncvm9087109 cinder]$ which nova /usr/bin/nova [ray@ncvm9087109 cinder]$ cat /usr/bin/nova #!/usr/bin/python # PBR Generated from u'console_scripts' import sys from novaclient.shell import main if __name__ == "__main__": sys.exit(main())
打開novaclient源碼目錄下面的novaclient/shell.py文件,main入口就在這裏:shell
786 def main(): 787 try: 788 argv = [strutils.safe_decode(a) for a in sys.argv[1:]] 789 OpenStackComputeShell().main(argv)
繼續切換到OpenStackComputeShell Class的mian函數。 主要作的事情就是參數處理,已經根據參數生成通常的HTTP client方便後續發出REST request。記住這裏的self.cs,後續還會用到。注意最後隱士的調用args.func(self.cs, args)。此時func已經指向後文中的do_volume_attach。json
根據API版本從v1/v2/v3 shell模塊從load submand parser。在novaclient/v3/shell.py僅僅找到並import那些"do_"開頭的函數 402 def get_subcommand_parser(self, version): 403 parser = self.get_base_parser() 404 405 self.subcommands = {} 406 subparsers = parser.add_subparsers(metavar='<subcommand>') 407 408 try: 409 actions_module = { 410 '1.1': shell_v1_1, 411 '2': shell_v1_1, 412 '3': shell_v3, 413 }[version] 414 except KeyError: 415 actions_module = shell_v1_1 416 417 self._find_actions(subparsers, actions_module) 418 self._find_actions(subparsers, self) ...... 516 def main(self, argv): 517 # Parse args once to find version and debug settings 518 parser = self.get_base_parser() 519 (options, args) = parser.parse_known_args(argv) ...... 538 subcommand_parser = self.get_subcommand_parser( 539 options.os_compute_api_version) 540 self.parser = subcommand_parser 546 args = subcommand_parser.parse_args(argv) ...... 649 self.cs = client.Client(options.os_compute_api_version, # 這裏self.cs 實際上指向 novaclient.v3.client.Client accoring to api version 650 os_username, os_password, os_tenant_name, 651 tenant_id=os_tenant_id, user_id=os_user_id, 652 auth_url=os_auth_url, insecure=insecure, 653 region_name=os_region_name, endpoint_type=endpoint_type, 654 extensions=self.extensions, service_type=service_type, 655 service_name=service_name, auth_system=os_auth_system, 656 auth_plugin=auth_plugin, auth_token=auth_token, 657 volume_service_name=volume_service_name, 658 timings=args.timings, bypass_url=bypass_url, 659 os_cache=os_cache, http_log_debug=options.debug, 660 cacert=cacert, timeout=timeout) ...... 724 args.func(self.cs, args)
在novaclient/v3/shell.py文件中,咱們找到了novaclient準備想nova發出REST請求的入口。utils.arg是很重要的修飾器,能夠動態的給shell文件的函數加載命令行參數。上文help看到的文檔就來自這些utils.arg修飾器。用時,函數最終會調用volumes模塊中attach_server_volume來最實際的attach操做。api
1393 @utils.arg('server', 1394 metavar='<server>', 1395 help='Name or ID of server.') 1396 @utils.arg('volume', 1397 metavar='<volume>', 1398 help='ID of the volume to attach.') 1399 @utils.arg('device', metavar='<device>', default=None, nargs='?', 1400 help='Name of the device e.g. /dev/vdb. ' 1401 'Use "auto" for autoassign (if supported)') 1402 def do_volume_attach(cs, args): # cs mean this above self.cs 1403 """Attach a volume to a server.""" 1404 if args.device == 'auto': 1405 args.device = None 1406 1407 volume = cs.volumes.attach_server_volume(_find_server(cs, args.server).id, 1408 args.volume, 1409 args.device) 1410 1411
走到這一步,咱們回頭看到self.cs是如何生成的:在novaclient/shell.py上面的client.Client()調用下,代碼最終會走到novaclient.v3.client.Client這個Top level object to the Openstack Computer API.app
519 def get_client_class(version): 520 version_map = { 521 '1.1': 'novaclient.v1_1.client.Client', 522 '2': 'novaclient.v1_1.client.Client', 523 '3': 'novaclient.v3.client.Client', 524 } 525 try: 526 client_path = version_map[str(version)] 527 except (KeyError, ValueError): 528 msg = _("Invalid client version '%(version)s'. must be one of: " 529 "%(keys)s") % {'version': version, 530 'keys': ', '.join(version_map.keys())} 531 raise exceptions.UnsupportedVersion(msg) 532 533 return utils.import_class(client_path) 534 535 536 def Client(version, *args, **kwargs): 537 client_class = get_client_class(version) 538 return client_class(*args, **kwargs)
繼續打開novaclient/v3/client.py 看看,主要關注是如何初始化的。咱們看到nova命令行中常常用到的image, flavor, keypairs, volumes等等object都組合在其中。 最後client.HTTPClient實例化了一個http client。固然咱們此次的看點是volume,如何把voulme 操做的request發送出去。ide
34 class Client(object): 35 """ 36 Top-level object to access the OpenStack Compute API. 37 38 Create an instance with your creds:: 39 40 >>> client = Client(USERNAME, PASSWORD, PROJECT_ID, AUTH_URL) 41 42 Then call methods on its managers:: 43 44 >>> client.servers.list() 45 ... 46 >>> client.flavors.list() 47 ... ...... 66 def __init__(self, username, password, project_id, auth_url=None, ...... 91 self.images = images.ImageManager(self) 92 self.keypairs = keypairs.KeypairManager(self) 93 self.quotas = quotas.QuotaSetManager(self) 94 self.servers = servers.ServerManager(self) 95 self.services = services.ServiceManager(self) 96 self.usage = usage.UsageManager(self) 97 self.volumes = volumes.VolumeManager(self) ...... 106 self.client = client.HTTPClient(username, 107 password, 108 user_id=user_id, 109 projectid=project_id, ......
這裏咱們找到了那個cs.volumes到底對應着那個object。 就是novaclient/v3/volumes.py文件按中的VolumeManager。這個管理類用來管理volumes的資源。函數
22 class VolumeManager(base.Manager): 23 """ 24 Manage :class:`Volume` resources. 25 """ 26 27 def attach_server_volume(self, server, volume_id, device): 28 """ 29 Attach a volume identified by the volume ID to the given server ID 30 31 :param server: The server (or it's ID) 32 :param volume_id: The ID of the volume to attach. 33 :param device: The device name 34 :rtype: :class:`Volume` 35 """ 36 body = {'volume_id': volume_id, 'device': device} 37 return self._action('attach', server, body) ...... 61 def _action(self, action, server, info=None, **kwargs): 62 """ 63 Perform a server "action" -- reboot/rebuild/resize/etc. 64 """ 65 body = {action: info} 66 self.run_hooks('modify_body_for_action', body, **kwargs) 67 url = '/servers/%s/action' % base.getid(server) 68 return self.api.client.post(url, body=body)
到這裏,咱們就能看到最後REST API的endpoint,即'/servers/<server_id>/action'。 api client會組裝http reqeust body參數,發送給nova的REST API。post
最終的Nova API endpoint 在這裏能夠找到:
http://api.openstack.org/api-ref-compute-v2-ext.html
log 實例:
2014-04-15 05:50:08.876 DEBUG routes.middleware [req-a623c80a-b4ef-4f01-9903-60a23e84cb58 demo demo] Matched POST /e40722e5e0c74a0b878c595c0afab5fd/servers/6a17e64d-23c7-46a3-9812-8409ad215e40/os-volume_attachments from (pid=17464) __call__ /usr/lib/python2.6/site-packages/routes/middleware.py:100 2014-04-15 05:50:08.877 DEBUG routes.middleware [req-a623c80a-b4ef-4f01-9903-60a23e84cb58 demo demo] Route path: '/{project_id}/servers/:server_id/os-volume_attachments', defaults: {'action': u'create', 'controller': <nova.api.openstack.wsgi.Resource object at 0x3986f50>} from (pid=17464) __call__ /usr/lib/python2.6/site-packages/routes/middleware.py:102 2014-04-15 05:50:08.877 DEBUG routes.middleware [req-a623c80a-b4ef-4f01-9903-60a23e84cb58 demo demo] Match dict: {'action': u'create', 'server_id': u'6a17e64d-23c7-46a3-9812-8409ad215e40', 'project_id': u'e40722e5e0c74a0b878c595c0afab5fd', 'controller': <nova.api.openstack.wsgi.Resource object at 0x3986f50>} from (pid=17464) __call__ /usr/lib/python2.6/site-packages/routes/middleware.py:103 2014-04-15 05:50:08.878 DEBUG nova.api.openstack.wsgi [req-a623c80a-b4ef-4f01-9903-60a23e84cb58 demo demo] Action: 'create', body: {"volumeAttachment": {"device": "/dev/vdc", "volumeId": "5fe8132e-f937-4c1b-8361-9984f94a7c28"}} from (pid=17464) _process_stack /opt/stack/nova/nova/api/openstack/wsgi.py:940 2014-04-15 05:50:08.879 DEBUG nova.api.openstack.wsgi [req-a623c80a-b4ef-4f01-9903-60a23e84cb58 demo demo] Calling method '<bound method VolumeAttachmentController.create of <nova.api.openstack.compute.contrib.volumes.VolumeAttachmentController object at 0x3470110>>' (Content-type='application/json', Accept='application/json') from (pid=17464) _process_stack /opt/stack/nova/nova/api/openstack/wsgi.py:945 2014-04-15 05:50:08.880 AUDIT nova.api.openstack.compute.contrib.volumes [req-a623c80a-b4ef-4f01-9903-60a23e84cb58 demo demo] Attach volume 5fe8132e-f937-4c1b-8361-9984f94a7c28 to instance 6a17e64d-23c7-46a3-9812-8409ad215e40 at /dev/vdc
第一篇novaclient分析文章全文完。轉載請指明出處。