| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655 |
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- from math import ceil
- from flask import Blueprint, request, url_for
- import json
- import requests
- from uuid import uuid4
- import jimit as ji
- from jimvc.models import Guest, DiskState, Host
- from jimvc.models.initialize import dev_table
- from jimvc.models import Config
- from jimvc.models import Disk
- from jimvc.models import Rules
- from jimvc.models import Utils
- from jimvc.models import OSTemplateImage
- from jimvc.models import StorageMode
- from base import Base
- __author__ = 'James Iter'
- __date__ = '2017/4/24'
- __contact__ = 'james.iter.cn@gmail.com'
- __copyright__ = '(c) 2017 by James Iter.'
- blueprint = Blueprint(
- 'api_disk',
- __name__,
- url_prefix='/api/disk'
- )
- blueprints = Blueprint(
- 'api_disks',
- __name__,
- url_prefix='/api/disks'
- )
- disk_base = Base(the_class=Disk, the_blueprint=blueprint, the_blueprints=blueprints)
- @Utils.dumps2response
- 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)
- @Utils.dumps2response
- 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)
- @Utils.dumps2response
- 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)
- def add_device(func):
- from functools import wraps
- @wraps(func)
- def _add_device(*args, **kwargs):
- ret = func(*args, **kwargs)
- if ret['data'].__len__() > 0:
- if isinstance(ret['data'], list):
- for i, item in enumerate(ret['data']):
- ret['data'][i][u'device'] = u'/dev/' + dev_table[item['sequence']]
- if item['sequence'] < 0:
- ret['data'][i][u'device'] = None
- elif isinstance(ret['data'], dict):
- ret['data'][u'device'] = u'/dev/' + dev_table[ret['data']['sequence']]
- if ret['data']['sequence'] < 0:
- ret['data'][u'device'] = None
- else:
- raise json.dumps(ret)
- return ret
- return _add_device
- @Utils.dumps2response
- @add_device
- def r_get(uuids):
- return disk_base.get(ids=uuids, ids_rule=Rules.UUIDS.value, by_field='uuid')
- @Utils.dumps2response
- @add_device
- def r_get_by_filter():
- return disk_base.get_by_filter()
- @Utils.dumps2response
- @add_device
- def r_content_search():
- return disk_base.content_search()
- @Utils.dumps2response
- 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)
- @Utils.dumps2response
- def r_distribute_count():
- from jimvc.models import Disk
- rows, count = Disk.get_all()
- ret = dict()
- ret['state'] = ji.Common.exchange_state(20000)
- ret['data'] = {
- 'kind': {'system': 0, 'data_mounted': 0, 'data_idle': 0},
- 'total_size': 0,
- 'disks': rows.__len__()
- }
- for disk in rows:
- if disk['sequence'] == 0:
- ret['data']['kind']['system'] += 1
- elif disk['sequence'] < 0:
- ret['data']['kind']['data_idle'] += 1
- else:
- ret['data']['kind']['data_mounted'] += 1
- ret['data']['total_size'] += disk['size']
- return ret
- @Utils.dumps2response
- def r_show():
- args = list()
- page = request.args.get('page', 1)
- if page == '':
- page = 1
- page = int(page)
- page_size = int(request.args.get('page_size', 10))
- keyword = request.args.get('keyword', None)
- show_area = request.args.get('show_area', 'unmount')
- guest_uuid = request.args.get('guest_uuid', None)
- sequence = request.args.get('sequence', None)
- order_by = request.args.get('order_by', None)
- order = request.args.get('order', None)
- filters = list()
- if page is not None:
- args.append('page=' + page.__str__())
- if page_size is not None:
- args.append('page_size=' + page_size.__str__())
- if keyword is not None:
- args.append('keyword=' + keyword.__str__())
- if guest_uuid is not None:
- filters.append('guest_uuid:in:' + guest_uuid.__str__())
- show_area = 'all'
- if sequence is not None:
- filters.append('sequence:in:' + sequence.__str__())
- show_area = 'all'
- if show_area in ['unmount', 'data_disk', 'all']:
- if show_area == 'unmount':
- filters.append('sequence:eq:-1')
- elif show_area == 'data_disk':
- filters.append('sequence:gt:0')
- else:
- pass
- else:
- # 与前端页面相照应,首次打开时,默认只显示未挂载的磁盘
- filters.append('sequence:eq:-1')
- if order_by is not None:
- args.append('order_by=' + order_by)
- if order is not None:
- args.append('order=' + order)
- if filters.__len__() > 0:
- args.append('filter=' + ','.join(filters))
- hosts_url = url_for('api_hosts.r_get_by_filter', _external=True)
- disks_url = url_for('api_disks.r_get_by_filter', _external=True)
- if keyword is not None:
- disks_url = url_for('api_disks.r_content_search', _external=True)
- # 关键字检索,不支持显示域过滤
- show_area = 'all'
- hosts_ret = requests.get(url=hosts_url, cookies=request.cookies)
- hosts_ret = json.loads(hosts_ret.content)
- hosts_mapping_by_node_id = dict()
- for host in hosts_ret['data']:
- hosts_mapping_by_node_id[int(host['node_id'])] = host
- if args.__len__() > 0:
- disks_url = disks_url + '?' + '&'.join(args)
- disks_ret = requests.get(url=disks_url, cookies=request.cookies)
- disks_ret = json.loads(disks_ret.content)
- guests_uuid = list()
- disks_uuid = list()
- for disk in disks_ret['data']:
- disks_uuid.append(disk['uuid'])
- if disk['guest_uuid'].__len__() == 36:
- guests_uuid.append(disk['guest_uuid'])
- if guests_uuid.__len__() > 0:
- guests, _ = Guest.get_by_filter(filter_str='uuid:in:' + ','.join(guests_uuid))
- guests_uuid_mapping = dict()
- for guest in guests:
- guests_uuid_mapping[guest['uuid']] = guest
- for i, disk in enumerate(disks_ret['data']):
- if disk['guest_uuid'].__len__() == 36:
- disks_ret['data'][i]['guest'] = guests_uuid_mapping[disk['guest_uuid']]
- if disks_uuid.__len__() > 0:
- snapshots_id_mapping_by_disks_uuid_url = url_for('api_snapshots.r_get_snapshots_by_disks_uuid',
- disks_uuid=','.join(disks_uuid), _external=True)
- snapshots_id_mapping_by_disks_uuid_ret = requests.get(url=snapshots_id_mapping_by_disks_uuid_url,
- cookies=request.cookies)
- snapshots_id_mapping_by_disks_uuid_ret = json.loads(snapshots_id_mapping_by_disks_uuid_ret.content)
- snapshots_id_mapping_by_disk_uuid = dict()
- for snapshot_id_mapping_by_disk_uuid in snapshots_id_mapping_by_disks_uuid_ret['data']:
- disk_uuid = snapshot_id_mapping_by_disk_uuid['disk_uuid']
- snapshot_id = snapshot_id_mapping_by_disk_uuid['snapshot_id']
- if disk_uuid not in snapshots_id_mapping_by_disk_uuid:
- snapshots_id_mapping_by_disk_uuid[disk_uuid] = list()
- snapshots_id_mapping_by_disk_uuid[disk_uuid].append(snapshot_id)
- for i, disk in enumerate(disks_ret['data']):
- if disk['uuid'] in snapshots_id_mapping_by_disk_uuid:
- disks_ret['data'][i]['snapshot'] = snapshots_id_mapping_by_disk_uuid[disk['uuid']]
- config = Config()
- config.id = 1
- config.get()
- show_on_host = False
- if config.storage_mode == StorageMode.local.value:
- show_on_host = True
- last_page = int(ceil(disks_ret['paging']['total'] / float(page_size)))
- page_length = 5
- pages = list()
- if page < int(ceil(page_length / 2.0)):
- for i in range(1, page_length + 1):
- pages.append(i)
- if i == last_page or last_page == 0:
- break
- elif last_page - page < page_length / 2:
- for i in range(last_page - page_length + 1, last_page + 1):
- if i < 1:
- continue
- pages.append(i)
- else:
- for i in range(page - page_length / 2, page + int(ceil(page_length / 2.0))):
- pages.append(i)
- if i == last_page or last_page == 0:
- break
- ret = dict()
- ret['state'] = ji.Common.exchange_state(20000)
- ret['data'] = {
- 'disks': disks_ret['data'],
- 'hosts_mapping_by_node_id': hosts_mapping_by_node_id,
- 'order_by': order_by,
- 'order': order,
- 'show_area': show_area,
- 'config': config.__dict__,
- 'show_on_host': show_on_host,
- 'paging': disks_ret['paging'],
- 'page': page,
- 'page_size': page_size,
- 'keyword': keyword,
- 'pages': pages,
- 'last_page': last_page
- }
- return ret
- @Utils.dumps2response
- def r_detail(uuid):
- disk = Disk()
- disk.uuid = uuid
- disk.get_by(field='uuid')
- disk.wrap_device(dev_table=dev_table)
- guest = None
- os_template_image = None
- config = Config()
- config.id = 1
- config.get()
- if disk.sequence != -1:
- guest = Guest()
- guest.uuid = disk.guest_uuid
- guest.get_by('uuid')
- os_template_image = OSTemplateImage()
- os_template_image.id = guest.os_template_image_id
- os_template_image.get()
- guest = guest.__dict__
- os_template_image = os_template_image.__dict__
- ret = dict()
- ret['state'] = ji.Common.exchange_state(20000)
- ret['data'] = {
- 'guest': guest,
- 'os_template_image': os_template_image,
- 'disk': disk.__dict__,
- 'config': config.__dict__
- }
- return ret
|