Exemple #1
0
def r_resize(uuid, size):

    args_rules = [Rules.UUID.value, Rules.DISK_SIZE_STR.value]

    try:
        ji.Check.previewing(args_rules, {'uuid': uuid, 'size': size})

        disk = Disk()
        disk.uuid = uuid
        disk.get_by('uuid')

        ret = dict()
        ret['state'] = ji.Common.exchange_state(20000)

        if disk.size >= int(size):
            ret['state'] = ji.Common.exchange_state(41257)
            return ret

        config = Config()
        config.id = 1
        config.get()

        disk.size = int(size)
        disk.quota(config=config)
        # 将在事件返回层(models/event_processor.py:224 附近),更新数据库中 disk 对象

        message = {
            '_object': 'disk',
            'action': 'resize',
            'uuid': disk.uuid,
            'guest_uuid': disk.guest_uuid,
            'storage_mode': config.storage_mode,
            'size': disk.size,
            'dfs_volume': config.dfs_volume,
            'node_id': disk.node_id,
            'image_path': disk.path,
            'disks': [disk.__dict__],
            'passback_parameters': {
                'size': disk.size
            }
        }

        if config.storage_mode in [
                StorageMode.shared_mount.value, StorageMode.ceph.value,
                StorageMode.glusterfs.value
        ]:
            message['node_id'] = Host.get_lightest_host()['node_id']

        if disk.guest_uuid.__len__() == 36:
            message['device_node'] = dev_table[disk.sequence]

        Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))

        return ret

    except ji.PreviewingError, e:
        return json.loads(e.message)
Exemple #2
0
def update_ssh_key(uuid):

    guest = Guest()
    guest.uuid = uuid
    guest.get_by('uuid')

    # 不支持更新离线虚拟机的 SSH-KEY
    if guest.status != GuestState.running.value:
        return

    os_template_image = OSTemplateImage()
    os_template_profile = OSTemplateProfile()

    os_template_image.id = guest.os_template_image_id
    os_template_image.get()

    os_template_profile.id = os_template_image.os_template_profile_id
    os_template_profile.get()

    # 不支持更新 Windows 虚拟机的 SSH-KEY
    if os_template_profile.os_type == 'windows':
        return

    rows, _ = SSHKeyGuestMapping.get_by_filter(
        filter_str=':'.join(['guest_uuid', 'eq', uuid]))

    ssh_keys_id = list()
    for row in rows:
        ssh_keys_id.append(row['ssh_key_id'].__str__())

    ssh_keys = list()

    if ssh_keys_id.__len__() > 0:
        rows, _ = SSHKey.get_by_filter(
            filter_str=':'.join(['id', 'in', ','.join(ssh_keys_id)]))
        for row in rows:
            ssh_keys.append(row['public_key'])

    else:
        ssh_keys.append('')

    message = {
        '_object': 'guest',
        'uuid': uuid,
        'node_id': guest.node_id,
        'action': 'update_ssh_key',
        'ssh_keys': ssh_keys,
        'os_type': os_template_profile.os_type,
        'passback_parameters': {
            'uuid': uuid,
            'ssh_keys': ssh_keys,
            'os_type': os_template_profile.os_type
        }
    }

    Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))
def r_delete(ids):
    ret = dict()
    ret['state'] = ji.Common.exchange_state(20000)

    config = Config()
    config.id = 1
    config.get()

    # 取全部活着的 hosts
    available_hosts = Host.get_available_hosts(nonrandom=None)

    if available_hosts.__len__() == 0:
        ret['state'] = ji.Common.exchange_state(50351)
        return ret

    chosen_host = available_hosts[0]
    node_id = chosen_host['node_id']

    os_template_image = OSTemplateImage()

    # TODO: 加入对,是否有被 Guest 引用的判断
    for _id in ids.split(','):
        os_template_image.id = _id
        os_template_image.get()

    for _id in ids.split(','):
        os_template_image.id = _id
        os_template_image.get()

        # 暂时不支持从计算节点上,删除公共镜像
        if os_template_image.kind == OSTemplateImageKind.public.value:
            os_template_image.delete()
            continue

        elif os_template_image.kind == OSTemplateImageKind.custom.value:
            os_template_image.progress = 254

            message = {
                '_object': 'os_template_image',
                'action': 'delete',
                'storage_mode': config.storage_mode,
                'dfs_volume': config.dfs_volume,
                'template_path': os_template_image.path,
                # uuid 这里没有实际意义,仅仅是为了迁就 JimV-C 的个命令格式
                'uuid': None,
                'node_id': node_id,
                'os_template_image_id': os_template_image.id,
                'passback_parameters': {'id': os_template_image.id}
            }

            Utils.emit_instruction(message=json.dumps(message))

            os_template_image.update()

    return ret
Exemple #4
0
def r_delete(snapshots_id):

    args_rules = [Rules.SNAPSHOTS_ID.value]

    try:
        ji.Check.previewing(args_rules, {'snapshots_id': snapshots_id})

        snapshot = Snapshot()
        guest = Guest()

        # 检测所指定的 快照 都存在
        for snapshot_id in snapshots_id.split(','):
            snapshot.snapshot_id = snapshot_id
            snapshot.get_by('snapshot_id')

            guest.uuid = snapshot.guest_uuid
            guest.get_by('uuid')

        # 执行删除操作
        for snapshot_id in snapshots_id.split(','):
            snapshot.snapshot_id = snapshot_id
            snapshot.get_by('snapshot_id')

            guest.uuid = snapshot.guest_uuid
            guest.get_by('uuid')

            message = {
                '_object': 'snapshot',
                'action': 'delete',
                'uuid': snapshot.guest_uuid,
                'snapshot_id': snapshot.snapshot_id,
                'node_id': guest.node_id,
                'passback_parameters': {
                    'id': snapshot.id
                }
            }

            Utils.emit_instruction(message=json.dumps(message))

            # 删除创建失败的 快照
            if snapshot.progress == 255:
                SnapshotDiskMapping.delete_by_filter(filter_str=':'.join(
                    ['snapshot_id', 'eq', snapshot.snapshot_id]))
                snapshot.delete()

            else:
                snapshot.progress = 254
                snapshot.update()

        ret = dict()
        ret['state'] = ji.Common.exchange_state(20000)
        return ret

    except ji.PreviewingError, e:
        return json.loads(e.message)
Exemple #5
0
def r_delete(uuids):

    args_rules = [Rules.UUIDS.value]

    try:
        ji.Check.previewing(args_rules, {'uuids': uuids})

        ret = dict()
        ret['state'] = ji.Common.exchange_state(20000)

        disk = Disk()

        # 检测所指定的 UUDIs 磁盘都存在
        for uuid in uuids.split(','):
            disk.uuid = uuid
            disk.get_by('uuid')

            # 判断磁盘是否与虚拟机处于离状态
            if disk.state not in [DiskState.idle.value, DiskState.dirty.value]:
                ret['state'] = ji.Common.exchange_state(41256)
                return ret

        config = Config()
        config.id = 1
        config.get()

        # 执行删除操作
        for uuid in uuids.split(','):
            disk.uuid = uuid
            disk.get_by('uuid')

            message = {
                '_object': 'disk',
                'action': 'delete',
                'uuid': disk.uuid,
                'storage_mode': config.storage_mode,
                'dfs_volume': config.dfs_volume,
                'node_id': disk.node_id,
                'image_path': disk.path
            }

            if config.storage_mode in [
                    StorageMode.shared_mount.value, StorageMode.ceph.value,
                    StorageMode.glusterfs.value
            ]:
                message['node_id'] = Host.get_lightest_host()['node_id']

            Utils.emit_instruction(
                message=json.dumps(message, ensure_ascii=False))

        return ret

    except ji.PreviewingError, e:
        return json.loads(e.message)
Exemple #6
0
def r_after_request(response):
    try:
        # https://developer.mozilla.org/en/HTTP_access_control
        # (中文版) https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS#Access-Control-Allow-Credentials
        # http://www.w3.org/TR/cors/
        # 由于浏览器同源策略,凡是发送请求url的协议、域名、端口三者之间任意一与当前页面地址不同即为跨域。

        if request.referrer is None:
            # 跑测试脚本时,用该规则。
            response.headers['Access-Control-Allow-Origin'] = '*'
        else:
            # 生产环境中,如果前后端分离。那么请指定具体的前端域名地址,不要用如下在开发环境中的便捷方式。
            # -- Access-Control-Allow-Credentials为true,携带cookie时,不允许Access-Control-Allow-Origin为通配符,是浏览器对用户的一种安全保护。
            # -- 至少能避免登录山寨网站,骗取用户相关信息。
            response.headers['Access-Control-Allow-Origin'] = '/'.join(
                request.referrer.split('/')[:3])

        response.headers['Access-Control-Allow-Credentials'] = 'true'
        response.headers[
            'Access-Control-Allow-Methods'] = 'HEAD, GET, POST, DELETE, OPTIONS, PATCH, PUT'
        response.headers[
            'Access-Control-Allow-Headers'] = 'X-Request-With, Content-Type'
        response.headers['Access-Control-Expose-Headers'] = 'Set-Cookie'

        # 少于session生命周期一半时,自动对其续期
        if not is_not_need_to_auth(request.endpoint) and hasattr(g, 'token') and \
                g.token['exp'] < (ji.Common.ts() + (app_config['token_ttl'] / 2)):
            token = Utils.generate_token(g.token['uid'])
            # 清除原有session,由新session代替
            for key in session.keys():
                session.pop(key=key)
            session['token'] = token
        return response
    except ji.JITError, e:
        return json.loads(e.message)
Exemple #7
0
def r_sign_in():

    args_rules = [Rules.LOGIN_NAME.value, Rules.PASSWORD.value]

    user = User()
    user.login_name = request.json.get('login_name')
    user.password = request.json.get('password')

    try:
        ji.Check.previewing(args_rules, user.__dict__)
        plain_password = user.password
        user.get_by('login_name')

        if not ji.Security.ji_pbkdf2_check(password=plain_password,
                                           password_hash=user.password):
            ret = dict()
            ret['state'] = ji.Common.exchange_state(40101)
            ret['state']['sub']['zh-cn'] = ''.join(
                [ret['state']['sub']['zh-cn'], u': 鉴权失败'])
            raise ji.PreviewingError(json.dumps(ret, ensure_ascii=False))

        token = Utils.generate_token(user.id)
        session['token'] = token
        rep = make_response()
        rep.data = json.dumps({'state': ji.Common.exchange_state(20000)},
                              ensure_ascii=False)
        return rep

    except ji.PreviewingError, e:
        return json.loads(e.message)
Exemple #8
0
def r_create():

    args_rules = [Rules.GUEST_UUID.value]

    if 'label' in request.json:
        args_rules.append(Rules.LABEL.value, )

    try:
        ret = dict()
        ret['state'] = ji.Common.exchange_state(20000)

        ji.Check.previewing(args_rules, request.json)

        snapshot = Snapshot()
        guest = Guest()
        guest.uuid = request.json.get('guest_uuid')
        guest.get_by('uuid')

        snapshot.label = request.json.get('label', '')
        snapshot.status = guest.status
        snapshot.guest_uuid = guest.uuid
        snapshot.snapshot_id = '_'.join(
            ['tmp', ji.Common.generate_random_code(length=8)])
        snapshot.parent_id = '-'
        snapshot.progress = 0

        snapshot.create()
        snapshot.get_by('snapshot_id')

        message = {
            '_object': 'snapshot',
            'action': 'create',
            'uuid': guest.uuid,
            'node_id': guest.node_id,
            'passback_parameters': {
                'id': snapshot.id
            }
        }

        Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))

        ret['data'] = snapshot.__dict__
        return ret

    except ji.PreviewingError, e:
        return json.loads(e.message)
Exemple #9
0
def r_revert(snapshot_id):

    args_rules = [Rules.SNAPSHOT_ID.value]

    try:
        ret = dict()
        ret['state'] = ji.Common.exchange_state(20000)

        ji.Check.previewing(args_rules, {'snapshot_id': snapshot_id})

        snapshot = Snapshot()
        guest = Guest()

        snapshot.snapshot_id = snapshot_id
        snapshot.get_by('snapshot_id')
        snapshot.progress = 253
        snapshot.update()
        snapshot.get()

        guest.uuid = snapshot.guest_uuid
        guest.get_by('uuid')

        message = {
            '_object': 'snapshot',
            'action': 'revert',
            'uuid': guest.uuid,
            'snapshot_id': snapshot.snapshot_id,
            'node_id': guest.node_id,
            'passback_parameters': {
                'id': snapshot.id
            }
        }

        Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))

        ret['data'] = snapshot.__dict__
        return ret

    except ji.PreviewingError, e:
        return json.loads(e.message)
Exemple #10
0
def r_send_reset_password_email(login_name):

    args_rules = [Rules.LOGIN_NAME.value]

    try:
        ji.Check.previewing(args_rules, {'login_name': login_name})

        user = User()
        try:
            user.login_name = login_name
            user.get_by('login_name')

        except ji.PreviewingError, e:
            # 如果 login_name 没有找到,则尝试从email里面查找
            # 因为用户可能会把登录名理解成email
            user.email = login_name
            user.get_by('email')

        host_url = request.host_url.rstrip('/')
        # 5 分钟有效期
        token = Utils.generate_token(uid=user.id,
                                     ttl=300,
                                     audience='r_reset_password')

        reset_password_url = '/'.join([host_url, 'reset_password', token])

        smtp_server = ji.NetUtils.smtp_init(
            host=app_config['smtp_host'],
            port=app_config.get('smtp_port', None),
            login_name=app_config['smtp_user'],
            password=app_config['smtp_password'],
            tls=app_config['smtp_starttls'])

        ji.NetUtils.send_mail(smtp_server=smtp_server,
                              sender=app_config['smtp_user'],
                              receivers=[user.email],
                              title=u'重置登录密码',
                              message=u'请复制以下地址到浏览器中打开:' + reset_password_url)

        ret = dict()
        ret['state'] = ji.Common.exchange_state(20000)
        ret['data'] = {'email': user.email}

        return ret
Exemple #11
0
def r_reset_password(token):

    args_rules = [Rules.TOKEN.value]

    try:
        ji.Check.previewing(args_rules, {'token': token})

        token = Utils.verify_token(token, audience='r_reset_password')

        user = User()
        user.id = token['uid']
        user.get()

        args_rules = [Rules.PASSWORD.value]

        user.password = request.json.get('password')

        ji.Check.previewing(args_rules, user.__dict__)
        user.password = ji.Security.ji_pbkdf2(user.password)
        user.update()
    except (ji.PreviewingError, ji.JITError), e:
        return json.loads(e.message)
Exemple #12
0
def r_before_request():
    try:
        g.ts = ji.Common.ts()
        if not is_not_need_to_auth(
                request.endpoint
        ) and request.blueprint is not None and request.method != 'OPTIONS':
            g.config = Config()
            g.config.id = 1
            g.config.get()

            token = session.get('token', '')
            g.token = Utils.verify_token(token)

            user = User()
            user.id = g.token['uid']

            try:
                user.get()
            except ji.PreviewingError, e:
                # 如果该用户获取失败,则清除该用户对应的session。因为该用户可能已经被删除。
                for key in session.keys():
                    session.pop(key=key)
                return json.loads(e.message)

    except ji.JITError, e:
        ret = json.loads(e.message)

        if ret['state']['code'] == '404':
            return redirect(location=url_for('v_config.create'),
                            Response=Response)

        if ret['state']['sub']['code'] in ['41208']:
            return redirect(location=url_for('v_misc.login'),
                            Response=Response)

        return ret
Exemple #13
0
def r_create():

    args_rules = [
        Rules.DISK_SIZE.value, Rules.REMARK.value, Rules.QUANTITY.value
    ]

    config = Config()
    config.id = 1
    config.get()

    # 非共享模式,必须指定 node_id
    if config.storage_mode not in [
            StorageMode.shared_mount.value, StorageMode.ceph.value,
            StorageMode.glusterfs.value
    ]:
        args_rules.append(Rules.NODE_ID.value)

    try:
        ji.Check.previewing(args_rules, request.json)

        size = request.json['size']
        quantity = request.json['quantity']

        ret = dict()
        ret['state'] = ji.Common.exchange_state(20000)

        # 如果是共享模式,则让负载最轻的计算节点去创建磁盘
        if config.storage_mode in [
                StorageMode.shared_mount.value, StorageMode.ceph.value,
                StorageMode.glusterfs.value
        ]:
            available_hosts = Host.get_available_hosts()

            if available_hosts.__len__() == 0:
                ret['state'] = ji.Common.exchange_state(50351)
                return ret

            # 在可用计算节点中平均分配任务
            chosen_host = available_hosts[quantity % available_hosts.__len__()]
            request.json['node_id'] = chosen_host['node_id']

        node_id = request.json['node_id']

        if size < 1:
            ret['state'] = ji.Common.exchange_state(41255)
            return ret

        while quantity:
            quantity -= 1
            disk = Disk()
            disk.guest_uuid = ''
            disk.size = size
            disk.uuid = uuid4().__str__()
            disk.remark = request.json.get('remark', '')
            disk.node_id = int(node_id)
            disk.sequence = -1
            disk.format = 'qcow2'
            disk.path = config.storage_path + '/' + disk.uuid + '.' + disk.format
            disk.quota(config=config)

            message = {
                '_object': 'disk',
                'action': 'create',
                'uuid': disk.uuid,
                'storage_mode': config.storage_mode,
                'dfs_volume': config.dfs_volume,
                'node_id': disk.node_id,
                'image_path': disk.path,
                'size': disk.size
            }

            Utils.emit_instruction(
                message=json.dumps(message, ensure_ascii=False))

            disk.create()

        return ret

    except ji.PreviewingError, e:
        return json.loads(e.message)
Exemple #14
0
def r_update(uuids):

    ret = dict()
    ret['state'] = ji.Common.exchange_state(20000)
    ret['data'] = list()

    args_rules = [Rules.UUIDS.value]

    if 'remark' in request.json:
        args_rules.append(Rules.REMARK.value)

    if 'iops' in request.json:
        args_rules.append(Rules.IOPS.value)

    if 'iops_rd' in request.json:
        args_rules.append(Rules.IOPS_RD.value)

    if 'iops_wr' in request.json:
        args_rules.append(Rules.IOPS_WR.value)

    if 'iops_max' in request.json:
        args_rules.append(Rules.IOPS_MAX.value)

    if 'iops_max_length' in request.json:
        args_rules.append(Rules.IOPS_MAX_LENGTH.value)

    if 'bps' in request.json:
        args_rules.append(Rules.BPS.value)

    if 'bps_rd' in request.json:
        args_rules.append(Rules.BPS_RD.value)

    if 'bps_wr' in request.json:
        args_rules.append(Rules.BPS_WR.value)

    if 'bps_max' in request.json:
        args_rules.append(Rules.BPS_MAX.value)

    if 'bps_max_length' in request.json:
        args_rules.append(Rules.BPS_MAX_LENGTH.value)

    if args_rules.__len__() < 2:
        return ret

    request.json['uuids'] = uuids

    need_update_quota = False
    need_update_quota_parameters = [
        'iops', 'iops_rd', 'iops_wr', 'iops_max', 'iops_max_length', 'bps',
        'bps_rd', 'bps_wr', 'bps_max', 'bps_max_length'
    ]

    if filter(lambda p: p in request.json,
              need_update_quota_parameters).__len__() > 0:
        need_update_quota = True

    try:
        ji.Check.previewing(args_rules, request.json)

        disk = Disk()

        # 检测所指定的 UUDIs 磁盘都存在
        for uuid in uuids.split(','):
            disk.uuid = uuid
            disk.get_by('uuid')

        for uuid in uuids.split(','):
            disk.uuid = uuid
            disk.get_by('uuid')
            disk.remark = request.json.get('remark', disk.remark)
            disk.iops = request.json.get('iops', disk.iops)
            disk.iops_rd = request.json.get('iops_rd', disk.iops_rd)
            disk.iops_wr = request.json.get('iops_wr', disk.iops_wr)
            disk.iops_max = request.json.get('iops_max', disk.iops_max)
            disk.iops_max_length = request.json.get('iops_max_length',
                                                    disk.iops_max_length)
            disk.bps = request.json.get('bps', disk.bps)
            disk.bps_rd = request.json.get('bps_rd', disk.bps_rd)
            disk.bps_wr = request.json.get('bps_wr', disk.bps_wr)
            disk.bps_max = request.json.get('bps_max', disk.bps_max)
            disk.bps_max_length = request.json.get('bps_max_length',
                                                   disk.bps_max_length)
            disk.update()
            disk.get()

            if disk.sequence >= 0 and need_update_quota:
                message = {
                    '_object': 'disk',
                    'action': 'quota',
                    'uuid': disk.uuid,
                    'guest_uuid': disk.guest_uuid,
                    'node_id': disk.node_id,
                    'disks': [disk.__dict__]
                }

                Utils.emit_instruction(message=json.dumps(message))

            ret['data'].append(disk.__dict__)

        return ret

    except ji.PreviewingError, e:
        return json.loads(e.message)
Exemple #15
0
def r_update_quota():

    config = Config()

    args_rules = [
    ]

    if 'iops_base' in request.json:
        args_rules.append(
            Rules.IOPS_BASE.value,
        )

    if 'iops_pre_unit' in request.json:
        args_rules.append(
            Rules.IOPS_PRE_UNIT.value,
        )

    if 'iops_cap' in request.json:
        args_rules.append(
            Rules.IOPS_CAP.value,
        )

    if 'iops_max' in request.json:
        args_rules.append(
            Rules.IOPS_MAX.value,
        )

    if 'iops_max_length' in request.json:
        args_rules.append(
            Rules.IOPS_MAX_LENGTH.value,
        )

    if 'bps_base' in request.json:
        args_rules.append(
            Rules.BPS_BASE.value,
        )

    if 'bps_pre_unit' in request.json:
        args_rules.append(
            Rules.BPS_PRE_UNIT.value,
        )

    if 'bps_cap' in request.json:
        args_rules.append(
            Rules.BPS_CAP.value,
        )

    if 'bps_max' in request.json:
        args_rules.append(
            Rules.BPS_MAX.value,
        )

    if 'bps_max_length' in request.json:
        args_rules.append(
            Rules.BPS_MAX_LENGTH.value,
        )

    if 'influence_current_guest' in request.json:
        args_rules.append(
            Rules.INFLUENCE_CURRENT_GUEST.value,
        )

    if args_rules.__len__() < 1:
        ret = dict()
        ret['state'] = ji.Common.exchange_state(20000)
        return ret

    try:
        config.id = 1
        ji.Check.previewing(args_rules, request.json)
        config.get()

        config.iops_base = int(request.json.get('iops_base', config.iops_base))
        config.iops_pre_unit = int(request.json.get('iops_pre_unit', config.iops_pre_unit))
        config.iops_cap = int(request.json.get('iops_cap', config.iops_cap))
        config.iops_max = int(request.json.get('iops_max', config.iops_max))
        config.iops_max_length = int(request.json.get('iops_max_length', config.iops_max_length))
        config.bps_base = int(request.json.get('bps_base', config.bps_base))
        config.bps_pre_unit = int(request.json.get('bps_pre_unit', config.bps_pre_unit))
        config.bps_cap = int(request.json.get('bps_cap', config.bps_cap))
        config.bps_max = int(request.json.get('bps_max', config.bps_max))
        config.bps_max_length = int(request.json.get('bps_max_length', config.bps_max_length))

        if request.json.get('influence_current_guest', False):
            disks, _ = Disk.get_all()

            disk = Disk()
            for disk_info in disks:
                disk.id = disk_info['id']
                disk.get()
                disk.quota(config=config)
                disk.update()

                if disk.sequence >= 0:
                    message = {
                        '_object': 'disk',
                        'action': 'quota',
                        'uuid': disk.uuid,
                        'guest_uuid': disk.guest_uuid,
                        'node_id': disk.node_id,
                        'disks': [disk.__dict__]
                    }

                    Utils.emit_instruction(message=json.dumps(message))

        config.update()
        config.get()

        ret = dict()
        ret['state'] = ji.Common.exchange_state(20000)
        ret['data'] = config.__dict__

        return ret
    except ji.PreviewingError, e:
        return json.loads(e.message)
Exemple #16
0
def r_convert_to_os_template_image(snapshot_id, disk_uuid):

    args_rules = [
        Rules.SNAPSHOT_ID.value, Rules.DISK_UUID.value, Rules.LABEL.value
    ]

    try:
        ret = dict()
        ret['state'] = ji.Common.exchange_state(20000)

        ji.Check.previewing(
            args_rules, {
                'snapshot_id': snapshot_id,
                'disk_uuid': disk_uuid,
                'label': request.json.get('label')
            })

        rows, _ = SnapshotDiskMapping.get_by_filter(
            filter_str=':'.join(['snapshot_id', 'eq', snapshot_id]))

        disks_uuid = list()

        for row in rows:
            disks_uuid.append(row['disk_uuid'])

        if disk_uuid not in disks_uuid:
            ret['state'] = ji.Common.exchange_state(40401)
            ret['state']['sub']['zh-cn'] = ''.join([
                ret['state']['sub']['zh-cn'], u': 未在快照: ', snapshot_id,
                u' 中找到磁盘:', disk_uuid
            ])
            return ret

        config = Config()
        config.id = 1
        config.get()

        snapshot = Snapshot()
        os_template_image = OSTemplateImage()
        guest = Guest()
        disk = Disk()

        snapshot.snapshot_id = snapshot_id
        snapshot.get_by('snapshot_id')
        snapshot.progress = 252

        guest.uuid = snapshot.guest_uuid
        guest.get_by('uuid')

        disk.uuid = disk_uuid
        disk.get_by('uuid')

        os_template_image.id = guest.os_template_image_id
        os_template_image.get()

        image_name = '_'.join([snapshot.snapshot_id, disk.uuid
                               ]) + '.' + disk.format

        os_template_image.id = 0
        os_template_image.label = request.json.get('label')
        os_template_image.path = '/'.join(
            [os.path.dirname(os_template_image.path), image_name])
        os_template_image.kind = OSTemplateImageKind.custom.value
        os_template_image.progress = 0
        os_template_image.create_time = ji.Common.tus()

        if os_template_image.exist_by('path'):
            ret['state'] = ji.Common.exchange_state(40901)
            ret['state']['sub']['zh-cn'] = ''.join(
                [ret['state']['sub']['zh-cn'], ': ', os_template_image.path])
            return ret

        os_template_image.create()
        os_template_image.get_by('path')

        message = {
            '_object': 'snapshot',
            'action': 'convert',
            'uuid': disk.guest_uuid,
            'snapshot_id': snapshot.snapshot_id,
            'storage_mode': config.storage_mode,
            'dfs_volume': config.dfs_volume,
            'node_id': disk.node_id,
            'snapshot_path': disk.path,
            'template_path': os_template_image.path,
            'os_template_image_id': os_template_image.id,
            'passback_parameters': {
                'id': snapshot.snapshot_id,
                'os_template_image_id': os_template_image.id
            }
        }

        Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))

        snapshot.update()

        return ret

    except ji.PreviewingError, e:
        return json.loads(e.message)