# 先看下挂载卷的入口代码:
nova\api\openstack\compute\contrib\volumes.py class VolumeAttachmentController(wsgi.Controller):
@wsgi.serializers(xml=VolumeAttachmentTemplate) def create(self, req, server_id, body): """Attach a volume to an instance.""" context = req.environ['nova.context'] authorize(context) authorize_attach(context, action='create') if not self.is_valid_body(body, 'volumeAttachment'): msg = _("volumeAttachment not specified") raise exc.HTTPBadRequest(explanation=msg) try: volume_id = body['volumeAttachment']['volumeId'] except KeyError: msg = _("volumeId must be specified.") raise exc.HTTPBadRequest(explanation=msg) device = body['volumeAttachment'].get('device') self._validate_volume_id(volume_id) LOG.audit(_("Attach volume %(volume_id)s to instance %(server_id)s " "at %(device)s"), {'volume_id': volume_id, 'device': device, 'server_id': server_id}, context=context) try: instance = self.compute_api.get(context, server_id, want_objects=True) device = self.compute_api.attach_volume(context, instance, volume_id, device) except exception.NotFound as e: raise exc.HTTPNotFound(explanation=e.format_message()) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'attach_volume') # The attach is async attachment = {} attachment['id'] = volume_id attachment['serverId'] = server_id attachment['volumeId'] = volume_id attachment['device'] = device # NOTE(justinsb): And now, we have a problem... # The attach is async, so there's a window in which we don't see # the attachment (until the attachment completes). We could also # get problems with concurrent requests. I think we need an # attachment state, and to write to the DB here, but that's a bigger # change. # For now, we'll probably have to rely on libraries being smart # TODO(justinsb): How do I return "accepted" here? return {'volumeAttachment': attachment}
# 设置好一个断点,dashboard上执行挂载操作来触发pdb调试。首先会进入到入口函数中:
[root@controller ~]# /usr/bin/python /usr/bin/nova-api > /usr/lib/python2.7/site-packages/nova/api/openstack/compute/contrib/volumes.py(392)create() -> context = req.environ['nova.context'] (Pdb) l 387 388 @wsgi.serializers(xml=VolumeAttachmentTemplate) 389 def create(self, req, server_id, body): 390 """Attach a volume to an instance.""" 391 import pdb; pdb.set_trace() 392 context = req.environ['nova.context'] 393 authorize(context) 394 authorize_attach(context, action='create') 395 396 if not self.is_valid_body(body, 'volumeAttachment'): 397 msg = _("volumeAttachment not specified") (Pdb) pp body {u'volumeAttachment': {u'device': u'', u'volumeId': u'c6129426-c4ae-45fe-9052-b9248eacad73'}} (Pdb) pp req <Request at 0x84674d0 POST http://controller:8774/v2/0ea8ceb2f23a4814976b878fe61b1b55/servers/4cfde5a8-58eb-42e7-ace6-1e2de41b5c3a/os-volume_attachments> (Pdb) pp server_id u'4cfde5a8-58eb-42e7-ace6-1e2de41b5c3a'
# 在做一些参数验证后,调用compute api中的attach_volume方法:
415 try: 416 instance = self.compute_api.get(context, server_id, 417 want_objects=True) 418 device = self.compute_api.attach_volume(context, instance, 419 volume_id, device) 420 except exception.NotFound as e: 421 raise exc.HTTPNotFound(explanation=e.format_message()) 422 except exception.InstanceIsLocked as e: 423 raise exc.HTTPConflict(explanation=e.format_message()) (Pdb) pp self.compute_api <nova.compute.api.API object at 0x511d0d0>
# 代码如下:
\nova\compute\api.py class API(base.Base):
def _attach_volume(self, context, instance, volume_id, device, disk_bus, device_type): """Attach an existing volume to an existing instance. This method is separated to make it possible for cells version to override it. """ # NOTE(vish): This is done on the compute host because we want # to avoid a race where two devices are requested at # the same time. When db access is removed from # compute, the bdm will be created here and we will # have to make sure that they are assigned atomically. volume_bdm = self.compute_rpcapi.reserve_block_device_name( context, instance, device, volume_id, disk_bus=disk_bus, device_type=device_type) try: volume = self.volume_api.get(context, volume_id) self.volume_api.check_attach(context, volume, instance=instance) self.volume_api.reserve_volume(context, volume_id) self.compute_rpcapi.attach_volume(context, instance=instance, volume_id=volume_id, mountpoint=device, bdm=volume_bdm) except Exception: with excutils.save_and_reraise_exception(): volume_bdm.destroy(context) return volume_bdm.device_name @wrap_check_policy @check_instance_lock @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.PAUSED, vm_states.STOPPED, vm_states.RESIZED, vm_states.SOFT_DELETED]) def attach_volume(self, context, instance, volume_id, device=None, disk_bus=None, device_type=None, is_ext_volume=False): """Attach an existing volume to an existing instance.""" # NOTE(vish): Fail fast if the device is not going to pass. This # will need to be removed along with the test if we # change the logic in the manager for what constitutes # a valid device. if device and not block_device.match_device(device): raise exception.InvalidDevicePath(path=device) if is_ext_volume: return self.ext_volume_api.attach_volume(context, instance, volume_id, device, disk_bus, device_type) return self._attach_volume(context, instance, volume_id, device, disk_bus, device_type)
# 进而,会调用rpc api来向指定的nova-compue节点发送message。
> /usr/lib/python2.7/site-packages/nova/compute/api.py(3156)_attach_volume() -> volume_id=volume_id, mountpoint=device, bdm=volume_bdm) (Pdb) l 3151 try: 3152 volume = self.volume_api.get(context, volume_id) 3153 self.volume_api.check_attach(context, volume, instance=instance) 3154 self.volume_api.reserve_volume(context, volume_id) 3155 self.compute_rpcapi.attach_volume(context, instance=instance, 3156 volume_id=volume_id, mountpoint=device, bdm=volume_bdm) 3157 except Exception: 3158 with excutils.save_and_reraise_exception(): 3159 volume_bdm.destroy(context) 3160 3161 return volume_bdm.device_name (Pdb) pp instance Instance(access_ip_v4=None,access_ip_v6=None,architecture=None,auto_disk_config=True,availability_zone='nova',cell_name=None,cleaned=False,config_drive='',cpu_pinning=<?>,created_at=2015-07-15T08:49:29Z,default_ephemeral_device=None,default_swap_device=None,deleted=False,deleted_at=None,disable_terminate=False,display_description='instance01',display_name='instance01',ephemeral_gb=0,ephemeral_key_uuid=None,fault=<?>,host='compute1',hostname='instance01',id=33,image_ref='',info_cache=InstanceInfoCache,instance_type_id=2,kernel_id='',key_data=None,key_name=None,launch_index=0,launched_at=2015-07-15T08:49:36Z,launched_on='compute1',locked=False,locked_by=None,memory_mb=512,metadata={},node='compute1',numa_topology=<?>,os_type=None,pci_devices=<?>,power_state=4,progress=0,project_id='0ea8ceb2f23a4814976b878fe61b1b55',ramdisk_id='',reservation_id='r-jajb10ay',root_device_name='/dev/vda',root_gb=1,scheduled_at=None,security_groups=SecurityGroupList,shutdown_terminate=False,system_metadata={image_base_image_ref='',image_min_disk='1',instance_type_ephemeral_gb='0',instance_type_flavorid='1',instance_type_id='2',instance_type_memory_mb='512',instance_type_name='m1.tiny',instance_type_root_gb='1',instance_type_rxtx_factor='1.0',instance_type_swap='0',instance_type_vcpu_weight=None,instance_type_vcpus='1'},task_state=None,terminated_at=None,updated_at=2015-07-15T09:42:58Z,user_data=None,user_id='8a3073f7423d46bea51a3ab1398da61c',uuid=4cfde5a8-58eb-42e7-ace6-1e2de41b5c3a,vcpus=1,vm_mode=None,vm_state='stopped') (Pdb) pp bdm *** NameError: NameError("name 'bdm' is not defined",) (Pdb) pp volume_bdm BlockDeviceMapping(boot_index=None,connection_info=None,created_at=2015-07-16T02:48:36Z,delete_on_termination=False,deleted=False,deleted_at=None,destination_type='volume',device_name='/dev/vdb',device_type=None,disk_bus=None,guest_format=None,id=39,image_id=None,instance=<?>,instance_uuid=4cfde5a8-58eb-42e7-ace6-1e2de41b5c3a,no_device=False,snapshot_id=None,source_type='volume',updated_at=None,volume_id='c6129426-c4ae-45fe-9052-b9248eacad73',volume_size=None)
> /usr/lib/python2.7/site-packages/nova/compute/rpcapi.py(408)attach_volume() -> def attach_volume(self, ctxt, instance, volume_id, mountpoint, bdm=None): (Pdb) l 403 version=version) 404 return cctxt.call(ctxt, 'attach_interface', 405 instance=instance, network_id=network_id, 406 port_id=port_id, requested_ip=requested_ip) 407 408 def attach_volume(self, ctxt, instance, volume_id, mountpoint, bdm=None): 409 # NOTE(ndipanov): Remove volume_id and mountpoint on the next major 410 # version bump - they are not needed when using bdm objects. 411 version = '3.16' 412 kw = {'instance': instance, 'volume_id': volume_id, 413 'mountpoint': mountpoint, 'bdm': bdm} (Pdb) l 414 cctxt = self.client.prepare(server=_compute_host(None, instance), 415 version=version) 416 cctxt.cast(ctxt, 'attach_volume', **kw)
# 下一篇分析nova-compute节点接收到attach_volume message后执行的流程。