cinder manage/unmanage

cinder manage/unmanage 管理卷

mange命令
类似于create命令,但是区别是mange,不会在后端创建一个新的LUN,而是将一个已有的LUN纳入Cinder的管理(会对这个LUN从命名)。

cinder manage
:必须是pool之一;
:必须是storage可以识别的,如netapp下可以是path,或者uuid。
unmanage命令
类似于delete命令,但是不会从后端将LUN删除。

cinder unmanage

一、cinder manage

(1)测试:

[root@node1 ~]# cinder manage cinder1@netapp-iscsi-20170613161543#vol_09052017_154346_88 /vol/vol_09052017_154346_88/lun_31072017_164348_21 --name miaomiao --volume-type netapp_volume_type --id-type source-name --description jojo --availability-zone nova
±-------------------------------±-----------------------------------------------------------+
| Property | Value |
±-------------------------------±-----------------------------------------------------------+
| attachments | [] |
| availability_zone | nova |
| bootable | false |
| consistencygroup_id | None |
| created_at | 2017-07-31T08:44:36.000000 |
| description | jojo |
| encrypted | False |
| id | a89c9e8a-b589-497c-b0f7-f81149f62226 |
| metadata | {} |
| migration_status | None |
| multiattach | False |
| name | miaomiao |
| os-vol-host-attr:host | cinder1@netapp-iscsi-20170613161543#vol_09052017_154346_88 |
| os-vol-mig-status-attr:migstat | None |
| os-vol-mig-status-attr:name_id | None |
| os-vol-tenant-attr:tenant_id | 7b7d6627cf044d80aa6ec3f2f4ade2ab |
| replication_status | None |
| size | 0 |
| snapshot_id | None |
| source_volid | None |
| status | creating |
| updated_at | 2017-07-31T08:44:37.000000 |
| user_id | 3f56d2976eac468dbe836ebcc4400858 |
| volume_type | netapp_volume_type
(2)源码分析

cinder.volume.api.API#manage_existing:

def manage_existing(self, context, host, cluster_name, ref, name=None,
                    description=None, volume_type=None, metadata=None,
                    availability_zone=None, bootable=False):
    if volume_type and 'extra_specs' not in volume_type:
        extra_specs = volume_types.get_volume_type_extra_specs(
            volume_type['id'])
        volume_type['extra_specs'] = extra_specs

    service = self._get_service_by_host_cluster(context, host,
                                                cluster_name)

    if availability_zone is None:
        availability_zone = service.availability_zone

    manage_what = {
        'context': context,
        'name': name,
        'description': description,
        'host': service.host,
        'cluster_name': service.cluster_name,
        'ref': ref,
        'volume_type': volume_type,
        'metadata': metadata,
        'availability_zone': availability_zone,
        'bootable': bootable,
    }

    try:
        
        flow_engine = manage_existing.get_flow(self.scheduler_rpcapi,
                                               self.db,
                                               manage_what)
    except Exception:
        msg = _('Failed to manage api volume flow.')
        LOG.exception(msg)
        raise exception.CinderException(msg)

    
    
    
    with flow_utils.DynamicLogListener(flow_engine, logger=LOG):
        flow_engine.run()
        vol_ref = flow_engine.storage.fetch('volume')
        LOG.info(_LI("Manage volume request issued successfully."),
                 resource=vol_ref)
        return vol_ref

上段代码里manage_existing.get_flow是把cinder-manage最核心的里的任务串成一个线性任务。
在 TaskFlow 中使用 flow(流) 来关联各个 Task, 并且规定这些 Task 之间的执行和回滚顺序. flow 中不仅能够包含 task 还能够嵌套 flow. 常见类型有以下几种:

Linear(linear_flow.Flow): 线性流, 该类型 flow 中的 task/flow 按照加入的顺序来依次执行, 按照加入的倒序依次回滚.
Unordered(unordered_flow.Flow): 无序流, 该类型 flow 中的 task/flow 可能按照任意的顺序来执行和回滚.
Graph(graph_flow.Flow): 图流, 该类型 flow 中的 task/flow 按照显式指定的依赖关系或通过其间的 provides/requires 属性的隐含依赖关系来执行和回滚.
cinder.volume.flows.api.manage_existing.get_flow

ACTION = ‘volume:manage_existing’

def get_flow(scheduler_rpcapi, db_api, create_what):

flow_name = ACTION.replace(":", "_") + "_api"

api_flow = linear_flow.Flow(flow_name)




api_flow.add(EntryCreateTask(db_api),
             ManageCastTask(scheduler_rpcapi, db_api))



return taskflow.engines.load(api_flow, store=create_what)

EntryCreateTask(db_api) 和 ManageCastTask(scheduler_rpcapi, db_api)

EntryCreateTask 和 ManageCastTask 都继承至CinderTask类。做为任务类,最重要的是复写了taskflow.atom.Atom的execute(执行业务)和revert(回退业务)方法。Atom 是 TaskFlow 的最小单位, 其他的所有类, 包括 Task 类都是 Atom 类的子类.

task类关系图.png
EntryCreateTask
EntryCreateTask的execute是创建一条size为0 的volume记录,仅仅是在数据库层操作;revert是把该volume销毁。

class EntryCreateTask(flow_utils.CinderTask):
“”"Creates an entry for the given volume creation in the database.

Reversion strategy: remove the volume_id created from the database.
"""
default_provides = set(['volume_properties', 'volume'])

def __init__(self, db):
    requires = ['availability_zone', 'description', 'metadata',
                'name', 'host', 'cluster_name', 'bootable', 'volume_type',
                'ref']
    super(EntryCreateTask, self).__init__(addons=[ACTION],
                                          requires=requires)
    self.db = db

def execute(self, context, **kwargs):
    """Creates a database entry for the given inputs and returns details.

    Accesses the database and creates a new entry for the to be created
    volume using the given volume properties which are extracted from the
    input kwargs.
    """
    volume_type = kwargs.pop('volume_type')
    volume_type_id = volume_type['id'] if volume_type else None

    volume_properties = {
        'size': 0,
        'user_id': context.user_id,
        'project_id': context.project_id,
        'status': 'managing',
        'attach_status': fields.VolumeAttachStatus.DETACHED,
        
        'display_description': kwargs.pop('description'),
        'display_name': kwargs.pop('name'),
        'host': kwargs.pop('host'),
        'cluster_name': kwargs.pop('cluster_name'),
        'availability_zone': kwargs.pop('availability_zone'),
        'volume_type_id': volume_type_id,
        'metadata': kwargs.pop('metadata') or {},
        'bootable': kwargs.pop('bootable'),
    }
    
    volume = objects.Volume(context=context, **volume_properties)
    volume.create()

    return {
        'volume_properties': volume_properties,
        'volume': volume,
    }

def revert(self, context, result, optional_args=None, **kwargs):
    
    if isinstance(result, ft.Failure):
        return

    vol_id = result['volume_id']
    try:
        self.db.volume_destroy(context.elevated(), vol_id)
    except exception.CinderException:
        LOG.exception(_LE("Failed destroying volume entry: %s."), vol_id)

ManageCastTask
ManageCastTask会真正管理物理卷,去后端查找lun并纳入cinder管理。
class ManageCastTask(flow_utils.CinderTask):
“”"Performs a volume manage cast to the scheduler and to the volume manager.

This which will signal a transition of the api workflow to another child
and/or related workflow.
"""

def __init__(self, scheduler_rpcapi, db):
    requires = ['volume', 'volume_properties', 'volume_type', 'ref']
    super(ManageCastTask, self).__init__(addons=[ACTION],
                                         requires=requires)
    self.scheduler_rpcapi = scheduler_rpcapi
    self.db = db

def execute(self, context, volume, **kwargs):
    request_spec = kwargs.copy()
    request_spec['volume_id'] = volume.id

    
    
    self.scheduler_rpcapi.manage_existing(context, volume,
                                          request_spec=request_spec)

def revert(self, context, result, flow_failures, volume, **kwargs):
    
    
    common.error_out(volume, status='error_managing')
    LOG.error(_LE("Volume %s: manage failed."), volume.id)
    exc_info = False
    
    if all(flow_failures[-1].exc_info):
        exc_info = flow_failures[-1].exc_info
    LOG.error(_LE('Unexpected build error:'), exc_info=exc_info)

它通过rpc,在scheduler建立一系列子任务作成一个嵌套taskflow:

cinder.volume.flows.manager.manage_existing.get_flow:

volume_flow.add(create_mgr.NotifyVolumeActionTask(db, "manage_existing.start"),
                PrepareForQuotaReservationTask(db, driver),
                create_api.QuotaReserveTask(),
                ManageExistingTask(db, driver),
                create_api.QuotaCommitTask(),
                create_mgr.CreateVolumeOnFinishTask(db, "manage_existing.end"))

create_mgr.NotifyVolumeActionTask(db, “manage_existing.start”) : 发出一个rpc通知 “manage_existing.start”
PrepareForQuotaReservationTask(db, driver) : 通过驱动按下列格式返回卷的大小等信息。

size = self.driver.manage_existing_get_size(volume,
                                                    manage_existing_ref)
{'size': size,
'volume_type_id': volume.volume_type_id,
'volume_properties': volume,
'volume_spec': {'status': volume.status,
                'volume_name': volume.name,
                'volume_id': volume.id}}

create_api.QuotaReserveTask() : 预留一个卷的quota
ManageExistingTask(db, driver) : 调用驱动方法,把lun交给cinder管理
create_api.QuotaCommitTask() : 提交之前预留的quota
create_mgr.CreateVolumeOnFinishTask(db, “manage_existing.end”) : 发出一个rpc通知 “manage_existing.end”;更新volume状态available,更新launched_at时间。
以netapp为例,ManageExistingTask调用cinder.volume.drivers.netapp.dataontap.block_base.NetAppBlockStorageLibrary#manage_existing:

def manage_existing(self, volume, existing_ref):
    """Brings an existing storage object under Cinder management.

    existing_ref can contain source-id or source-name or both.
    source-id: lun uuid.
    source-name: complete lun path eg. /vol/vol0/lun.
    """
    
    lun = self._get_existing_vol_with_manage_ref(existing_ref)
    
    extra_specs = na_utils.get_volume_extra_specs(volume)
    
    self._check_volume_type_for_lun(volume, lun, existing_ref, extra_specs)
    
    qos_policy_group_info = self._setup_qos_for_volume(volume, extra_specs)
    qos_policy_group_name = (
        na_utils.get_qos_policy_group_name_from_info(
            qos_policy_group_info))
    
    path = lun.get_metadata_property
    
    
    if lun.name == volume['name']:
        new_path = path
        LOG.info(_LI("LUN with given ref %s need not be renamed "
                     "during manage operation."), existing_ref)
    else:
        
        (rest, splitter, name) = path.rpartition('/')
        
        new_path = '%s/%s' % (rest, volume['name'])
        self.zapi_client.move_lun(path, new_path)
        lun = self._get_existing_vol_with_manage_ref(
            {'source-name': new_path})

    
    if qos_policy_group_name is not None:
        self.zapi_client.set_lun_qos_policy_group(new_path,
                                                  qos_policy_group_name)
    
    self._add_lun_to_table(lun)
    LOG.info(_LI("Manage operation completed for LUN with new path"
                 " %(path)s and uuid %(uuid)s."),
             {'path': lun.get_metadata_property('Path'),
              'uuid': lun.get_metadata_property('UUID')})
    
    
    
    
    return self._get_volume_model_update(volume)

二、cinder unmanage

(1)测试:

[root@node1 ~]# cinder unmanage 2e774095-3452-436a-a524-733551a7f269
(2)源码分析:

cinder.api.contrib.volume_unmanage.VolumeUnmanageController#unmanage

vol = self.volume_api.get(context, id)
self.volume_api.delete(context, vol, unmanage_only=True)

cinder.volume.manager.VolumeManager#delete_volume

把lun信息从lun_table移除
删除volume记录
后端的lun不变

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值