# 先看下挂载卷的入口代码:
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后执行的流程。