egg.js+mongodb+openstack 公有云计费系统(三)OpenStack 对接 blcokStorage(2)

磁盘相关
控制器
'use strict';

const Controller = require('egg').Controller;

class OPBlcokStorageVolumeController extends Controller {
constructor(ctx) {
super(ctx);
this.auth = ctx.state.user;
this.QMFN = ctx.helper.queryParamFn;
this.DUFN = ctx.helper.deleteUndefined;
this.volumeMODEL = ctx.model.Openstack.BlcokStorage.Volumes;
this.volumeSERVICE = ctx.service.openstack.blcokStorage.volume;
};
/**
* @name 磁盘列表
*
* @param {String} detail 不需要参数
*
* ----------------------------------- 分页 -------------------------------------
*
* @param {String} limit 分页>>条数/页
* @param {String} marker markerID(上页最后条id)
*
* ----------------------------------- 排序 -------------------------------------
*
* @param {String} sort_dir a|b 排序方式 ['asc':default,'desc']
* @param {String} sort_key 排序字段 [status,name,created_at]
*
* @description query参数detail存在则显示实力配置列表详细信息,若不传仅显示概要列表
*
* @example GET /openstack/blcokstorage/volume?{detail}
*
*/
async index() {
const { ctx } = this;
const RESULT = await this.volumeMODEL.getUsers(this.auth.id, this.QMFN({...ctx.query }));
ctx.body = RESULT;
};
/**
* @name 获取单一磁盘
*
* @description
*
* @example GET /openstack/blcokstorage/volume/{id}
*
*/
async show() {
const ctx = this.ctx;
// const datas = await this.volumeSERVICE.show(ctx.params.id, this.auth.id);
// ctx.body = datas;
const RESULT = await this.volumeMODEL.getOne(this.auth.id, ctx.params.id, ctx.isAdmin())
ctx.body = RESULT;
};
/**
* @name 创建磁盘
*
* @description
*
* @example POST /openstack/blcokstorage/volume
*
* @param {String} size 大小
* @param {String} availability_zone Optional 可用区域
* @param {String} snapshot_id Optional 快照ID FN从快照创建
* @param {String} backup_id Optional 备份ID FN从备份创建
* @param {String} imageRef Optional 镜像ID FN从镜像创建
* @param {String} volume_type Optional 磁盘类型 默认HDD
*
*/
async create() {
const { ctx } = this;
const { availability_zone, snapshot_id, backup_id, imageRef, size = 5, volume_type = "hdd" } = ctx.request.body;
const BODYS = this.DUFN({ availability_zone, snapshot_id, backup_id, imageRef, size, volume_type })
const datas = await this.volumeMODEL.createOne(this.auth.id, BODYS)
ctx.body = datas
};
/**
* @name 调整磁盘
*
* @description
*
* @example POST /openstack/blcokstorage/volume
*
* @param {String} size 大小
* @param {String} availability_zone Optional 可用区域
* @param {String} snapshot_id Optional 快照ID FN从快照创建
* @param {String} backup_id Optional 备份ID FN从备份创建
* @param {String} imageRef Optional 镜像ID FN从镜像创建
* @param {String} volume_type Optional 磁盘类型 默认HDD
*
*/
async update() {
const ctx = this.ctx;
const BODYS = ctx.request.body;
const { action, name } = BODYS;
delete BODYS.action;
const datas = !!action ? await this.volumeSERVICE.action(this.auth.id, this.auth.ProjectID, ctx.params.id, action, BODYS) : await this.serverSERVICE.update(ctx.params.id, this.auth.id, this.DUFn({ name }));
ctx.body = datas
};
/**
* @name 删除磁盘
*
* @description
*
*/
async destroy() {
const ctx = this.ctx;
const DATAS = await this.volumeSERVICE.destroy(this.auth.id, this.auth.ProjectID, ctx.params.id);
ctx.body = DATAS
}

}

module.exports = OPBlcokStorageVolumeController;
model
module.exports = app => {
const mongoose = app.mongoose;
const Schema = mongoose.Schema;
const ctx = app.createAnonymousContext();
const {
helper,
service
} = app.createAnonymousContext();
const {
NumToStr
} = ctx.helper;
const OSblockStorageVolumeSchema = new Schema({
// 配置
config: {},
// 公司id
_comp: {
type: Schema.Types.ObjectId,
ref: 'Company',
required: true,
index: true
},
// 支付方式
payment: {
type: String,
default: 'prepay'
},
// 到期时间
endTime: {
type: Date
},
// 后台备注描述
_description: {

},
// 源信息
_metadata: {
type: String,
index: true
},
/*---------------------------------------------------*/
"status": {
type: 'String',
default: 'creating'
},
"migration_status": {},
"user_id": {},
"attachments": ,
"availability_zone": {},
"bootable": {},
"encrypted": {},
"created_at": {},
"description": {},
"updated_at": {},
"volume_type": {},
"name": {},
"replication_status": {},
"consistencygroup_id": {},
"source_volid": {},
"imageRef": {},
"backup_id": {},
"snapshot_id": {},
"multiattach": {},
"metadata": {},
"id": {
type: String,
index: true
},
"size": {},
"os-vol-host-attr:host": {},
"os-vol-tenant-attr:tenant_id": {}
}, {
timestamps: {
createdAt: 'created',
updatedAt: 'updated'
}
});
OSblockStorageVolumeSchema.statics = {
getUsers: async function(_comp, queryMix) {
const serverSERVICE = ctx.service.openstack.server.servers;
const {
querys,
select,
sort,
limit,
marker
} = queryMix;
const withDetail = querys.detail === '' || querys.detail;
delete querys.detail;
let QUERTS = {};
for (const key in querys) {
QUERTS[key] = eval(`/${querys[key]}.*/i`)
}
const MODELS = (count = false) => this.find({
_comp,
...QUERTS,
...(marker && !count && {
'_id': {
"$lt": marker
}
}),
}, {
...select,
}, {
...!count && {
limit
},
'sort': {
_id: -1,
...sort
}
});
try {
const datas = await MODELS()
return {
data: {
result: datas,
limit,
marker: datas.length ? [...datas].pop()._id : '',
totalCount: await MODELS(true).count()
}
}
} catch (error) {
return error
}
},
/**
* @name 创建硬盘
* @param {*} _id
* @param {*} body
*/
createOne: async function(_id, bodys) {
try {
const {
endTime,
payment,
_metadata
} = bodys;
delete bodys._metadata;
delete bodys.endTime;
delete bodys.payment;
//
const DATAS = await service.openstack.blcokStorage.volume.create(_id, {
...bodys,
... {
"name": `volume-${Math.random().toString(36).slice(2, 8)}`
}
});
const {
id: _openstack_id
} = DATAS.data.volume;
!!DATAS.data.volume && await ctx.model.Schedule.Openstack.Volume.addOne({
_comp: _id,
_openstack_id,
type: 'volume',
active: 'creating',
_metadata
})
return !!DATAS.data.volume ? {
data: await this.create({
_comp: _id,
endTime,
payment,
_metadata,
...DATAS.data.volume
})
} : {
'code': 422,
'message': '创建失败'
};
} catch (error) {
return error
}
},
// 获取全部
getAll: async function(queryMix) {
const {
querys,
select,
pages,
sort,
dates
} = queryMix;
const MODELS = this.find({
'status': {
'$ne': 0
},
...querys,
// ...(!!pages && !!pages.marker && { '_id': { "$lt": pages.marker } }),
}, {
...select,
})
// .limit(!!pages ? pages.limit : 10)
.sort({
// '_id': -1,
'id': 1,
...sort
});
return {
data: {
result: await MODELS,
totalCount: await MODELS.count()
}
};
},
// 查看单个
getOne: async function(_comp, id, isAdmin) {
const MODELS = this.findOne({...isAdmin ? {
id
} : {
_comp,
id
}
});
try {
const RESULT = await MODELS;
return {
data: RESULT
}
} catch (error) {
return error
}
},
// 绑定到磁盘
/**
* @name 绑定到磁盘
* @param {*} _comp 用户UUID
* @param {*} id 磁盘ID
* @param {*} type 'bind'|'unbind' 绑定方式
*/
bindToECS: async function({
_comp,
id,
type = "bind"
} = {}, bodys) {
const MODEL = this.findOneAndUpdate({
_comp,
id
}, {
'attachments': type === 'bind' ? [bodys] : ,
'status': type === 'bind' ? 'in-use' : 'available'
});
try {
const datas = await MODEL;
} catch (error) {
return error
}
}
}
return mongoose.model('openstack_block_storage_volume', OSblockStorageVolumeSchema)
}
service
'use strict';
const ServerIndex = require('../index')
//
class VolumeService extends ServerIndex {
constructor(ctx) {
super(ctx);
this.adminprojectID = this.config.openstack.projectID.default;
this.actions = ':8776/v3';
};
// 获取磁盘列表
async list(querys, _ucid, project_id, isAdmin) {
try {
const detail = Object.keys(querys).includes('detail');
delete querys.detail;
let SORT = {}
if (querys.sort_key) {
SORT = { 'sort': `${querys.sort_key}:${querys.sort_dir}` };
delete querys.sort_key;
delete querys.sort_dir;
}
const datas = await this.OSAJax(`${this.actions}/${!!isAdmin ? this.adminprojectID : project_id}/volumes${detail ? '/detail' : ''}`, {
...!isAdmin && { _ucid },
body: {
...querys,
...querys.sort_key && SORT,
...isAdmin && { 'all_tenants': true },
},
});
return {
data: {
result: datas.volumes.map(e => {
!!detail && e.attachments.length && (e.attachment = e.attachments[0].server_id)
delete e.links;
delete e.attachments;
return e
}).filter(e => e.status != 'deleting'),
totalCount: datas.volumes.length
}
};
} catch (error) {
return error
}
}
// 获取磁盘详情
async show(id, _ucid, _description) {
try {
const datas = await this.OSAJax(`${this.actions}/${await this.getProject(_ucid)}/volumes/${id}`, {
..._ucid !== 'admin' && { _ucid }
});
return _ucid === 'admin' ? { ...datas.volume, _description } : { data: datas.volume };
} catch (error) {
return error
}
}
// 创建磁盘
async create(_ucid, bodys, order = "false") {
const DATAS = this.OSAJax(`${this.actions}/${await this.getProject(_ucid)}/volumes`, {
body: {
'volume': {
... {
"availability_zone": null,
"source_volid": null,
"description": null,
"multiattach": false,
"snapshot_id": null,
"backup_id": null,
"imageRef": null,
"metadata": {},
"consistencygroup_id": null
},
...bodys
}
},
method: 'POST',
_ucid
});
try {
const datas = await DATAS
return !!order ? { data: datas } : datas
} catch (error) {
return error
}

}
// 删除磁盘
async destroy(_ucid, project_id, volume_id) {
try {
const datas = await this.OSAJax(`${this.actions}/${project_id}/volumes/${volume_id}`, {
_ucid,
method: 'DELETE',
full: true
});
return {
...datas.status === 404 && {
message: `${volume_id}不存在或已删除`
}
}
} catch (error) {

}
}
// 磁盘操作
// {'os-extend':'磁盘扩容','revert':'镜像回滚'}
async action(_ucid, project_id, volume_id, type, bodys) {
const isIn = (e) => ['os-extend', 'revert'].includes(e);
if (!isIn(type)) return { code: 422 };
const actionMAP = (e) => {
const OBJ = {};
OBJ[e] = !bodys ? null : bodys;
return OBJ;
}
try {
const DATAS = await this.OSAJax(`${this.actions}/${project_id}/volumes/${volume_id}/action`, {
body: actionMAP(type),
method: 'POST',
_ucid, full: true
});
return {
code: DATAS.status
}
} catch (error) {
return error
}
}
async update(_ucid, project_id, volume_id, bodys) {
try {
const DATAS = await this.OSAJax(`${this.actions}/${project_id}/volumes/${volume_id}`, {
_ucid,
bodys: {
...bodys
},
method: 'PUT'
});
return {
data: DATAS
}
} catch (error) {
return error
}
}
}
module.exports = VolumeService;

schedule
const Subscription = require('egg').Subscription;

class getVolumeStatus extends Subscription {
constructor(ctx) {
super(ctx);
this.SERVICE = ctx.service.openstack.blcokStorage.volume;
this.MODELS = ctx.model.Schedule.Openstack.Volume;
this.volumeMODEL = ctx.model.Openstack.BlcokStorage.Volumes;
}
static get schedule() {
return {
interval: '10s',
type: 'worker',
};
}
async subscribe() {
const ctx = this.ctx;
const lists = await this.MODELS.getALL();
if (lists.length) {
const datas = await this.checkItem(lists);
}
}
async checkItem(result) {
// 调取记录
const checkFN = result.map(e => {
// 记录尝试次数
this.MODELS.retry(e._openstack_id)
return this.SERVICE.show(e._openstack_id, 'admin', e);
})
let DATAS = await Promise.all(checkFN);
// 检查ACTION
if (!!DATAS.length) {
const endOrder = DATAS.map(e => {
const { _comp, _openstack_id: id } = e._description;
delete e._description
delete e.links
if (e.status === 'available' || e.status === 'in-use') {
return this.volumeMODEL.findOneAndUpdate({ id }, { ...e });
}
})
DATAS = await Promise.all(endOrder);
};
// 清除已完成任务
if (DATAS.length) {
const clearSchedule = DATAS.map(e => {
if (!!e) {
const { id: _openstack_id } = e;
return this.MODELS.deleteOne({ '_openstack_id': _openstack_id })
}
})
DATAS = await Promise.all(clearSchedule);
}
}
}

module.exports = getVolumeStatus;

schedule model
module.exports = app => {
const mongoose = app.mongoose;
const Schema = mongoose.Schema;
const ctx = app.createAnonymousContext();
const scheduleOSVolumeSchema = new Schema({
// 类型
type: {
type: String,
},
// 尝试次数
retry: {
type: Number,
dafault: 0
},
// 是否创建成功
status: {
type: Boolean,
default: false
},
// 操作方式 [BUILD:创建,DELETE:删除]
active: {
type: String,
},
// 数据ID
_openstack_id: {
type: String,
index: true
},
// 公司ID
_comp: {
type: String
},
// 其他配置参数
/**
* ecs:Number
*/
_description: {

},
// 其他配置元信息
_metadata: {

}
}, {
timestamps: {
createdAt: 'created',
updatedAt: 'updated'
}
});
scheduleOSVolumeSchema.statics = {
getALL: async function (limit = 50) {
const MODEL = this.find().limit(limit);
try {
return await MODEL;
} catch (error) {
return error
}
},
addOne: async function (bodys) {
const MODEL = this.create({ ...bodys });
try {
const result = await MODEL;
return { code: !!result ? 201 : 404 }
} catch (error) {
return error
}
},
// destroyOne: async function (id) {
// const MODEL = this.deleteOne(id);
// try {
// const result = await MODEL
// return result
// } catch (error) {
// return error
// }
// },
retry: async function (_openstack_id) {
const MODEL = this.findOneAndUpdate({ _openstack_id }, {
'$inc': { retry: 1 }
});
try {
await MODEL
} catch (error) {
return error
}
}
}
return mongoose.model('schedule_openstack_volume', scheduleOSVolumeSchema);
}

0 个评论

要回复文章请先登录注册