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)
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
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)
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 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)
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)
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)
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)
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
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)
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
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)
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)
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)
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)