Преглед на файлове

完成去除启动作业的轻重构

James Iter преди 8 години
родител
ревизия
a00ff67856

+ 0 - 145
api/boot_job.py

@@ -1,145 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-
-from flask import Blueprint
-from flask import request, g
-import jimit as ji
-import json
-
-from api.base import Base
-from models import Rules
-from models import Utils
-from models.boot_job import BootJob, OperateRule
-
-
-__author__ = 'James Iter'
-__date__ = '2017/6/19'
-__contact__ = 'james.iter.cn@gmail.com'
-__copyright__ = '(c) 2017 by James Iter.'
-
-
-blueprint = Blueprint(
-    'api_boot_job',
-    __name__,
-    url_prefix='/api/boot_job'
-)
-
-blueprints = Blueprint(
-    'api_boot_jobs',
-    __name__,
-    url_prefix='/api/boot_jobs'
-)
-
-
-boot_job_base = Base(the_class=BootJob, the_blueprint=blueprint, the_blueprints=blueprints)
-
-
-@Utils.dumps2response
-def r_create():
-
-    boot_job = BootJob()
-
-    args_rules = [
-        Rules.NAME.value,
-        Rules.USE_FOR.value,
-        Rules.REMARK.value
-    ]
-
-    boot_job.name = request.json.get('name')
-    boot_job.use_for = request.json.get('use_for')
-    boot_job.remark = request.json.get('remark')
-
-    try:
-        ji.Check.previewing(args_rules, boot_job.__dict__)
-
-        ret = dict()
-        ret['state'] = ji.Common.exchange_state(20000)
-
-        if boot_job.exist_by('name'):
-            ret['state'] = ji.Common.exchange_state(40901)
-            ret['state']['sub']['zh-cn'] = ''.join([ret['state']['sub']['zh-cn'], ': ', boot_job.name])
-            return ret
-
-        boot_job.create()
-        boot_job.get_by('name')
-        ret['data'] = boot_job.__dict__
-        return ret
-    except ji.PreviewingError, e:
-        return json.loads(e.message)
-
-
-@Utils.dumps2response
-def r_update(_id):
-
-    boot_job = BootJob()
-
-    args_rules = [
-        Rules.ID.value
-    ]
-
-    if 'name' in request.json:
-        args_rules.append(
-            Rules.NAME.value,
-        )
-
-    if 'use_for' in request.json:
-        args_rules.append(
-            Rules.USE_FOR.value,
-        )
-
-    if 'remark' in request.json:
-        args_rules.append(
-            Rules.REMARK.value,
-        )
-
-    if args_rules.__len__() < 2:
-        ret = dict()
-        ret['state'] = ji.Common.exchange_state(20000)
-        return ret
-
-    request.json['id'] = _id
-
-    try:
-        ji.Check.previewing(args_rules, request.json)
-        boot_job.id = request.json.get('id')
-        boot_job.get()
-        boot_job.name = request.json.get('name', boot_job.name)
-        boot_job.use_for = request.json.get('use_for', boot_job.use_for)
-        boot_job.remark = request.json.get('remark', boot_job.remark)
-
-        boot_job.update()
-        g.config = None
-
-        boot_job.get()
-
-        ret = dict()
-        ret['state'] = ji.Common.exchange_state(20000)
-        ret['data'] = boot_job.__dict__
-        return ret
-    except ji.PreviewingError, e:
-        return json.loads(e.message)
-
-
-@Utils.dumps2response
-def r_delete(ids):
-    operate_rule_base = Base(the_class=OperateRule)
-    operate_rule_base.delete(ids=ids, ids_rule=Rules.IDS.value, by_field='boot_job_id')
-
-    return boot_job_base.delete(ids=ids, ids_rule=Rules.IDS.value, by_field='id')
-
-
-@Utils.dumps2response
-def r_get(ids):
-    return boot_job_base.get(ids=ids, ids_rule=Rules.IDS.value, by_field='id')
-
-
-@Utils.dumps2response
-def r_get_by_filter():
-    return boot_job_base.get_by_filter()
-
-
-@Utils.dumps2response
-def r_content_search():
-    return boot_job_base.content_search()
-

+ 32 - 179
api/guest.py

@@ -11,7 +11,6 @@ import jimit as ji
 
 from api.base import Base
 from models import DiskState, Host
-from models import OperateRule
 from models.initialize import app, dev_table
 from models import Database as db
 from models import Config
@@ -19,7 +18,9 @@ from models import Disk
 from models import Rules
 from models import Utils
 from models import Guest
-from models import OSTemplate
+from models import OSTemplateImage
+from models import OSTemplateProfile
+from models import OSTemplateInitializeOperate
 from models import GuestXML
 from models import status
 
@@ -52,7 +53,7 @@ def r_create():
     args_rules = [
         Rules.CPU.value,
         Rules.MEMORY.value,
-        Rules.OS_TEMPLATE_ID.value,
+        Rules.OS_TEMPLATE_IMAGE_ID.value,
         Rules.QUANTITY.value,
         Rules.REMARK.value,
         Rules.PASSWORD.value,
@@ -74,20 +75,23 @@ def r_create():
         config.id = 1
         config.get()
 
-        os_template = OSTemplate()
-        os_template.id = request.json.get('os_template_id')
-        if not os_template.exist():
+        os_template_image = OSTemplateImage()
+        os_template_profile = OSTemplateProfile()
+
+        os_template_image.id = request.json.get('os_template_image_id')
+        if not os_template_image.exist():
             ret['state'] = ji.Common.exchange_state(40450)
-            ret['state']['sub']['zh-cn'] = ''.join([ret['state']['sub']['zh-cn'], ': ', os_template.id.__str__()])
+            ret['state']['sub']['zh-cn'] = ''.join([ret['state']['sub']['zh-cn'], ': ', os_template_image.id.__str__()])
             return ret
 
-        os_template.get()
-        # 重置密码的 boot job id 固定为 1
-        boot_jobs_id = [1, os_template.boot_job_id]
+        os_template_image.get()
+        os_template_profile.id = os_template_image.os_template_profile_id
+        os_template_profile.get()
 
-        boot_jobs, boot_jobs_count = OperateRule.get_by_filter(
-            filter_str='boot_job_id:in:' +
-                       ','.join(['{0}'.format(boot_job_id) for boot_job_id in boot_jobs_id]).__str__())
+        os_template_initialize_operates, os_template_initialize_operates_count = \
+            OSTemplateInitializeOperate.get_by_filter(
+                filter_str='os_template_initialize_operate_set_id:eq:' +
+                           os_template_profile.os_template_initialize_operate_set_id.__str__())
 
         if db.r.scard(app.config['ip_available_set']) < 1:
             ret['state'] = ji.Common.exchange_state(50350)
@@ -119,7 +123,7 @@ def r_create():
             guest.cpu = request.json.get('cpu')
             # 虚拟机内存单位,模板生成方法中已置其为GiB
             guest.memory = request.json.get('memory')
-            guest.os_template_id = request.json.get('os_template_id')
+            guest.os_template_image_id = request.json.get('os_template_image_id')
             guest.label = ji.Common.generate_random_code(length=8)
             guest.remark = request.json.get('remark', '')
 
@@ -151,7 +155,7 @@ def r_create():
             disk.quota(config=config)
             disk.create()
 
-            guest_xml = GuestXML(guest=guest, disk=disk, config=config, os_type=os_template.os_type)
+            guest_xml = GuestXML(guest=guest, disk=disk, config=config, os_type=os_template_profile.os_type)
             guest.xml = guest_xml.get_domain()
 
             # 在可用计算节点中平均分配任务
@@ -165,9 +169,9 @@ def r_create():
             guest.create()
 
             # 替换占位符为有效内容
-            _boot_jobs = copy.deepcopy(boot_jobs)
-            for k, v in enumerate(_boot_jobs):
-                _boot_jobs[k]['content'] = v['content'].replace('{IP}', guest.ip).\
+            _os_template_initialize_operates = copy.deepcopy(os_template_initialize_operates)
+            for k, v in enumerate(_os_template_initialize_operates):
+                _os_template_initialize_operates[k]['content'] = v['content'].replace('{IP}', guest.ip).\
                     replace('{HOSTNAME}', guest.label). \
                     replace('{PASSWORD}', guest.password). \
                     replace('{NETMASK}', config.netmask).\
@@ -175,7 +179,7 @@ def r_create():
                     replace('{DNS1}', config.dns1).\
                     replace('{DNS2}', config.dns2)
 
-                _boot_jobs[k]['command'] = v['command'].replace('{IP}', guest.ip). \
+                _os_template_initialize_operates[k]['command'] = v['command'].replace('{IP}', guest.ip). \
                     replace('{HOSTNAME}', guest.label). \
                     replace('{PASSWORD}', guest.password). \
                     replace('{NETMASK}', config.netmask). \
@@ -191,14 +195,12 @@ def r_create():
                 'dfs_volume': config.dfs_volume,
                 'node_id': guest.node_id,
                 'name': guest.label,
-                'template_path': os_template.path,
-                'os_type': os_template.os_type,
-                'disk': disk.__dict__,
-                # disk 将被废弃,由 disks 代替,暂时保留它的目的,是为了保持与 JimV-N 的兼容性
+                'template_path': os_template_image.path,
+                'os_type': os_template_profile.os_type,
                 'disks': [disk.__dict__],
                 'xml': guest_xml.get_domain(),
-                'boot_jobs': _boot_jobs,
-                'passback_parameters': {'boot_jobs_id': boot_jobs_id}
+                'os_template_initialize_operates': _os_template_initialize_operates,
+                'passback_parameters': {}
             }
 
             Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))
@@ -379,40 +381,14 @@ def r_boot(uuids):
             guest.uuid = uuid
             guest.get_by('uuid')
 
-            _, boot_jobs_id = guest.get_boot_jobs()
-
-            boot_jobs = list()
-
-            if boot_jobs_id.__len__() > 0:
-                boot_jobs, count = OperateRule.get_by_filter(filter_str='boot_job_id:in:' + ','.join(boot_jobs_id))
-
-            # 替换占位符为有效内容
-            for k, v in enumerate(boot_jobs):
-                boot_jobs[k]['content'] = v['content'].replace('{IP}', guest.ip). \
-                    replace('{HOSTNAME}', guest.label). \
-                    replace('{PASSWORD}', guest.password). \
-                    replace('{NETMASK}', config.netmask). \
-                    replace('{GATEWAY}', config.gateway). \
-                    replace('{DNS1}', config.dns1). \
-                    replace('{DNS2}', config.dns2)
-
-                boot_jobs[k]['command'] = v['command'].replace('{IP}', guest.ip). \
-                    replace('{HOSTNAME}', guest.label). \
-                    replace('{PASSWORD}', guest.password). \
-                    replace('{NETMASK}', config.netmask). \
-                    replace('{GATEWAY}', config.gateway). \
-                    replace('{DNS1}', config.dns1). \
-                    replace('{DNS2}', config.dns2)
-
             disks, _ = Disk.get_by_filter(filter_str=':'.join(['guest_uuid', 'eq', guest.uuid]))
 
             message = {
                 '_object': 'guest',
                 'action': 'boot',
                 'uuid': uuid,
-                'boot_jobs': boot_jobs,
                 'node_id': guest.node_id,
-                'passback_parameters': {'boot_jobs_id': boot_jobs_id},
+                'passback_parameters': {},
                 'disks': disks
             }
 
@@ -757,7 +733,7 @@ def r_distribute_count():
     ret['state'] = ji.Common.exchange_state(20000)
 
     ret['data'] = {
-        'os_template_id': dict(),
+        'os_template_image_id': dict(),
         'status': dict(),
         'node_id': dict(),
         'cpu_memory': dict(),
@@ -767,8 +743,8 @@ def r_distribute_count():
     }
 
     for guest in rows:
-        if guest['os_template_id'] not in ret['data']['os_template_id']:
-            ret['data']['os_template_id'][guest['os_template_id']] = 0
+        if guest['os_template_image_id'] not in ret['data']['os_template_image_id']:
+            ret['data']['os_template_image_id'][guest['os_template_image_id']] = 0
 
         if guest['status'] not in ret['data']['status']:
             ret['data']['status'][guest['status']] = 0
@@ -780,7 +756,7 @@ def r_distribute_count():
         if cpu_memory not in ret['data']['cpu_memory']:
             ret['data']['cpu_memory'][cpu_memory] = 0
 
-        ret['data']['os_template_id'][guest['os_template_id']] += 1
+        ret['data']['os_template_image_id'][guest['os_template_image_id']] += 1
         ret['data']['status'][guest['status']] += 1
         ret['data']['node_id'][guest['node_id']] += 1
         ret['data']['cpu_memory'][cpu_memory] += 1
@@ -829,127 +805,6 @@ def r_update(uuid):
         return json.loads(e.message)
 
 
-@Utils.dumps2response
-def r_add_boot_jobs(uuids, boot_jobs_id):
-
-    args_rules = [
-        Rules.UUIDS.value,
-        Rules.BOOT_JOBS_ID.value
-    ]
-
-    try:
-        ji.Check.previewing(args_rules, {'uuids': uuids, 'boot_jobs_id': boot_jobs_id})
-
-        guest = Guest()
-        for uuid in uuids.split(','):
-            guest.uuid = uuid
-            guest.get_by('uuid')
-
-        for uuid in uuids.split(','):
-            guest.uuid = uuid
-            guest.add_boot_jobs(boot_jobs_id=boot_jobs_id.split(','))
-
-        ret = dict()
-        ret['state'] = ji.Common.exchange_state(20000)
-
-        if uuids.split(',').__len__() > 1:
-            ret['data'] = dict()
-            for uuid in uuids.split(','):
-                guest.uuid = uuid
-                boot_jobs = dict()
-                boot_jobs['ttl'], boot_jobs['boot_jobs'] = guest.get_boot_jobs()
-                ret['data'][uuid] = boot_jobs
-
-        else:
-            guest.uuid = uuids
-            ret['data'] = dict()
-            ret['data']['ttl'], ret['data']['boot_jobs'] = guest.get_boot_jobs()
-
-        return ret
-
-    except ji.PreviewingError, e:
-        return json.loads(e.message)
-
-
-@Utils.dumps2response
-def r_get_boot_jobs(uuids):
-
-    args_rules = [
-        Rules.UUIDS.value
-    ]
-
-    try:
-        ji.Check.previewing(args_rules, {'uuids': uuids})
-
-        guest = Guest()
-
-        for uuid in uuids.split(','):
-            guest.uuid = uuid
-            guest.get_by('uuid')
-
-        ret = dict()
-        ret['state'] = ji.Common.exchange_state(20000)
-
-        if uuids.split(',').__len__() > 1:
-            ret['data'] = dict()
-            for uuid in uuids.split(','):
-                guest.uuid = uuid
-                boot_jobs = dict()
-                boot_jobs['ttl'], boot_jobs['boot_jobs'] = guest.get_boot_jobs()
-                ret['data'][uuid] = boot_jobs
-
-        else:
-            guest.uuid = uuids
-            ret['data'] = dict()
-            ret['data']['ttl'], ret['data']['boot_jobs'] = guest.get_boot_jobs()
-
-        return ret
-
-    except ji.PreviewingError, e:
-        return json.loads(e.message)
-
-
-@Utils.dumps2response
-def r_delete_boot_jobs(uuids, boot_jobs_id):
-
-    args_rules = [
-        Rules.UUIDS.value,
-        Rules.BOOT_JOBS_ID.value
-    ]
-
-    try:
-        ji.Check.previewing(args_rules, {'uuids': uuids, 'boot_jobs_id': boot_jobs_id})
-
-        guest = Guest()
-        # 检测所指定的 UUDIs 实例都存在
-        for uuid in uuids.split(','):
-            guest.uuid = uuid
-            guest.get_by('uuid')
-
-        for uuid in uuids.split(','):
-            guest.uuid = uuid
-            guest.delete_boot_jobs(boot_jobs_id=boot_jobs_id.split(','))
-
-        ret = dict()
-        ret['state'] = ji.Common.exchange_state(20000)
-        return ret
-
-    except ji.PreviewingError, e:
-        return json.loads(e.message)
-
-
-@Utils.dumps2response
-def r_get_uuids_of_all_had_boot_job():
-    guest = Guest()
-    try:
-        ret = dict()
-        ret['state'] = ji.Common.exchange_state(20000)
-        ret['data'] = guest.get_uuids_of_all_had_boot_job()
-        return ret
-    except ji.PreviewingError, e:
-        return json.loads(e.message)
-
-
 @Utils.dumps2response
 def r_reset_password(uuids, password):
 
@@ -974,8 +829,6 @@ def r_reset_password(uuids, password):
             guest.password = password
             guest.update()
 
-            guest.add_boot_jobs(boot_jobs_id=['1'])
-
         ret = dict()
         ret['state'] = ji.Common.exchange_state(20000)
         return ret

+ 0 - 200
api/operate_rule.py

@@ -1,200 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-
-from flask import Blueprint
-from flask import request
-import jimit as ji
-import json
-
-from api.base import Base
-from models import Rules
-from models import Utils
-from models.boot_job import OperateRule, BootJob
-from models.status import OperateRuleKind
-
-__author__ = 'James Iter'
-__date__ = '2017/6/19'
-__contact__ = 'james.iter.cn@gmail.com'
-__copyright__ = '(c) 2017 by James Iter.'
-
-
-blueprint = Blueprint(
-    'api_operate_rule',
-    __name__,
-    url_prefix='/api/operate_rule'
-)
-
-blueprints = Blueprint(
-    'api_operate_rules',
-    __name__,
-    url_prefix='/api/operate_rules'
-)
-
-
-operate_rule_base = Base(the_class=OperateRule, the_blueprint=blueprint, the_blueprints=blueprints)
-
-
-@Utils.dumps2response
-def r_create():
-
-    boot_job = BootJob()
-    operate_rule = OperateRule()
-
-    args_rules = [
-        Rules.BOOT_JOB_ID_EXT.value,
-        Rules.OPERATE_RULE_KIND.value,
-        Rules.OPERATE_RULE_SEQUENCE.value
-    ]
-
-    operate_rule.boot_job_id = request.json.get('boot_job_id')
-    operate_rule.kind = request.json.get('kind')
-    operate_rule.path = request.json.get('path', '')
-    operate_rule.sequence = request.json.get('sequence', 0)
-    operate_rule.content = request.json.get('content', '')
-    operate_rule.command = request.json.get('command', '')
-
-    if operate_rule.kind == OperateRuleKind.cmd.value:
-        args_rules.append(
-            Rules.OPERATE_RULE_COMMAND.value
-        )
-
-    else:
-        args_rules.extend([
-            Rules.OPERATE_RULE_PATH.value,
-            Rules.OPERATE_RULE_CONTENT.value
-        ])
-
-    try:
-        ji.Check.previewing(args_rules, operate_rule.__dict__)
-
-        ret = dict()
-        ret['state'] = ji.Common.exchange_state(20000)
-
-        boot_job.id = operate_rule.boot_job_id
-        if not boot_job.exist():
-            ret['state'] = ji.Common.exchange_state(40401)
-            ret['state']['sub']['zh-cn'] = ''.join([ret['state']['sub']['zh-cn'], ': ', boot_job.id.__str__()])
-            return ret
-
-        if operate_rule.kind == OperateRuleKind.cmd.value:
-            data, total = operate_rule.get_by_filter(
-                filter_str=':'.join(['boot_job_id', 'eq', operate_rule.boot_job_id.__str__()]) + ';' +
-                           ':'.join(['command', 'eq', operate_rule.command]))
-
-        else:
-            data, total = operate_rule.get_by_filter(
-                filter_str=':'.join(['boot_job_id', 'eq', operate_rule.boot_job_id.__str__()]) + ';' +
-                           ':'.join(['path', 'eq', operate_rule.path]))
-
-        if data.__len__() > 0:
-            ret['state'] = ji.Common.exchange_state(40901)
-
-            if operate_rule.kind == OperateRuleKind.cmd.value:
-                ret['state']['sub']['zh-cn'] = ''.join([ret['state']['sub']['zh-cn'],
-                                                        ', command: ', operate_rule.command,
-                                                        ', boot_job_id: ', operate_rule.boot_job_id.__str__()])
-
-            else:
-                ret['state']['sub']['zh-cn'] = ''.join([ret['state']['sub']['zh-cn'], ', path: ', operate_rule.path,
-                                                        ', boot_job_id: ', operate_rule.boot_job_id.__str__()])
-            return ret
-
-        operate_rule.create()
-        data, total = operate_rule.get_by_filter(
-            filter_str=':'.join(['boot_job_id', 'eq', operate_rule.boot_job_id.__str__()]) + ';' +
-                       ':'.join(['path', 'eq', operate_rule.path]))
-        ret['data'] = data[0]
-        return ret
-
-    except ji.PreviewingError, e:
-        return json.loads(e.message)
-
-
-@Utils.dumps2response
-def r_update(_id):
-
-    operate_rule = OperateRule()
-
-    args_rules = [
-        Rules.ID.value
-    ]
-
-    if 'boot_job_id' in request.json:
-        args_rules.append(
-            Rules.BOOT_JOB_ID_EXT.value,
-        )
-
-    if 'kind' in request.json:
-        args_rules.append(
-            Rules.OPERATE_RULE_KIND.value,
-        )
-
-    if 'sequence' in request.json:
-        args_rules.append(
-            Rules.OPERATE_RULE_SEQUENCE.value,
-        )
-
-    if 'path' in request.json:
-        args_rules.append(
-            Rules.OPERATE_RULE_PATH.value,
-        )
-
-    if 'content' in request.json:
-        args_rules.append(
-            Rules.OPERATE_RULE_CONTENT.value,
-        )
-
-    if 'command' in request.json:
-        args_rules.append(
-            Rules.OPERATE_RULE_COMMAND.value,
-        )
-
-    if args_rules.__len__() < 2:
-        ret = dict()
-        ret['state'] = ji.Common.exchange_state(20000)
-        return ret
-
-    request.json['id'] = _id
-
-    try:
-        ji.Check.previewing(args_rules, request.json)
-        operate_rule.id = request.json.get('id')
-        operate_rule.get()
-        operate_rule.boot_job_id = request.json.get('boot_job_id', operate_rule.boot_job_id)
-        operate_rule.kind = request.json.get('kind', operate_rule.kind)
-        operate_rule.sequence = request.json.get('sequence', operate_rule.sequence)
-        operate_rule.path = request.json.get('path', operate_rule.path)
-        operate_rule.content = request.json.get('content', operate_rule.content)
-        operate_rule.command = request.json.get('command', operate_rule.command)
-
-        operate_rule.update()
-        operate_rule.get()
-
-        ret = dict()
-        ret['state'] = ji.Common.exchange_state(20000)
-        ret['data'] = operate_rule.__dict__
-        return ret
-    except ji.PreviewingError, e:
-        return json.loads(e.message)
-
-
-@Utils.dumps2response
-def r_delete(ids):
-    return operate_rule_base.delete(ids=ids, ids_rule=Rules.IDS.value, by_field='id')
-
-
-@Utils.dumps2response
-def r_get(ids):
-    return operate_rule_base.get(ids=ids, ids_rule=Rules.IDS.value, by_field='id')
-
-
-@Utils.dumps2response
-def r_get_by_filter():
-    return operate_rule_base.get_by_filter()
-
-
-@Utils.dumps2response
-def r_content_search():
-    return operate_rule_base.content_search()
-

+ 0 - 165
api/os_template.py

@@ -1,165 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-
-from flask import Blueprint
-from flask import request
-import jimit as ji
-import json
-
-from api.base import Base
-from models import OSTemplate
-from models import Rules
-from models import Utils
-
-
-__author__ = 'James Iter'
-__date__ = '2017/3/30'
-__contact__ = 'james.iter.cn@gmail.com'
-__copyright__ = '(c) 2017 by James Iter.'
-
-
-blueprint = Blueprint(
-    'api_os_template',
-    __name__,
-    url_prefix='/api/os_template'
-)
-
-blueprints = Blueprint(
-    'api_os_templates',
-    __name__,
-    url_prefix='/api/os_templates'
-)
-
-
-os_template_base = Base(the_class=OSTemplate, the_blueprint=blueprint, the_blueprints=blueprints)
-
-
-@Utils.dumps2response
-def r_create():
-
-    os_template = OSTemplate()
-
-    args_rules = [
-        Rules.LABEL.value,
-        Rules.PATH.value,
-        Rules.ACTIVE.value,
-        Rules.OS_TYPE.value,
-        Rules.ICON.value,
-        Rules.BOOT_JOB_ID_EXT.value
-    ]
-
-    os_template.label = request.json.get('label')
-    os_template.path = request.json.get('path')
-    os_template.os_type = request.json.get('os_type')
-    os_template.active = request.json.get('active')
-    os_template.icon = request.json.get('icon')
-    os_template.boot_job_id = request.json.get('boot_job_id', 0)
-
-    try:
-        ji.Check.previewing(args_rules, os_template.__dict__)
-
-        ret = dict()
-        ret['state'] = ji.Common.exchange_state(20000)
-
-        if os_template.exist_by('path'):
-            ret['state'] = ji.Common.exchange_state(40901)
-            ret['state']['sub']['zh-cn'] = ''.join([ret['state']['sub']['zh-cn'], ': ', os_template.path])
-            return ret
-
-        os_template.create()
-        os_template.get_by('path')
-        ret['data'] = os_template.__dict__
-        return ret
-    except ji.PreviewingError, e:
-        return json.loads(e.message)
-
-
-@Utils.dumps2response
-def r_update(_id):
-
-    os_template = OSTemplate()
-
-    args_rules = [
-        Rules.ID.value
-    ]
-
-    if 'label' in request.json:
-        args_rules.append(
-            Rules.LABEL.value,
-        )
-
-    if 'path' in request.json:
-        args_rules.append(
-            Rules.PATH.value,
-        )
-
-    if 'os_type' in request.json:
-        args_rules.append(
-            Rules.OS_TYPE.value,
-        )
-
-    if 'active' in request.json:
-        args_rules.append(
-            Rules.ACTIVE.value,
-        )
-
-    if 'icon' in request.json:
-        args_rules.append(
-            Rules.ICON.value,
-        )
-
-    if 'boot_job_id' in request.json:
-        args_rules.append(
-            Rules.BOOT_JOB_ID_EXT.value,
-        )
-
-    if args_rules.__len__() < 2:
-        ret = dict()
-        ret['state'] = ji.Common.exchange_state(20000)
-        return ret
-
-    request.json['id'] = _id
-
-    try:
-        ji.Check.previewing(args_rules, request.json)
-        os_template.id = request.json.get('id')
-
-        os_template.get()
-        os_template.label = request.json.get('label', os_template.label)
-        os_template.path = request.json.get('path', os_template.path)
-        os_template.os_type = request.json.get('os_type', os_template.os_type)
-        os_template.active = request.json.get('active', os_template.active)
-        os_template.icon = request.json.get('icon', os_template.icon)
-        os_template.boot_job_id = request.json.get('boot_job_id', os_template.boot_job_id)
-
-        os_template.update()
-        os_template.get()
-
-        ret = dict()
-        ret['state'] = ji.Common.exchange_state(20000)
-        ret['data'] = os_template.__dict__
-        return ret
-    except ji.PreviewingError, e:
-        return json.loads(e.message)
-
-
-@Utils.dumps2response
-def r_delete(ids):
-    return os_template_base.delete(ids=ids, ids_rule=Rules.IDS.value, by_field='id')
-
-
-@Utils.dumps2response
-def r_get(ids):
-    return os_template_base.get(ids=ids, ids_rule=Rules.IDS.value, by_field='id')
-
-
-@Utils.dumps2response
-def r_get_by_filter():
-    return os_template_base.get_by_filter()
-
-
-@Utils.dumps2response
-def r_content_search():
-    return os_template_base.content_search()
-

+ 0 - 26
api_route_table.py

@@ -7,9 +7,6 @@ from api import config
 from api import user
 from api import guest
 from api import disk
-from api import boot_job
-from api import operate_rule
-from api import os_template
 from api import os_template_image
 from api import os_template_profile
 from api import os_template_initialize_operate_set
@@ -41,29 +38,6 @@ add_rule_api(user.blueprint, '/_reset_password/<token>', api_func='user.r_reset_
 add_rule_api(user.blueprint, '/_send_reset_password_email/<login_name>', api_func='user.r_send_reset_password_email',
              methods=['PUT'])
 
-# 系统启动作业配置操作
-add_rule_api(boot_job.blueprint, '', api_func='boot_job.r_create', methods=['POST'])
-add_rule_api(boot_job.blueprint, '/<_id>', api_func='boot_job.r_update', methods=['PATCH'])
-add_rule_api(boot_job.blueprints, '/<ids>', api_func='boot_job.r_delete', methods=['DELETE'])
-add_rule_api(boot_job.blueprints, '/<ids>', api_func='boot_job.r_get', methods=['GET'])
-add_rule_api(boot_job.blueprints, '', api_func='boot_job.r_get_by_filter', methods=['GET'])
-add_rule_api(boot_job.blueprints, '/_search', api_func='boot_job.r_content_search', methods=['GET'])
-
-add_rule_api(operate_rule.blueprint, '', api_func='operate_rule.r_create', methods=['POST'])
-add_rule_api(operate_rule.blueprint, '/<_id>', api_func='operate_rule.r_update', methods=['PATCH'])
-add_rule_api(operate_rule.blueprints, '/<ids>', api_func='operate_rule.r_delete', methods=['DELETE'])
-add_rule_api(operate_rule.blueprints, '/<ids>', api_func='operate_rule.r_get', methods=['GET'])
-add_rule_api(operate_rule.blueprints, '', api_func='operate_rule.r_get_by_filter', methods=['GET'])
-add_rule_api(operate_rule.blueprints, '/_search', api_func='operate_rule.r_content_search', methods=['GET'])
-
-# 系统模板操作
-add_rule_api(os_template.blueprint, '', api_func='os_template.r_create', methods=['POST'])
-add_rule_api(os_template.blueprint, '/<_id>', api_func='os_template.r_update', methods=['PATCH'])
-add_rule_api(os_template.blueprints, '/<ids>', api_func='os_template.r_delete', methods=['DELETE'])
-add_rule_api(os_template.blueprints, '/<ids>', api_func='os_template.r_get', methods=['GET'])
-add_rule_api(os_template.blueprints, '', api_func='os_template.r_get_by_filter', methods=['GET'])
-add_rule_api(os_template.blueprints, '/_search', api_func='os_template.r_content_search', methods=['GET'])
-
 # 系统模板镜像操作
 add_rule_api(os_template_image.blueprint, '', api_func='os_template_image.r_create', methods=['POST'])
 add_rule_api(os_template_image.blueprint, '/<_id>', api_func='os_template_image.r_update', methods=['PATCH'])

+ 0 - 28
main.py

@@ -26,13 +26,6 @@ from models import Config
 from models import User
 from api.user import blueprint as user_blueprint
 
-from api.boot_job import blueprint as boot_job_blueprint
-from api.boot_job import blueprints as boot_job_blueprints
-from api.operate_rule import blueprint as operate_rule_blueprint
-from api.operate_rule import blueprints as operate_rule_blueprints
-from api.os_template import blueprint as os_template_blueprint
-from api.os_template import blueprints as os_template_blueprints
-
 from api.os_template_image import blueprint as os_template_image_blueprint
 from api.os_template_image import blueprints as os_template_image_blueprints
 from api.os_template_initialize_operate_set import blueprint as os_template_initialize_operate_set_blueprint
@@ -68,13 +61,6 @@ from views.log import blueprints as view_log_blueprints
 from views.os_template_image import blueprint as view_os_template_image_blueprint
 from views.os_template_image import blueprints as view_os_template_image_blueprints
 
-from views.os_template import blueprint as view_os_template_blueprint
-from views.os_template import blueprints as view_os_template_blueprints
-from views.boot_job import blueprint as view_boot_job_blueprint
-from views.boot_job import blueprints as view_boot_job_blueprints
-from views.operate_rule import blueprint as view_operate_rule_blueprint
-from views.operate_rule import blueprints as view_operate_rule_blueprints
-
 from views.host import blueprint as view_host_blueprint
 from views.host import blueprints as view_host_blueprints
 
@@ -226,13 +212,6 @@ try:
 
     app.register_blueprint(user_blueprint)
 
-    app.register_blueprint(boot_job_blueprint)
-    app.register_blueprint(boot_job_blueprints)
-    app.register_blueprint(operate_rule_blueprint)
-    app.register_blueprint(operate_rule_blueprints)
-    app.register_blueprint(os_template_blueprint)
-    app.register_blueprint(os_template_blueprints)
-
     app.register_blueprint(os_template_image_blueprint)
     app.register_blueprint(os_template_image_blueprints)
     app.register_blueprint(os_template_initialize_operate_set_blueprint)
@@ -267,13 +246,6 @@ try:
     app.register_blueprint(view_os_template_image_blueprint)
     app.register_blueprint(view_os_template_image_blueprints)
 
-    app.register_blueprint(view_os_template_blueprint)
-    app.register_blueprint(view_os_template_blueprints)
-    app.register_blueprint(view_boot_job_blueprint)
-    app.register_blueprint(view_boot_job_blueprints)
-    app.register_blueprint(view_operate_rule_blueprint)
-    app.register_blueprint(view_operate_rule_blueprints)
-
     app.register_blueprint(view_host_blueprint)
     app.register_blueprint(view_host_blueprints)
 

+ 1 - 1
misc/init.sql

@@ -32,7 +32,7 @@ CREATE TABLE IF NOT EXISTS guest(
     label VARCHAR(64) NOT NULL UNIQUE,
     password VARCHAR(255) NOT NULL,
     remark VARCHAR(255) NOT NULL DEFAULT '',
-    os_template_id BIGINT UNSIGNED NOT NULL,
+    os_template_image_id BIGINT UNSIGNED NOT NULL,
     create_time BIGINT UNSIGNED NOT NULL,
     -- 运行时的状态用 status;
     status TINYINT UNSIGNED NOT NULL DEFAULT 0,

+ 3 - 12
models/__init__.py

@@ -39,14 +39,6 @@ from guest import (
     Guest, Disk
 )
 
-from boot_job import (
-    BootJob, OperateRule
-)
-
-from os_template import (
-    OSTemplate
-)
-
 from os_template_image import (
     OSTemplateImage
 )
@@ -68,8 +60,7 @@ from status import (
     GuestState,
     ResponseState,
     DiskState,
-    LogLevel,
-    OSType
+    LogLevel
 )
 
 from guest_xml import (
@@ -108,8 +99,8 @@ __copyright__ = '(c) 2017 by James Iter.'
 
 
 __all__ = [
-    'Rules', 'Utils', 'Init', 'Database', 'FilterFieldType', 'Filter', 'EmitKind', 'GuestState', 'DiskState', 'OSType',
-    'LogLevel', 'ORM', 'User', 'Config', 'Guest', 'Disk', 'BootJob', 'OperateRule', 'OSTemplate', 'GuestXML', 'Log',
+    'Rules', 'Utils', 'Init', 'Database', 'FilterFieldType', 'Filter', 'EmitKind', 'GuestState', 'DiskState',
+    'LogLevel', 'ORM', 'User', 'Config', 'Guest', 'Disk', 'GuestXML', 'Log',
     'OSTemplateImage', 'OSTemplateProfile', 'OSTemplateInitializeOperateSet', 'OSTemplateInitializeOperate',
     'EventProcessor', 'ResponseState', 'GuestCPUMemory', 'GuestTraffic', 'GuestDiskIO', 'HostCPUMemory', 'HostTraffic',
     'HostDiskUsageIO', 'Host'

+ 0 - 79
models/boot_job.py

@@ -1,79 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-
-from models import FilterFieldType
-from models import ORM
-
-
-__author__ = 'James Iter'
-__date__ = '2017/6/19'
-__contact__ = 'james.iter.cn@gmail.com'
-__copyright__ = '(c) 2017 by James Iter.'
-
-
-class BootJob(ORM):
-
-    _table_name = 'boot_job'
-    _primary_key = 'id'
-
-    def __init__(self):
-        super(BootJob, self).__init__()
-        self.id = 0
-        self.name = None
-        self.use_for = None
-        self.remark = ''
-
-    @staticmethod
-    def get_filter_keywords():
-        return {
-            'id': FilterFieldType.INT.value,
-            'name': FilterFieldType.STR.value,
-            'use_for': FilterFieldType.INT.value,
-            'remark': FilterFieldType.STR.value
-        }
-
-    @staticmethod
-    def get_allow_update_keywords():
-        return ['use_for']
-
-    @staticmethod
-    def get_allow_content_search_keywords():
-        return ['name', 'remark']
-
-
-class OperateRule(ORM):
-
-    _table_name = 'operate_rule'
-    _primary_key = 'id'
-
-    def __init__(self):
-        super(OperateRule, self).__init__()
-        self.id = 0
-        self.boot_job_id = None
-        self.kind = ''
-        self.sequence = 0
-        self.command = ''
-        self.path = ''
-        self.content = ''
-
-    @staticmethod
-    def get_filter_keywords():
-        return {
-            'id': FilterFieldType.INT.value,
-            'boot_job_id': FilterFieldType.INT.value,
-            'sequence': FilterFieldType.INT.value,
-            'command': FilterFieldType.STR.value,
-            'path': FilterFieldType.STR.value,
-            'content': FilterFieldType.STR.value,
-        }
-
-    @staticmethod
-    def get_allow_update_keywords():
-        return ['boot_job_id', 'sequence']
-
-    @staticmethod
-    def get_allow_content_search_keywords():
-        return ['command', 'path', 'content']
-
-

+ 1 - 5
models/event_processor.py

@@ -205,13 +205,9 @@ class EventProcessor(object):
                     cls.disk.update()
 
             elif action == 'boot':
-                boot_jobs_id = cls.message['message']['passback_parameters']['boot_jobs_id']
 
                 if state == ResponseState.success.value:
-                    cls.guest.uuid = uuid
-
-                    if boot_jobs_id.__len__() > 0:
-                        cls.guest.delete_boot_jobs(boot_jobs_id=boot_jobs_id)
+                    pass
 
         elif _object == 'disk':
             if action == 'create':

+ 1 - 37
models/guest.py

@@ -7,8 +7,6 @@ import jimit as ji
 from filter import FilterFieldType
 from orm import ORM
 from status import GuestState, DiskState
-from database import Database as db
-from initialize import app
 
 
 __author__ = 'James Iter'
@@ -29,7 +27,7 @@ class Guest(ORM):
         self.label = None
         self.password = None
         self.remark = ''
-        self.os_template_id = None
+        self.os_template_image_id = None
         self.create_time = ji.Common.tus()
         self.status = GuestState.no_state.value
         self.progress = 0
@@ -63,40 +61,6 @@ class Guest(ORM):
     def get_allow_content_search_keywords():
         return ['label', 'remark', 'node_id', 'ip']
 
-    def get_boot_jobs_key(self):
-        return ':'.join([app.config['guest_boot_jobs'], self.uuid])
-
-    def add_boot_jobs(self, boot_jobs_id):
-        if not isinstance(boot_jobs_id, list):
-            raise ValueError('The boot_jobs_id must be a list.')
-
-        key = self.get_boot_jobs_key()
-        db.r.sadd(key, *boot_jobs_id)
-        db.r.expire(key, app.config['guest_boot_jobs_wait_time'])
-
-    def get_boot_jobs(self):
-        return db.r.ttl(self.get_boot_jobs_key()), list(db.r.smembers(self.get_boot_jobs_key()))
-
-    def delete_boot_jobs(self, boot_jobs_id):
-        if not isinstance(boot_jobs_id, list):
-            raise ValueError('The boot_jobs_id must be a list.')
-
-        key = self.get_boot_jobs_key()
-        db.r.srem(key, *boot_jobs_id)
-
-        # 如果集合下还有值,则更新启动作业有效时间
-        if db.r.exists(key):
-            db.r.expire(key, app.config['guest_boot_jobs_wait_time'])
-
-    @staticmethod
-    def get_uuids_of_all_had_boot_job():
-        boot_job_keys = db.r.keys(pattern=app.config['guest_boot_jobs'] + '*')
-        uuids = list()
-        for boot_job_key in boot_job_keys:
-            uuids.append(boot_job_key.split(':')[-1])
-
-        return uuids
-
 
 class Disk(ORM):
 

+ 2 - 2
models/guest_xml.py

@@ -2,7 +2,7 @@
 # -*- coding: utf-8 -*-
 
 
-from models import Config, Disk, OSTemplate, OSType
+from models import Config, Disk
 from models import Guest
 from models import status
 
@@ -59,7 +59,7 @@ class GuestXML(object):
         # Windows Guest 设为 localtime,非 Windows Guest 都设为 utc
         offset = 'utc'
 
-        if self.os_type == OSType.windows.value:
+        if str(self.os_type).lower().find('windows') >= 0:
             offset = 'localtime'
 
         return """<clock offset='{0}'/>""".format(offset)

+ 0 - 48
models/os_template.py

@@ -1,48 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-
-from models import FilterFieldType
-from models import ORM
-
-
-__author__ = 'James Iter'
-__date__ = '2017/3/23'
-__contact__ = 'james.iter.cn@gmail.com'
-__copyright__ = '(c) 2017 by James Iter.'
-
-
-class OSTemplate(ORM):
-
-    _table_name = 'os_template'
-    _primary_key = 'id'
-
-    def __init__(self):
-        super(OSTemplate, self).__init__()
-        self.id = 0
-        self.label = None
-        self.path = None
-        self.os_type = None
-        self.active = None
-        self.icon = None
-        self.boot_job_id = None
-
-    @staticmethod
-    def get_filter_keywords():
-        return {
-            'id': FilterFieldType.INT.value,
-            'label': FilterFieldType.STR.value,
-            'path': FilterFieldType.STR.value,
-            'os_type': FilterFieldType.INT.value,
-            'active': FilterFieldType.BOOL.value,
-            'boot_job_id': FilterFieldType.INT.value
-        }
-
-    @staticmethod
-    def get_allow_update_keywords():
-        return ['active', 'boot_job_id', 'os_type']
-
-    @staticmethod
-    def get_allow_content_search_keywords():
-        return ['label', 'path', 'os_type']
-

+ 1 - 1
models/rules.py

@@ -64,7 +64,7 @@ class Rules(Enum):
     UUIDS = (REG_UUIDS, 'uuids')
     CPU = (int, 'cpu')
     MEMORY = (int, 'memory')
-    OS_TEMPLATE_ID = (int, 'os_template_id')
+    OS_TEMPLATE_IMAGE_ID = (int, 'os_template_image_id')
     QUANTITY = (int, 'quantity')
     NAME = (basestring, 'name')
     PATH = (basestring, 'path')

+ 0 - 15
models/status.py

@@ -85,12 +85,6 @@ class BootJobUseFor(IntEnum):
     user = 2
 
 
-class OperateRuleKind(IntEnum):
-    cmd = 0
-    write_file = 1
-    append_file = 2
-
-
 class OSTemplateInitializeOperateKind(IntEnum):
     cmd = 0
     write_file = 1
@@ -108,12 +102,3 @@ class HostCollectionPerformanceDataKind(IntEnum):
     traffic = 1
     disk_usage_io = 2
 
-
-class OSType(IntEnum):
-    linux = 0
-    windows = 1
-    bsd = 2
-    aix = 3
-    hp_unix = 4
-    unknown = 255
-

+ 0 - 523
templates/boot_jobs_show.html

@@ -1,523 +0,0 @@
-{% extends "layout.html" %}
-{% block head %}
-    {{ super() }}
-    <style type="text/css">
-
-        @media (min-width: 768px) {
-            .form-horizontal .control-label {
-                text-align: left;
-            }
-        }
-
-        label>span {
-            color: deepskyblue;
-        }
-
-        .table {
-            font-size: 12px;
-            border-width: 1px;
-            line-height: 20px;
-        }
-        .table > thead > tr > th,
-        .table > tfoot > tr > th {
-            color: #999999;
-            font-weight: normal;
-            border-bottom: 0 solid #e1e6eb;
-            background-color: #F5F6FA;
-        }
-        
-        .table > tbody > tr > td {
-            color: #424547;
-        }
-
-        .table-bordered > tbody > tr {
-            padding-top: 10px;
-            padding-bottom: 10px;
-        }
-
-        .table-bordered > thead > tr > th,
-        .table-bordered > tbody > tr > th,
-        .table-bordered > tfoot > tr > th,
-        .table-bordered > thead > tr > td,
-        .table-bordered > tbody > tr > td,
-        .table-bordered > tfoot > tr > td {
-            border-style: solid;
-            border-width: 1px 0 0 0;
-        }
-
-        .btn,
-        .form-control,
-        .modal-content {
-            border-radius: 0 !important;
-        }
-    </style>
-
-{% endblock head %}
-{% block content %}
-
-<script type="text/javascript">
-    var page = 1;
-    var page_size = 10;
-    var keyword = '';
-    var resource_path = window.location.pathname;
-    var cur_url = resource_path;
-
-    $(document).ready(function() {
-        page_size = $('#page_size').val();
-        cur_url = resource_path + '?page=' + page + '&page_size=' + page_size;
-
-        var last_ready = null;
-        $('#content_search').keydown(function() {
-            if (last_ready !== null) {
-                clearTimeout(last_ready);
-            }
-            last_ready = setTimeout(function () {
-                keyword = $('#content_search').val();
-                cur_url = resource_path + '?page=' + page + '&page_size=' + page_size;
-                if (keyword.length > 0) {
-                    cur_url = resource_path + '?page=' + page + '&page_size=' + page_size + '&keyword=' + keyword;
-                }
-                window.location.href=cur_url;
-            }, 1000);
-        });
-
-        $('#page_size').change(function () {
-            keyword = $('#content_search').val();
-            page_size = $('#page_size').val();
-            cur_url = resource_path + '?page=' + page + '&page_size=' + page_size;
-            if (keyword.length > 0) {
-                cur_url = resource_path + '?page=' + page + '&page_size=' + page_size + '&keyword=' + keyword;
-            }
-            window.location.href=cur_url;
-        });
-
-        $('#edit_name_modal').on('show.bs.modal', function (me) {
-            $('#boot_job_id').val($(me.relatedTarget).parent().parent().prev().text());
-            $('#edit_name').val($(me.relatedTarget).prev().text());
-        });
-
-        $('#edit_remark_modal').on('show.bs.modal', function (me) {
-            $('#boot_job_id').val($(me.relatedTarget).parent().parent().prev().prev().prev().text());
-            $('#edit_remark').val($(me.relatedTarget).prev().text());
-        });
-
-        $('#add_boot_job_modal').on('show.bs.modal', function (me) {
-        });
-
-        $('#update_use_for_modal').on('show.bs.modal', function (me) {
-            $('#boot_job_id').val($(me.relatedTarget).parent().prev().prev().text());
-            $('#update_use_for_instance_desc').text($(me.relatedTarget).parent().prev().text());
-        });
-
-        $('#add_boot_job_form').formValidation({
-            framework: 'bootstrap4',
-            icon: {
-                valid: 'fa fa-check',
-                invalid: 'fa fa-times',
-                validating: 'fa fa-refresh'
-            },
-            // Since the Bootstrap Button hides the radio and checkbox
-            // We exclude the disabled elements only
-            excluded: ':disabled',
-            locale: 'zh_CN',
-            fields: {
-                name: {
-                    validators: {
-                        notEmpty: {
-                        },
-                        stringLength: {
-                            min: 1,
-                            max: 255
-                        }
-                    }
-                },
-                use_for: {
-                    validators: {
-                        notEmpty: {
-                        }
-                    }
-                },
-                remark: {
-                    validators: {
-                        stringLength: {
-                            max: 255
-                        }
-                    }
-                }
-            }
-        })
-        .on('success.field.fv', function(e, data) {
-            if (data.fv.getInvalidFields().length > 0) {    // There is invalid field
-                data.fv.disableSubmitButtons(true);
-            }
-        });
-    });
-
-    function refresh() {
-        keyword = $('#content_search').val();
-        page = $('#pagination li.active a').text();
-        page_size = $('#page_size').val();
-        cur_url = resource_path + '?page=' + page + '&page_size=' + page_size;
-        if (keyword.length > 0) {
-            cur_url = resource_path + '?page=' + page + '&page_size=' + page_size + '&keyword=' + keyword;
-        }
-        window.location.href=cur_url;
-    }
-
-    function row_onmouseover(me) {
-        $(me).find(".edit_name_trigger, .edit_remark_trigger").css('display','inline-flex');
-    }
-
-    function row_onmouseout(me) {
-        $(me).find(".edit_name_trigger, .edit_remark_trigger").css('display','none');
-    }
-
-    function name_update(me) {
-        var boot_job_id = $('#boot_job_id').val();
-        var name = $('#edit_name').val();
-        $('#edit_name_modal').modal('hide');
-        $.ajax({
-            url : '/api/boot_job/' + boot_job_id,
-            type : 'PATCH',
-            contentType: "application/json; charset=utf-8",
-            data : JSON.stringify({
-                name: name
-            }),
-            error : function() {
-                alter_danger('启动作业名称更新失败!');
-            },
-            success : function() {
-                alter_success('启动作业名称更新成功!');
-                refresh()
-            }
-        });
-    }
-
-    function remark_update(me) {
-        var boot_job_id = $('#boot_job_id').val();
-        var remark = $('#edit_remark').val();
-        $('#edit_remark_modal').modal('hide');
-        $.ajax({
-            url : '/api/boot_job/' + boot_job_id,
-            type : 'PATCH',
-            contentType: "application/json; charset=utf-8",
-            data : JSON.stringify({
-                remark: remark
-            }),
-            error : function() {
-                alter_danger('启动作业备注更新失败!');
-            },
-            success : function() {
-                alter_success('启动作业备注路径更新成功!');
-                refresh()
-            }
-        });
-    }
-
-    function update_use_for(id) {
-        $.ajax({
-            url : '/api/boot_job/' + id,
-            type : 'PATCH',
-            contentType: "application/json; charset=utf-8",
-            data : JSON.stringify({
-                use_for: parseInt($('#update_use_for').val())
-            }),
-            error : function() {
-                alter_danger('变更启动作业作用域指令发送失败!');
-            },
-            success : function() {
-                alter_success('变更启动作业作用域指令发送成功!');
-                refresh();
-            }
-        });
-    }
-
-    function remove(id) {
-        $.ajax({
-            url : '/api/boot_jobs/' + id,
-            type : 'DELETE',
-            contentType: "application/json; charset=utf-8",
-            error : function() {
-                alter_danger('启动作业删除指令发送失败!');
-            },
-            success : function() {
-                alter_success('启动作业删除指令发送成功!');
-                refresh();
-            }
-        });
-    }
-
-    function delete_at(me) {
-        var id = $('#boot_job_id').val();
-        remove(id);
-        $('#delete_modal').modal('hide');
-    }
-
-    function update_use_for_at(me) {
-        var id = $('#boot_job_id').val();
-        update_use_for(id);
-        $('#update_use_for_modal').modal('hide');
-    }
-</script>
-<div class="panel">
-    <div class="panel-body">
-        <h3 class="title-hero" style="font-size: 24px;">
-            启动作业
-        </h3>
-        <div>
-            <div id="datatable-row-highlight_wrapper" class="dataTables_wrapper form-inline">
-                <div class="row" style="padding: 10px 10px 10px 0; width: 100%;">
-                    <div class="col-sm-12" style="padding-right: 0;">
-                        <div id="datatable-row-highlight_filter" class="dataTables_filter" style="display: inline-block;">
-                            <input id="content_search" type="search" class="form-control" placeholder="模糊搜索..." value="{%- if keyword -%} {{ keyword }} {%- endif -%}" style="margin-left: 0; border-radius: 0;">
-                        </div>
-                        <div class="pull-right">
-                            <button class="btn btn-default" onclick="refresh()" style="border-radius: 0;"><span class="glyph-icon icon-elusive-arrows-cw"></span></button>
-                            <a class="btn btn-info" href="javascript:;" data-toggle="modal" data-target="#add_boot_job_modal" style="border-radius: 0; padding-left: 40px; padding-right: 40px;">添加启动作业</a>
-                        </div>
-                    </div>
-                </div>
-                <table id="boot_job_list" class="table table-bordered table-hover" cellspacing="0" width="100%" role="grid"
-                       style="width: 100%; margin-bottom: 0; border-bottom-width: 0;">
-                <thead>
-                <tr role="row">
-                    <th style="display: none;">ID</th>
-                    <th width="280px;">名称</th>
-                    <th>作用域</th>
-                    <th width="600px;">备注</th>
-                    <th>操作</th>
-                </tr>
-                </thead>
-                <tbody>
-                {% for item in boot_jobs_ret.data %}
-                <tr role="row" class="odd" onmouseover="row_onmouseover(this);" onmouseout="row_onmouseout(this);">
-                    <td style="display: none;">{{ item.id }}</td>
-                    <td>
-                        <div>
-                            <a href="/operate_rules?boot_job_id={{ item.id }}"><p style="display: inline-block;">{{ item.name }}</p></a>
-                            <a href="javascript:;" class="edit_name_trigger" data-toggle="modal" data-target="#edit_name_modal" style="display: none; float: right;">
-                                <span class="glyph-icon icon-elusive-pencil" style="width: 20px; height: 20px; margin-left: 10px; border-radius: 0; border: 1px solid rgb(220, 233, 255); background-color: #ffffff;"></span>
-                            </a>
-                        </div>
-                    </td>
-                    <td>
-                        <a href="javascript:;" data-toggle="modal" data-target="#update_use_for_modal">
-                            {% if item.use_for == 0 %}
-                                <span style="color: #8800FF;">模板</span>
-                            {% elif item.use_for == 1 %}
-                                <span style="color: #3333FF;">系统</span>
-                            {% elif item.use_for == 2 %}
-                                <span style="color: #00BB00;">用户</span>
-                            {% else %}
-                                <span style="color: #990000;">未知</span>
-                            {% endif %}
-                        </a>
-                    </td>
-                    <td>
-                        <div>
-                            <p style="display: inline-block;">{{ item.remark }}</p>
-                            <a href="javascript:;" class="edit_remark_trigger" data-toggle="modal" data-target="#edit_remark_modal" style="display: none; float: right;">
-                                <span class="glyph-icon icon-elusive-pencil" style="width: 20px; height: 20px; margin-left: 10px; border-radius: 0; border: 1px solid rgb(220, 233, 255); background-color: #ffffff;"></span>
-                            </a>
-                        </div>
-                    </td>
-                    <td>
-                        <div class="dropdown inline-block">
-                            <a href="javascript:;" class="dropdown-toggle" data-toggle="dropdown">
-                                更多
-                            </a>
-                            <ul class="dropdown-menu">
-                                <li>
-                                    <a href="javascript:;" data-toggle="modal" data-target="#delete_modal"
-                                       onclick="$('#boot_job_id').val($(this).parent().parent().parent().parent().parent().children()[0].textContent);
-                                       $('#delete_instance_desc').text($(this).parent().parent().parent().parent().parent().children()[1].textContent)">
-                                        删除
-                                    </a>
-                                </li>
-                            </ul>
-                        </div>
-                    </td>
-                </tr>
-                {% endfor %}
-                </tbody>
-                </table>
-
-                <table class="table table-bordered" style="border-top-width: 0; z-index: 99; position: sticky; bottom: 0;">
-                    <tfoot>
-                    <tr style="height: 70px;">
-                        <th>
-                            <div class="row">
-                                <div class="col-sm-6">
-                                </div>
-                                <div class="col-sm-3" style="font-size: 12px; padding-top: 5px; text-align: right;">
-                                    共有{{ boot_jobs_ret.paging.total }}条,每页显示:
-                                    <select id="page_size" name="datatable-row-highlight_length" title="page_size" class="form-control" style="height: 22px; vertical-align: baseline;">
-                                        <option value="10" {% if page_size == 10 %} selected {% endif %}>10</option>
-                                        <option value="20" {% if page_size == 20 %} selected {% endif %}>20</option>
-                                        <option value="50" {% if page_size == 50  %} selected {% endif %}>50</option>
-                                    </select>&nbsp;&nbsp;条
-                                </div>
-                                <div class="col-sm-3" style="text-align: left;">
-                                    <div class="dataTables_paginate paging_bootstrap" id="datatable-row-highlight_paginate">
-                                        <ul id="pagination" class="pagination">
-                                            <li class="{% if page == 1 %} disabled {% endif %}">
-                                                <a href="{{ resource_path }}?page={{ page - 1 }}&page_size={{ page_size }}{% if keyword %}&keyword={{ keyword }}{% endif %}{% if order_by %}&order_by={{ order_by }}{% endif %}{% if order %}&order={{ order }}{% endif %}">«</a>
-                                            </li>
-                                            {% for item in pages %}
-                                            <li class="{% if item == page %} active {% endif %}">
-                                                <a href="{{ resource_path }}?page={{ item }}&page_size={{ page_size }}{% if keyword %}&keyword={{ keyword }}{% endif %}{% if order_by %}&order_by={{ order_by }}{% endif %}{% if order %}&order={{ order }}{% endif %}">{{ item }}</a>
-                                            </li>
-                                            {% endfor %}
-                                            <li class="{% if page == last_page %} disabled {% endif %}">
-                                                <a href="{{ resource_path }}?page={{ page + 1 }}&page_size={{ page_size }}{% if keyword %}&keyword={{ keyword }}{% endif %}{% if order_by %}&order_by={{ order_by }}{% endif %}{% if order %}&order={{ order }}{% endif %}">»</a>
-                                            </li>
-                                        </ul>
-                                    </div>
-                                </div>
-                            </div>
-                        </th>
-                    </tr>
-                    </tfoot>
-                </table>
-            </div>
-        </div>
-    </div>
-</div>
-
-<input id="boot_job_id" title="启动作业 ID" class="form-control" name="boot_job_id" hidden>
-
-<div class="modal" id="edit_name_modal" tabindex="-1" role="dialog" style="margin-top: 100px;">
-    <div class="modal-dialog modal-sm">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h4 class="modal-title">编辑启动作业名称:</h4>
-            </div>
-            <div class="modal-body">
-                <input id="edit_name" title="启动作业名称" class="form-control" name="boot_job_name">
-            </div>
-            <div class="modal-footer">
-                <button type="button" class="btn btn-sm btn-primary" onclick="name_update();">确定</button>
-                <button type="button" class="btn btn-sm btn-default" data-dismiss="modal">取消</button>
-            </div>
-        </div>
-    </div>
-</div>
-
-<div class="modal" id="edit_remark_modal" tabindex="-1" role="dialog" style="margin-top: 100px;">
-    <div class="modal-dialog">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h4 class="modal-title">编辑模板路径:</h4>
-            </div>
-            <div class="modal-body">
-                <input id="edit_remark" title="启动作业备注" class="form-control" name="boot_job_remark">
-            </div>
-            <div class="modal-footer">
-                <button type="button" class="btn btn-sm btn-primary" onclick="remark_update();">确定</button>
-                <button type="button" class="btn btn-sm btn-default" data-dismiss="modal">取消</button>
-            </div>
-        </div>
-    </div>
-</div>
-
-<div class="modal" id="delete_modal" tabindex="-1" role="dialog" style="margin-top: 100px;">
-    <div class="modal-dialog">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h4 class="modal-title">删除启动作业:</h4>
-            </div>
-            <div class="modal-body">
-                <p>你确定要删除该启动作业吗?</p>
-                <h3 style="color: orangered;" id="delete_instance_desc"></h3>
-            </div>
-            <div class="modal-footer">
-                <button type="button" class="btn btn-sm btn-primary" onclick="delete_at();">确定</button>
-                <button type="button" class="btn btn-sm btn-default" data-dismiss="modal">取消</button>
-            </div>
-        </div>
-    </div>
-</div>
-
-<div class="modal" id="add_boot_job_modal" tabindex="-1" role="dialog" style="margin-top: 100px;">
-    <div class="modal-dialog modal-lg">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h4 class="modal-title">添加启动作业:</h4>
-            </div>
-            <div class="modal-body" style="padding-top: 0; padding-bottom: 0;">
-                <div class="example-box-wrapper">
-                    <form id="add_boot_job_form" class="form-horizontal bordered-row" action="/boot_job" method="post">
-                        <div class="form-group">
-                            <div class="col-sm-2"></div>
-                            <label class="col-sm-2 control-label"><span class="glyph-icon icon-elusive-compass-circled"></span>&nbsp;&nbsp;启动作业名称</label>
-                            <div class="col-sm-6">
-                                <input id="name" name="name" type="text" title="启动作业名称" class="form-control">
-                            </div>
-                        </div>
-                        <div class="form-group">
-                            <div class="col-sm-2"></div>
-                            <label class="col-sm-2 control-label"><span class="glyph-icon icon-pie-chart"></span>&nbsp;&nbsp;作用域</label>
-                            <div class="col-sm-6">
-                                <select id="use_for" name="use_for" title="作用域" class="selectpicker">
-                                    <option value="2" data-subtext="通过重启系统,批量执行用户预定的操作" selected>用户</option>
-                                    <option value="0" data-subtext="从模板克隆的虚拟机,首次启动时初始化系统使用">模板</option>
-                                    <!--<option value="1" data-subtext="JimV系统平台内置的功能,如重置系统密码,更换IP等操作">系统</option>-->
-                                </select>
-                            </div>
-                        </div>
-                        <div class="form-group">
-                            <div class="col-sm-2"></div>
-                            <label class="col-sm-2 control-label"><span class="glyph-icon icon-newspaper-o"></span>&nbsp;&nbsp;备注</label>
-                            <div class="col-sm-6">
-                                <input id="remark" name="remark" type="text" title="备注" class="form-control">
-                            </div>
-                        </div>
-                        <div class="form-group">
-                            <div class="col-sm-4"></div>
-                            <div class="col-sm-6 pull-right">
-                                <button type="submit" class="btn btn-blue-alt" style="width: 180px; height: 40px; font-size: 16px;" disabled>创建</button>
-                                <button class="btn btn-default" style="width: 64px; height: 40px; font-size: 16px;" data-dismiss="modal">取消</button>
-                            </div>
-                        </div>
-                    </form>
-                </div>
-            </div>
-        </div>
-    </div>
-</div>
-
-<div class="modal" id="update_use_for_modal" tabindex="-1" role="dialog" style="margin-top: 100px;">
-    <div class="modal-dialog">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h4 class="modal-title">变更作用域:</h4>
-            </div>
-            <div class="modal-body" style="padding-top: 0;">
-                <form class="form-horizontal bordered-row">
-                    <div class="form-group">
-                        <div class="col-sm-1"></div>
-                        <label class="col-sm-3 control-label"><span class="glyph-icon icon-elusive-compass-circled"></span>&nbsp;&nbsp;启动作业名称</label>
-                        <div class="col-sm-8">
-                            <h3 style="color: orangered;" id="update_use_for_instance_desc"></h3>
-                        </div>
-                    </div>
-                    <div class="form-group">
-                        <div class="col-sm-1"></div>
-                        <label class="col-sm-3 control-label"><span class="glyph-icon icon-pie-chart"></span>&nbsp;&nbsp;作用域</label>
-                        <div class="col-sm-8">
-                            <select id="update_use_for" name="use_for" title="作用域" class="selectpicker">
-                                <option value="2" data-subtext="通过重启系统,批量执行用户预定的操作" selected>用户</option>
-                                <option value="0" data-subtext="从模板克隆的虚拟机,首次启动时初始化系统使用">模板</option>
-                            </select>
-                        </div>
-                    </div>
-                </form>
-            </div>
-            <div class="modal-footer">
-                <button type="button" class="btn btn-sm btn-primary" onclick="update_use_for_at();">确定</button>
-                <button type="button" class="btn btn-sm btn-default" data-dismiss="modal">取消</button>
-            </div>
-        </div>
-    </div>
-</div>
-{% endblock content %}

+ 0 - 307
templates/guest_boot_jobs.html

@@ -1,307 +0,0 @@
-{% extends "layout.html" %}
-{% block head %}
-    {{ super() }}
-    <style type="text/css">
-
-        @media (min-width: 768px) {
-            .form-horizontal .control-label {
-                text-align: left;
-            }
-        }
-
-        label>span {
-            color: deepskyblue;
-        }
-
-        .table {
-            font-size: 12px;
-            border-width: 1px;
-            line-height: 20px;
-        }
-        .table > thead > tr > th,
-        .table > tfoot > tr > th {
-            color: #999999;
-            font-weight: normal;
-            border-bottom: 0 solid #e1e6eb;
-            background-color: #F5F6FA;
-        }
-        
-        .table > tbody > tr > td {
-            color: #424547;
-        }
-
-        .table-bordered > tbody > tr {
-            padding-top: 10px;
-            padding-bottom: 10px;
-        }
-
-        .table-bordered > thead > tr > th,
-        .table-bordered > tbody > tr > th,
-        .table-bordered > tfoot > tr > th,
-        .table-bordered > thead > tr > td,
-        .table-bordered > tbody > tr > td,
-        .table-bordered > tfoot > tr > td {
-            border-style: solid;
-            border-width: 1px 0 0 0;
-        }
-
-        .btn,
-        .form-control,
-        .modal-content {
-            border-radius: 0 !important;
-        }
-    </style>
-
-{% endblock head %}
-{% block content %}
-
-<script type="text/javascript">
-    var resource_path = window.location.pathname;
-    var cur_url = resource_path;
-
-    $(document).ready(function() {
-        $('#add_boot_job_modal').on('show.bs.modal', function (me) {
-            refresh_boot_job_selectpicker($('#boot_job_selector'));
-        });
-        $('#add_guest_boot_job_form').formValidation({
-            framework: 'bootstrap4',
-            icon: {
-                valid: 'fa fa-check',
-                invalid: 'fa fa-times',
-                validating: 'fa fa-refresh'
-            },
-            // Since the Bootstrap Button hides the radio and checkbox
-            // We exclude the disabled elements only
-            excluded: ':disabled',
-            locale: 'zh_CN',
-            fields: {
-                boot_job_selector: {
-                    validators: {
-                        notEmpty: {}
-                    }
-                }
-            }
-        })
-        .on('success.field.fv', function(e, data) {
-            if (data.fv.getInvalidFields().length > 0) {    // There is invalid field
-                data.fv.disableSubmitButtons(true);
-            }
-        });
-    });
-
-    function refresh() {
-        cur_url = resource_path;
-        window.location.href=cur_url;
-    }
-
-    function refresh_boot_job_selectpicker(selectpicker) {
-        $.ajax({
-            url : '/api/boot_jobs?filter=use_for:eq:2',
-            type : 'GET',
-            contentType: "application/json; charset=utf-8",
-            dataType: 'json',
-            error : function() {
-            },
-            success : function(data, textStatus, xhr) {
-                selectpicker.empty();
-                $.each(data.data, function(k, v) {
-                    selectpicker.append(
-                        $('<option>', {value: v['id'], text: v['name'], 'data-subtext': v['remark']})
-                    );
-                });
-                selectpicker.selectpicker('refresh');
-            }
-        });
-    }
-
-    function add_boot_job(id) {
-        $.ajax({
-            url : '/api/guests/_boot_jobs/{{ uuid }}/' + id,
-            type : 'PUT',
-            contentType: "application/json; charset=utf-8",
-            error : function() {
-                alter_danger('启动作业添加指令发送失败!');
-            },
-            success : function() {
-                alter_success('启动作业添加指令发送成功!');
-                refresh();
-            }
-        });
-    }
-
-    function remove(id) {
-        $.ajax({
-            url : '/api/guests/_boot_jobs/{{ uuid }}/' + id,
-            type : 'DELETE',
-            contentType: "application/json; charset=utf-8",
-            error : function() {
-                alter_danger('启动作业删除指令发送失败!');
-            },
-            success : function() {
-                alter_success('启动作业删除指令发送成功!');
-                refresh();
-            }
-        });
-    }
-
-    function delete_at(me) {
-        var id = $('#boot_job_id').val();
-        remove(id);
-        $('#delete_modal').modal('hide');
-    }
-
-    function add_boot_job_at(me) {
-        var id = $('#boot_job_selector').val();
-        add_boot_job(id);
-        $('#add_boot_job_modal').modal('hide');
-    }
-</script>
-<div class="panel">
-    <div class="panel-body">
-        <h3 class="title-hero" style="font-size: 24px;">
-            启动作业(<a href="/guest/detail/{{ guest_ret.data.uuid }}" style="color: #0275d8;">{{ guest_ret.data.label }}/{{ guest_ret.data.remark }}</a>)
-        </h3>
-        <div>
-            <div id="datatable-row-highlight_wrapper" class="dataTables_wrapper form-inline">
-                <div class="row" style="padding: 10px 10px 10px 0; width: 100%;">
-                    <div class="col-sm-12" style="padding-right: 0;">
-                        <h4 style="color: orange; {% if guest_boot_jobs_ret.data.ttl < 1 %}display: none;{% endif %}">下列启动作业将在
-                            <span style="color: #b94a48; {% if guest_boot_jobs_ret.data.ttl // 60 == 0 %}display: none;{% endif %}">{{ guest_boot_jobs_ret.data.ttl // 60 }}分</span>
-                            <span style="color: #b94a48; {% if guest_boot_jobs_ret.data.ttl % 60 == 0 %}display: none;{% endif %}">{{ guest_boot_jobs_ret.data.ttl % 60 }}秒</span>
-                            <span>后失效</span>
-                        </h4>
-                        <div class="pull-right">
-                            <button class="btn btn-default" onclick="refresh()" style="border-radius: 0;"><span class="glyph-icon icon-elusive-arrows-cw"></span></button>
-                            <a class="btn btn-info" href="javascript:;" data-toggle="modal" data-target="#add_boot_job_modal" style="border-radius: 0; padding-left: 40px; padding-right: 40px;">添加启动作业</a>
-                        </div>
-                    </div>
-                </div>
-                <table id="boot_job_list" class="table table-bordered table-hover" cellspacing="0" width="100%" role="grid"
-                       style="width: 100%; margin-bottom: 0; border-bottom-width: 0;">
-                <thead>
-                <tr role="row">
-                    <th style="display: none;">ID</th>
-                    <th width="280px;">名称</th>
-                    <th>作用域</th>
-                    <th width="600px;">备注</th>
-                    <th>操作</th>
-                </tr>
-                </thead>
-                <tbody>
-                {% for item in guest_boot_jobs_ret.data.boot_jobs %}
-                <tr role="row" class="odd">
-                    <td style="display: none;">{{ item }}</td>
-                    <td>
-                        <div>
-                            <a href="/operate_rules?boot_job_id={{ item }}"><p style="display: inline-block;">{{ boot_jobs_mapping_by_id[item | int].name }}</p></a>
-                        </div>
-                    </td>
-                    <td>
-                        {% if boot_jobs_mapping_by_id[item | int].use_for == 0 %}
-                            <span style="color: #8800FF;">模板</span>
-                        {% elif boot_jobs_mapping_by_id[item | int].use_for == 1 %}
-                            <span style="color: #3333FF;">系统</span>
-                        {% elif boot_jobs_mapping_by_id[item | int].use_for == 2 %}
-                            <span style="color: #00BB00;">用户</span>
-                        {% else %}
-                            <span style="color: #990000;">未知</span>
-                        {% endif %}
-                    </td>
-                    <td>
-                        <div>
-                            <p style="display: inline-block;">{{ boot_jobs_mapping_by_id[item | int].remark }}</p>
-                        </div>
-                    </td>
-                    <td>
-                        <div class="dropdown inline-block">
-                            <a href="javascript:;" class="dropdown-toggle" data-toggle="dropdown">
-                                更多
-                            </a>
-                            <ul class="dropdown-menu">
-                                <li>
-                                    <a href="javascript:;" data-toggle="modal" data-target="#delete_modal"
-                                       onclick="$('#boot_job_id').val($(this).parent().parent().parent().parent().parent().children()[0].textContent);
-                                       $('#delete_instance_desc').text($(this).parent().parent().parent().parent().parent().children()[1].textContent)">
-                                        删除
-                                    </a>
-                                </li>
-                            </ul>
-                        </div>
-                    </td>
-                </tr>
-                {% endfor %}
-                </tbody>
-                </table>
-
-                <table class="table table-bordered" style="border-top-width: 0; z-index: 99; position: sticky; bottom: 0;">
-                    <tfoot>
-                    <tr style="height: 70px;">
-                        <th>
-                            <div class="row">
-                                <div class="col-sm-6">
-                                </div>
-                                <div class="col-sm-3" style="font-size: 12px; padding-top: 5px; text-align: right;">
-                                </div>
-                                <div class="col-sm-3" style="text-align: left;">
-                                </div>
-                            </div>
-                        </th>
-                    </tr>
-                    </tfoot>
-                </table>
-            </div>
-        </div>
-    </div>
-</div>
-
-<input id="boot_job_id" title="启动作业 ID" class="form-control" name="boot_job_id" hidden>
-
-<div class="modal" id="delete_modal" tabindex="-1" role="dialog" style="margin-top: 100px;">
-    <div class="modal-dialog">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h4 class="modal-title">删除启动作业:</h4>
-            </div>
-            <div class="modal-body">
-                <p>你确定要删除该启动作业吗?</p>
-                <h3 style="color: orangered;" id="delete_instance_desc"></h3>
-            </div>
-            <div class="modal-footer">
-                <button type="button" class="btn btn-sm btn-primary" onclick="delete_at();">确定</button>
-                <button type="button" class="btn btn-sm btn-default" data-dismiss="modal">取消</button>
-            </div>
-        </div>
-    </div>
-</div>
-
-<div class="modal" id="add_boot_job_modal" tabindex="-1" role="dialog" style="margin-top: 100px;">
-    <div class="modal-dialog modal-lg">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h4 class="modal-title">添加启动作业:</h4>
-            </div>
-            <div class="modal-body" style="padding-top: 0; padding-bottom: 0;">
-                <div class="example-box-wrapper">
-                    <form id="add_guest_boot_job_form" class="form-horizontal bordered-row">
-                        <div class="form-group">
-                            <div class="col-sm-2"></div>
-                            <label class="col-sm-2 control-label"><span class="glyph-icon icon-list-alt"></span>&nbsp;&nbsp;启动作业</label>
-                            <div class="col-sm-6">
-                                <select id="boot_job_selector" name="boot_job_selector" title="启动作业" class="selectpicker">
-                                </select>
-                            </div>
-                        </div>
-                        <div class="form-group">
-                            <div class="col-sm-4"></div>
-                            <div class="col-sm-6 pull-right">
-                                <button type="submit" class="btn btn-blue-alt" style="width: 180px; height: 40px; font-size: 16px;" onclick="add_boot_job_at();" disabled>添加</button>
-                                <button class="btn btn-default" style="width: 64px; height: 40px; font-size: 16px;" data-dismiss="modal">取消</button>
-                            </div>
-                        </div>
-                    </form>
-                </div>
-            </div>
-        </div>
-    </div>
-</div>
-{% endblock content %}

+ 50 - 5
templates/guest_create.html

@@ -60,7 +60,7 @@
                         }
                     }
                 },
-                os_template_id: {
+                os_template_image_id: {
                     validators: {
                         notEmpty: {
                         }
@@ -105,8 +105,56 @@
                 data.fv.disableSubmitButtons(true);
             }
         });
+
+        refresh_os_template_image_id_selectpicker($('#os_template_image_id'));
     });
 
+    function get_os_templates_profile_mapping_by_id() {
+        var os_templates_profile_mapping_by_id = {};
+
+        $.ajax({
+            url : '/api/os_templates_profile',
+            type : 'GET',
+            contentType: "application/json; charset=utf-8",
+            dataType: 'json',
+            async: false,
+            error : function() {
+            },
+            success : function(data, textStatus, xhr) {
+                $.each(data.data, function(k, v) {
+                    os_templates_profile_mapping_by_id[v['id']] = v;
+                });
+            }
+        });
+
+        return os_templates_profile_mapping_by_id;
+    }
+
+    function refresh_os_template_image_id_selectpicker(selectpicker) {
+        $.ajax({
+            url : '/api/os_templates_image',
+            type : 'GET',
+            contentType: "application/json; charset=utf-8",
+            dataType: 'json',
+            async: false,
+            error : function() {
+            },
+            success : function(data, textStatus, xhr) {
+                var os_templates_profile_mapping_by_id = get_os_templates_profile_mapping_by_id();
+                selectpicker.empty();
+                $.each(data.data, function(k, v) {
+                    if (v['logo'] === "") {
+                        v['logo'] = os_templates_profile_mapping_by_id[v['os_template_profile_id']].icon;
+                    }
+                    selectpicker.append(
+                        $('<option>', {value: v['id'], text: '\xa0\xa0' + v['label'], 'data-icon': v['logo']})
+                    );
+                });
+                selectpicker.selectpicker('refresh');
+            }
+        });
+    }
+
     $(function() { "use strict";
         $("#quantity").TouchSpin({
             max: 20,
@@ -201,10 +249,7 @@
                     <div class="form-group">
                         <label class="col-sm-2 control-label"><span class="glyph-icon icon-elusive-inbox-alt"></span>&nbsp;&nbsp;系统模板</label>
                         <div class="col-sm-6">
-                            <select id="os_template" name="os_template_id" title="请选择系统模板..." class="selectpicker" data-width="100%">
-                                {% for item in os_template_ret.data %}
-                                    <option value="{{ item.id }}" data-icon="{{ item.icon }}">&nbsp;&nbsp;{{ item.label }}</option>
-                                {% endfor %}
+                            <select id="os_template_image_id" name="os_template_image_id" title="请选择系统模板..." class="selectpicker" data-width="100%">
                             </select>
                         </div>
                     </div>

+ 7 - 2
templates/guest_detail.html

@@ -1107,9 +1107,14 @@
                             <td>
                                 <span class="guest-label">操作系统:&nbsp;&nbsp;&nbsp;&nbsp;</span>
                                 <span class="guest-desc">
-                                {{ os_template_ret.data.label }}
+                                {{ os_template_image_ret.data.label }}
                                 </span>&nbsp;&nbsp;&nbsp;&nbsp;
-                                <span class="{{ os_template_ret.data.icon }}"></span>
+
+                                {% if os_template_image_ret.data.logo == "" %}
+                                    <span class="{{ os_templates_profile_mapping_by_id[os_template_image_ret.data.os_template_profile_id].icon }}"></span>
+                                {% else %}
+                                    <span class="{{ os_template_image_ret.data.logo }}"></span>
+                                {% endif %}
                             </td>
                         </tr>
                         <tr>

+ 0 - 406
templates/guests_boot_jobs.html

@@ -1,406 +0,0 @@
-{% extends "layout.html" %}
-{% block head %}
-    {{ super() }}
-    <style type="text/css">
-        .table {
-            font-size: 12px;
-            border-width: 1px;
-            line-height: 20px;
-        }
-        .table > thead > tr > th,
-        .table > tfoot > tr > th {
-            color: #999999;
-            font-weight: normal;
-            border-bottom: 0 solid #e1e6eb;
-            background-color: #F5F6FA;
-        }
-        
-        .table > tbody > tr > td {
-            color: #424547;
-        }
-
-        .table-bordered > tbody > tr {
-            padding-top: 10px;
-            padding-bottom: 10px;
-        }
-
-        .table-bordered > thead > tr > th,
-        .table-bordered > tbody > tr > th,
-        .table-bordered > tfoot > tr > th,
-        .table-bordered > thead > tr > td,
-        .table-bordered > tbody > tr > td,
-        .table-bordered > tfoot > tr > td {
-            border-style: solid;
-            border-width: 1px 0 0 0;
-        }
-
-        .btn,
-        .form-control,
-        .modal-content {
-            border-radius: 0 !important;
-        }
-
-        .tr-selected td,
-        .tr-selected {
-            color: #000 !important;
-            background: #fafaff !important;
-        }
-    </style>
-
-{% endblock head %}
-{% block content %}
-
-<script type="text/javascript">
-    var page = 1;
-    var page_size = 10;
-    var keyword = '';
-    var resource_path = window.location.pathname;
-    var cur_url = resource_path;
-
-    $(document).ready(function() {
-        page_size = $('#page_size').val();
-        cur_url = resource_path + '?page=' + page + '&page_size=' + page_size;
-
-        var last_ready = null;
-        $('#content_search').keydown(function() {
-            if (last_ready !== null) {
-                clearTimeout(last_ready);
-            }
-            last_ready = setTimeout(function () {
-                keyword = $('#content_search').val();
-                cur_url = resource_path + '?page=' + page + '&page_size=' + page_size;
-                if (keyword.length > 0) {
-                    cur_url = resource_path + '?page=' + page + '&page_size=' + page_size + '&keyword=' + keyword;
-                }
-                window.location.href=cur_url;
-            }, 1000);
-        });
-
-        $('#page_size').change(function () {
-            keyword = $('#content_search').val();
-            page_size = $('#page_size').val();
-            cur_url = resource_path + '?page=' + page + '&page_size=' + page_size;
-            if (keyword.length > 0) {
-                cur_url = resource_path + '?page=' + page + '&page_size=' + page_size + '&keyword=' + keyword;
-            }
-            window.location.href=cur_url;
-        });
-
-        $("thead").on('click', ".all_selector", function() {
-            if ($("thead .all_selector").is(':checked')) {
-                $("tbody tr").find('td input[type="checkbox"]:eq(0)').prop('checked', true);
-                $(".all_selector").prop('checked', true);
-            } else {
-                $("tbody tr").find('td input[type="checkbox"]:eq(0)').prop('checked', false);
-                $(".all_selector").prop('checked', false);
-            }
-
-            select_item_action();
-        });
-
-        $("tfoot").on('click', ".all_selector", function() {
-            if ($("tfoot .all_selector").is(':checked')) {
-                $("tbody tr").find('td input[type="checkbox"]:eq(0)').prop('checked', true);
-                $(".all_selector").prop('checked', true);
-            } else {
-                $("tbody tr").find('td input[type="checkbox"]:eq(0)').prop('checked', false);
-                $(".all_selector").prop('checked', false);
-            }
-
-            select_item_action();
-        });
-
-        $("tbody tr").on('click', "input[type='checkbox']", function() {
-            select_item_action();
-        });
-
-        $('body').addClass('add-transition');
-        $('.add-page-transition').on('click', function(){
-            var transAttr = $(this).attr('data-transition');
-            $('.add-transition').attr('class', 'add-transition');
-            $('.add-transition').addClass(transAttr);
-        });
-    });
-
-    function get_selected_element(checked) {
-        if (checked === null) {
-            checked = true;
-        }
-
-        if (checked) {
-            return $('tbody :checked');
-        } else {
-
-            return $('tbody input:not(:checked)');
-        }
-    }
-
-    function highlight_selected_element() {
-        var selected_element = get_selected_element(true);
-        var no_selected_element = get_selected_element(false);
-
-        selected_element.each(function(i, e) {
-            $(e).parent().parent().toggleClass('tr-selected', true);
-        });
-
-        no_selected_element.each(function(i, e) {
-            $(e).parent().parent().toggleClass('tr-selected', false);
-        });
-    }
-
-    function shortcut_bar_enable() {
-        var selected_element = get_selected_element(true);
-
-        if (selected_element.length > 0) {
-            $('.btn-shortcut').toggleClass('disabled', false);
-        } else {
-            $('.btn-shortcut').toggleClass('disabled', true);
-        }
-    }
-
-    function select_item_action() {
-        highlight_selected_element();
-        shortcut_bar_enable();
-    }
-
-    function refresh() {
-        page = $('#pagination li.active a').text();
-        page_size = $('#page_size').val();
-        cur_url = resource_path + '?page=' + page + '&page_size=' + page_size;
-        window.location.href=cur_url;
-    }
-
-    function remove(uuid, boot_job_id, do_refresh) {
-        $.ajax({
-            url : '/api/guests/_boot_jobs/' + uuid + '/' + boot_job_id,
-            type : 'DELETE',
-            contentType: "application/json; charset=utf-8",
-            error : function() {
-                alter_danger('启动作业删除指令发送失败!');
-            },
-            success : function() {
-                // 未指定值的参数为 undefined
-                if (do_refresh) {
-                    alter_success('启动作业删除指令发送成功!');
-                    refresh();
-                }
-            }
-        });
-    }
-
-    function delete_at(me) {
-        var uuid = $('#instance_uuid').val();
-        var boot_job_id = $('#instance_boot_job_id').val();
-        remove(uuid, boot_job_id, true);
-        $('#delete_modal').modal('hide');
-    }
-
-    function get_selected_uuids_and_boot_jobs_id() {
-        var selected_element = get_selected_element(true);
-        var uuids = [];
-        var boot_jobs_id = [];
-
-        selected_element.each(function(i, e) {
-            var uuid = $(e).parent().prev().prev().text();
-            var boot_job_id = $(e).parent().prev().text();
-            uuids.push(uuid);
-            boot_jobs_id.push(boot_job_id);
-        });
-
-        return {'uuids': uuids, 'boot_jobs_id': boot_jobs_id};
-    }
-
-    function get_checked_uuids_and_boot_jobs_id() {
-        var uuids_and_boot_jobs_id = get_selected_uuids_and_boot_jobs_id();
-
-        if (uuids_and_boot_jobs_id['uuids'].length < 1) {
-            alter_warning('未选择任何可操作的实例!');
-            return false;
-        }
-
-        return uuids_and_boot_jobs_id;
-    }
-
-    function batch_delete() {
-        var uuids_and_boot_jobs_id = get_checked_uuids_and_boot_jobs_id();
-        if (uuids_and_boot_jobs_id['uuids'] && uuids_and_boot_jobs_id['boot_jobs_id']) {
-            for (var i=0; i<uuids_and_boot_jobs_id['uuids'].length; i++) {
-                if (i === uuids_and_boot_jobs_id['uuids'].length  - 1) {
-                    remove(uuids_and_boot_jobs_id['uuids'][i], uuids_and_boot_jobs_id['boot_jobs_id'][i], true);
-                } else {
-                    remove(uuids_and_boot_jobs_id['uuids'][i], uuids_and_boot_jobs_id['boot_jobs_id'][i], false);
-                }
-            }
-        }
-        $('#batch_delete_modal').modal('hide');
-    }
-</script>
-<div class="panel">
-    <div class="panel-body">
-        <h3 class="title-hero" style="font-size: 24px;">
-            虚拟机启动作业
-        </h3>
-        <div>
-            <div id="datatable-row-highlight_wrapper" class="dataTables_wrapper form-inline">
-                <div class="row" style="padding: 10px 10px 10px 0; width: 100%;">
-                    <div class="col-sm-12" style="padding-right: 0;">
-                        <div class="pull-right">
-                            <button class="btn btn-default" onclick="refresh()" style="border-radius: 0;"><span class="glyph-icon icon-elusive-arrows-cw"></span></button>
-                        </div>
-                    </div>
-                </div>
-                <table id="guests_boot_jobs_list" class="table table-bordered table-hover" cellspacing="0" width="100%" role="grid"
-                       style="width: 100%; margin-bottom: 0; border-bottom-width: 0;">
-                <thead>
-                <tr role="row">
-                    <th style="display: none;">UUID</th>
-                    <th style="display: none;">启动作业 ID</th>
-                    <th><input class="all_selector" title="选取所有" type="checkbox"></th>
-                    <th width="180px;">有效时间</th>
-                    <th width="180px;">启动作业</th>
-                    <th width="180px;">名称</th>
-                    <th></th>
-                    <th>状态</th>
-                    <th>IP</th>
-                    <th>操作</th>
-                </tr>
-                </thead>
-                <tbody>
-                {% for uuid, boot_jobs in guests_boot_jobs_ret.data.iteritems() %}
-                    {% for boot_job_id in boot_jobs.boot_jobs %}
-                    <tr role="row" class="odd">
-                        <td style="display: none;">{{ uuid }}</td>
-                        <td style="display: none;">{{ boot_job_id }}</td>
-                        <td><input title="选中" type="checkbox"></td>
-                        <td>
-                            <span style="color: #b94a48; {% if boot_jobs.ttl // 60 == 0 %}display: none;{% endif %}">{{ boot_jobs.ttl // 60 }}分</span>
-                            <span style="color: #b94a48; {% if boot_jobs.ttl % 60 == 0 %}display: none;{% endif %}">{{ boot_jobs.ttl % 60 }}秒</span>
-                        </td>
-                        <td>
-                            <div>
-                                <a href="/operate_rules?boot_job_id={{ boot_job_id }}">
-                                    <p style="display: inline-block;">{{ boot_jobs_mapping_by_id[boot_job_id | int].name }}</p>
-                                </a>
-                            </div>
-                        </td>
-                        <td>
-                            <div>
-                                <a href="/guest/detail/{{ uuid }}">
-                                    <p style="display: inline-block;">
-                                        {{ guests_mapping_by_uuid[uuid].label }}
-                                    </p>
-                                </a>
-                                <div>
-                                    <p style="display: inline-block;">{{ guests_mapping_by_uuid[uuid].remark }}</p>
-                                </div>
-                            </div>
-                        </td>
-                        <td><span class="{{ os_templates_mapping_by_id[guests_mapping_by_uuid[uuid].os_template_id].icon }}"></span></td>
-                        <td>{{ format_guest_status(guests_mapping_by_uuid[uuid].status, guests_mapping_by_uuid[uuid].progress)|safe }}</td>
-                        <td>{{ guests_mapping_by_uuid[uuid].ip }}</td>
-                        <td>
-                            <div class="dropdown inline-block">
-                                <a href="javascript:;" class="dropdown-toggle" data-toggle="dropdown">
-                                    更多
-                                </a>
-                                <ul class="dropdown-menu">
-                                    <li>
-                                        <a href="javascript:;" data-toggle="modal" data-target="#delete_modal"
-                                            onclick="$('#instance_uuid').val($(this).parent().parent().parent().parent().parent().children()[0].textContent);
-                                                $('#instance_boot_job_id').val($(this).parent().parent().parent().parent().parent().children()[1].textContent);
-                                                $('#delete_instance_desc').text($($(this).parent().parent().parent().parent().parent().children()[5]).find('div a')[0].text);
-                                                $('#delete_instance_desc').append('/' + $($(this).parent().parent().parent().parent().parent().children()[4]).find('div a')[0].text)">
-                                            删除
-                                        </a>
-                                    </li>
-                                </ul>
-                            </div>
-                        </td>
-                    </tr>
-                    {% endfor %}
-                {% endfor %}
-                </tbody>
-                </table>
-
-                <table class="table table-bordered" style="border-top-width: 0; z-index: 99; position: sticky; bottom: 0;">
-                    <tfoot>
-                    <tr style="height: 70px;">
-                        <th><input type="checkbox" title="选取所有" class="all_selector"></th>
-                        <th>
-                            <div class="row">
-                                <div class="col-sm-6">
-                                    <button class="btn btn-default btn-shortcut disabled" data-toggle="modal" data-target="#batch_delete_modal">删除</button>
-                                </div>
-                                <div class="col-sm-3" style="font-size: 12px; padding-top: 5px; text-align: right;">
-                                    共有{{ paging_total }}个,每页显示:
-                                    <select id="page_size" name="datatable-row-highlight_length" title="page_size" class="form-control" style="height: 22px; vertical-align: baseline;">
-                                        <option value="10" {% if page_size == 10 %} selected {% endif %}>10</option>
-                                        <option value="20" {% if page_size == 20 %} selected {% endif %}>20</option>
-                                        <option value="50" {% if page_size == 50  %} selected {% endif %}>50</option>
-                                    </select>&nbsp;&nbsp;个
-                                </div>
-                                <div class="col-sm-3" style="text-align: left;">
-                                    <div class="dataTables_paginate paging_bootstrap" id="datatable-row-highlight_paginate">
-                                        <ul id="pagination" class="pagination">
-                                            <li class="{% if page == 1 %} disabled {% endif %}">
-                                                <a href="{{ resource_path }}?page={{ page - 1 }}&page_size={{ page_size }}{% if keyword %}&keyword={{ keyword }}{% endif %}">«</a>
-                                            </li>
-                                            {% for item in pages %}
-                                                <li class="{% if item == page %} active {% endif %}">
-                                                    <a href="{{ resource_path }}?page={{ item }}&page_size={{ page_size }}{% if keyword %}&keyword={{ keyword }}{% endif %}">{{ item }}</a>
-                                                </li>
-                                            {% endfor %}
-                                            <li class="{% if page == last_page %} disabled {% endif %}">
-                                                <a href="{{ resource_path }}?page={{ page + 1 }}&page_size={{ page_size }}{% if keyword %}&keyword={{ keyword }}{% endif %}">»</a>
-                                            </li>
-                                        </ul>
-                                    </div>
-                                </div>
-                            </div>
-                        </th>
-                    </tr>
-                    </tfoot>
-                </table>
-            </div>
-        </div>
-    </div>
-</div>
-
-<input id="instance_uuid" title="实例 UUID" class="form-control" name="uuid" hidden>
-<input id="instance_boot_job_id" title="实例启动作业 ID" class="form-control" name="boot_job_id" hidden>
-
-<div class="modal" id="delete_modal" tabindex="-1" role="dialog" style="margin-top: 100px;">
-    <div class="modal-dialog">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h4 class="modal-title">删除启动作业:</h4>
-            </div>
-            <div class="modal-body">
-                <p>你确定要删除该启动作业吗?</p>
-                <h3 style="color: orangered;" id="delete_instance_desc"></h3>
-            </div>
-            <div class="modal-footer">
-                <button type="button" class="btn btn-sm btn-primary" onclick="delete_at();">确定</button>
-                <button type="button" class="btn btn-sm btn-default" data-dismiss="modal">取消</button>
-            </div>
-        </div>
-    </div>
-</div>
-
-<div class="modal" id="batch_delete_modal" tabindex="-1" role="dialog" style="margin-top: 100px;">
-    <div class="modal-dialog">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h4 class="modal-title">批量删除启动作业:</h4>
-            </div>
-            <div class="modal-body">
-                <p>您确定要执行批量删除选择的启动作业吗?</p>
-            </div>
-            <div class="modal-footer">
-                <button type="button" class="btn btn-sm btn-primary" onclick="batch_delete();">确定</button>
-                <button type="button" class="btn btn-sm btn-default" data-dismiss="modal">取消</button>
-            </div>
-        </div>
-    </div>
-</div>
-{% endblock content %}

+ 7 - 95
templates/guests_show.html

@@ -170,10 +170,6 @@
             $('#reset_password_instance_uuid').val(
                 $(me.relatedTarget).parent().parent().parent().parent().parent().children()[0].textContent);
         });
-
-        $('#add_boot_job_modal').on('show.bs.modal', function (me) {
-            refresh_boot_job_selectpicker($('#boot_job_selector'));
-        });
     });
 
     function refresh() {
@@ -187,26 +183,6 @@
         window.location.href=cur_url;
     }
 
-    function refresh_boot_job_selectpicker(selectpicker) {
-        $.ajax({
-            url : '/api/boot_jobs?filter=use_for:eq:2',
-            type : 'GET',
-            contentType: "application/json; charset=utf-8",
-            dataType: 'json',
-            error : function() {
-            },
-            success : function(data, textStatus, xhr) {
-                selectpicker.empty();
-                $.each(data.data, function(k, v) {
-                    selectpicker.append(
-                        $('<option>', {value: v['id'], text: v['name'], 'data-subtext': v['remark']})
-                    );
-                });
-                selectpicker.selectpicker('refresh');
-            }
-        });
-    }
-
     function row_onmouseover(me) {
         $(me).find(".edit_remark_trigger").css('display','inline-flex');
     }
@@ -417,22 +393,6 @@
         });
     }
 
-    function add_boot_job(uuids, boot_job_id) {
-        $.ajax({
-            url : '/api/guests/_boot_jobs/' + uuids.join(',') + '/' + boot_job_id,
-            type : 'PUT',
-            contentType: "application/json; charset=utf-8",
-            error : function() {
-                alter_danger('启动作业添加指令发送失败!');
-            },
-            success : function() {
-                alter_success('启动作业添加指令发送成功!');
-                window.location.href='/guests/boot_jobs_list';
-                // refresh();
-            }
-        });
-    }
-
     function boot_at(me) {
         var uuid = $('#instance_uuid').val();
         boot([uuid]);
@@ -594,15 +554,6 @@
         $('#batch_delete_modal').modal('hide');
     }
 
-    function batch_add_boot_job() {
-        var uuids = get_checked_uuids();
-        var boot_job_id = $('#boot_job_selector').val();
-        if (uuids) {
-            add_boot_job(uuids, boot_job_id);
-        }
-        $('#add_boot_job_modal').modal('hide');
-    }
-
     var show_real_password = false;
     function change_display_password(me) {
         if (show_real_password) {
@@ -669,7 +620,13 @@
                             </a>
                         </div>
                     </td>
-                    <td><span class="{{ os_templates_mapping_by_id[item.os_template_id].icon }}" title="{{ os_templates_mapping_by_id[item.os_template_id].label }}"></span></td>
+                    <td>
+                        {% if os_templates_image_mapping_by_id[item.os_template_image_id].logo == "" %}
+                            <span class="{{ os_templates_profile_mapping_by_id[os_templates_image_mapping_by_id[item.os_template_image_id].os_template_profile_id].icon }}" title="{{ os_templates_image_mapping_by_id[item.os_template_image_id].label }}"></span>
+                        {% else %}
+                            <span class="{{ os_templates_image_mapping_by_id[item.os_template_image_id].logo }}" title="{{ os_templates_image_mapping_by_id[item.os_template_image_id].label }}"></span>
+                        {% endif %}
+                    </td>
                     <td>{{ format_guest_status(item.status, item.progress)|safe }}</td>
                     <td>{{ hosts_mapping_by_node_id[item.node_id].hostname }}</td>
                     <td>CPU :&nbsp;&nbsp;{{ item.cpu }}&nbsp;核<br />内存 :&nbsp;&nbsp;{{ item.memory }}&nbsp;GB</td>
@@ -681,7 +638,6 @@
                             <a href="javascript:;" class="dropdown-toggle" data-toggle="dropdown">
                                 更多
                             </a>
-                            <div class="small-badge bg-red" style="display: {% if guests_boot_jobs_ret.data[item.uuid].boot_jobs|length < 1 %} none {% else %} inline-flex{% endif %};"></div>
                             <ul class="dropdown-menu">
                                 <li class="{% if item.status not in [4, 5, 6] %} disabled {% endif %}">
                                     <a href="javascript:;" data-toggle="modal" data-target="#boot_modal"
@@ -744,13 +700,6 @@
                                     <a href="/guest/vnc/{{ item.uuid }}" target="_blank">远程连接</a>
                                 </li>
                                 <li class="divider"></li>
-                                <li class="">
-                                    <a href="/guest/boot_jobs/{{ item.uuid }}" target="_blank">
-                                        启动作业&nbsp;&nbsp;
-                                        <span class="bs-badge badge-info" style="display: {% if guests_boot_jobs_ret.data[item.uuid].boot_jobs|length < 1 %} none {% endif %};">{{ guests_boot_jobs_ret.data[item.uuid].boot_jobs|length }}</span>
-                                    </a>
-                                </li>
-                                <li class="divider"></li>
                                 <li class="">
                                     <a href="javascript:;" data-toggle="modal" data-target="#reset_password_modal"
                                        onclick="$('#reset_password_type').val('single')">
@@ -813,12 +762,6 @@
                                                 </a>
                                             </li>
                                             <li class="divider"></li>
-                                            <li>
-                                                <a href="javascript:;" data-toggle="modal" data-target="#add_boot_job_modal">
-                                                    启动作业
-                                                </a>
-                                            </li>
-                                            <li class="divider"></li>
                                             <li>
                                                 <a href="javascript:;" data-toggle="modal" data-target="#batch_force_shutdown_modal">强制停止</a>
                                             </li>
@@ -922,37 +865,6 @@
     </div>
 </div>
 
-<div class="modal" id="add_boot_job_modal" tabindex="-1" role="dialog" style="margin-top: 100px;">
-    <div class="modal-dialog modal-lg">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h4 class="modal-title">添加启动作业:</h4>
-            </div>
-            <div class="modal-body" style="padding-top: 0; padding-bottom: 0;">
-                <div class="example-box-wrapper">
-                    <form class="form-horizontal bordered-row">
-                        <div class="form-group">
-                            <div class="col-sm-2"></div>
-                            <label class="col-sm-2 control-label"><span class="glyph-icon icon-list-alt"></span>&nbsp;&nbsp;启动作业</label>
-                            <div class="col-sm-6">
-                                <select id="boot_job_selector" name="boot_job_selector" title="启动作业" class="selectpicker">
-                                </select>
-                            </div>
-                        </div>
-                        <div class="form-group">
-                            <div class="col-sm-4"></div>
-                            <div class="col-sm-6 pull-right">
-                                <button class="btn btn-blue-alt" style="width: 180px; height: 40px; font-size: 16px;" onclick="batch_add_boot_job();">添加</button>
-                                <button class="btn btn-default" style="width: 64px; height: 40px; font-size: 16px;" data-dismiss="modal">取消</button>
-                            </div>
-                        </div>
-                    </form>
-                </div>
-            </div>
-        </div>
-    </div>
-</div>
-
 <input id="instance_uuid" title="实例 UUID" class="form-control" name="uuid" hidden>
 
 <div class="modal" id="delete_modal" tabindex="-1" role="dialog" style="margin-top: 100px;">

+ 2 - 26
templates/layout.html

@@ -338,16 +338,10 @@
                             </a>
                         </li>
                         <li>
-                            <a href="#" title="虚拟机">
+                            <a href="{{ url_for('v_guests.show') }}" title="虚拟机实例">
                                 <i class="glyph-icon icon-linecons-cloud"></i>
-                                <span>虚拟机</span>
+                                <span>虚拟机实例</span>
                             </a>
-                            <div class="sidebar-submenu">
-                                <ul>
-                                    <li><a href="{{ url_for('v_guests.show') }}" title="虚拟机实例列表"><span>实例</span></a></li>
-                                    <li><a href="{{ url_for('v_guests.show_guests_boot_jobs') }}" title="虚拟机启动作业列表"><span>启动作业</span></a></li>
-                                </ul>
-                            </div><!-- .sidebar-submenu -->
                         </li>
                         <li>
                             <a href="{{ url_for('v_disks.show') }}" title="磁盘实例列表">
@@ -373,24 +367,6 @@
                                 <span>虚拟机模板镜像</span>
                             </a>
                         </li>
-                        <li>
-                            <a href="{{ url_for('v_os_templates.show') }}" title="虚拟机模板列表">
-                                <i class="glyph-icon icon-linecons-inbox"></i>
-                                <span>虚拟机模板</span>
-                            </a>
-                        </li>
-                        <li>
-                            <a href="#" title="虚拟机启动作业">
-                                <i class="glyph-icon icon-linecons-paper-plane"></i>
-                                <span>启动作业</span>
-                            </a>
-                            <div class="sidebar-submenu">
-                                <ul>
-                                    <li><a href="{{ url_for('v_boot_jobs.show') }}" title="启动作业列表"><span>启动作业</span></a></li>
-                                    <li><a href="{{ url_for('v_operate_rules.show') }}" title="启动作业操作细则"><span>启动作业操作细则</span></a></li>
-                                </ul>
-                            </div><!-- .sidebar-submenu -->
-                        </li>
                         <li>
                             <a href="{{ url_for('v_config.show') }}" title="JimV 虚拟化平台系统配置">
                                 <i class="glyph-icon icon-linecons-cog"></i>

+ 0 - 733
templates/operate_rules_show.html

@@ -1,733 +0,0 @@
-{% extends "layout.html" %}
-{% block head %}
-    {{ super() }}
-    <style type="text/css">
-
-        @media (min-width: 768px) {
-            .form-horizontal .control-label {
-                text-align: left;
-            }
-        }
-
-        label>span {
-            color: deepskyblue;
-        }
-
-        .table {
-            font-size: 12px;
-            border-width: 1px;
-            line-height: 20px;
-        }
-        .table > thead > tr > th,
-        .table > tfoot > tr > th {
-            color: #999999;
-            font-weight: normal;
-            border-bottom: 0 solid #e1e6eb;
-            background-color: #F5F6FA;
-        }
-        
-        .table > tbody > tr > td {
-            color: #424547;
-        }
-
-        .table-bordered > tbody > tr {
-            padding-top: 10px;
-            padding-bottom: 10px;
-        }
-
-        .table-bordered > thead > tr > th,
-        .table-bordered > tbody > tr > th,
-        .table-bordered > tfoot > tr > th,
-        .table-bordered > thead > tr > td,
-        .table-bordered > tbody > tr > td,
-        .table-bordered > tfoot > tr > td {
-            border-style: solid;
-            border-width: 1px 0 0 0;
-        }
-
-        .btn,
-        .form-control,
-        .modal-content {
-            border-radius: 0 !important;
-        }
-
-        .path_or_command {
-            white-space: nowrap;
-            overflow: hidden;
-            background-color: #e5eef4;
-            text-overflow: ellipsis;
-            padding-left: 5px;
-            padding-right: 5px;
-        }
-
-        .path_or_command:hover {
-            white-space: inherit;
-            overflow: visible;
-        }
-    </style>
-
-{% endblock head %}
-{% block content %}
-
-<script type="text/javascript">
-    var page = 1;
-    var page_size = 10;
-    var keyword = '';
-    var resource_path = window.location.pathname;
-    var cur_url = resource_path;
-
-    $(document).ready(function() {
-        page_size = $('#page_size').val();
-        cur_url = resource_path + '?page=' + page + '&page_size=' + page_size;
-
-        var last_ready = null;
-        $('#content_search').keydown(function() {
-            if (last_ready !== null) {
-                clearTimeout(last_ready);
-            }
-            last_ready = setTimeout(function () {
-                keyword = $('#content_search').val();
-                cur_url = resource_path + '?page=' + page + '&page_size=' + page_size;
-                if (keyword.length > 0) {
-                    cur_url = resource_path + '?page=' + page + '&page_size=' + page_size + '&keyword=' + keyword;
-                }
-                window.location.href=cur_url;
-            }, 1000);
-        });
-
-        $('#page_size').change(function () {
-            keyword = $('#content_search').val();
-            page_size = $('#page_size').val();
-            cur_url = resource_path + '?page=' + page + '&page_size=' + page_size;
-            if (keyword.length > 0) {
-                cur_url = resource_path + '?page=' + page + '&page_size=' + page_size + '&keyword=' + keyword;
-            }
-            window.location.href=cur_url;
-        });
-
-        $('#edit_path_modal').on('show.bs.modal', function (me) {
-            $(me.relatedTarget).css('display','none');
-            $('#operate_rule_id').val($(me.relatedTarget).parent().parent().prev().prev().prev().prev().text());
-            $('#edit_path').val($(me.relatedTarget).parent()[0].innerText);
-        });
-
-        $('#edit_content_modal').on('show.bs.modal', function (me) {
-            // 下行 display 设为 none 的目的为,第三行取框内文本时,避免了末尾多出来的换行符。
-            $(me.relatedTarget).css('display','none');
-            $('#operate_rule_id').val($(me.relatedTarget).parent().parent().prev().prev().prev().prev().text());
-            $('#edit_content').val($(me.relatedTarget).parent()[0].innerText);
-        });
-
-        $('#edit_command_modal').on('show.bs.modal', function (me) {
-            $(me.relatedTarget).css('display','none');
-            $('#operate_rule_id').val($(me.relatedTarget).parent().parent().prev().prev().prev().prev().text());
-            $('#edit_command').val($(me.relatedTarget).parent()[0].innerText);
-        });
-
-        $('#add_operate_rule_modal').on('show.bs.modal', function (me) {
-            refresh_operate_rule_boot_job_selectpicker($('#boot_job_id'));
-        });
-
-        $('#update_kind_modal').on('show.bs.modal', function (me) {
-            $('#operate_rule_id').val($(me.relatedTarget).parent().prev().prev().text());
-            $('#update_kind_instance_desc').text($(me.relatedTarget).parent().prev()[0].innerText);
-            $('#update_kind_instance_desc_sub').empty();
-            $('#update_kind_instance_desc_sub').append($(me.relatedTarget).parent().next().next().find('div').prop('outerHTML'));
-        });
-
-        $('#update_sequence_modal').on('show.bs.modal', function (me) {
-            $('#operate_rule_id').val($(me.relatedTarget).parent().prev().prev().prev().text());
-            $('#update_sequence').val($(me.relatedTarget)[0].innerText);
-            $('#update_sequence_instance_desc').text($(me.relatedTarget).parent().prev().prev()[0].innerText);
-            $('#update_sequence_instance_desc_sub').empty();
-            $('#update_sequence_instance_desc_sub').append($(me.relatedTarget).parent().next().find('div').prop('outerHTML'));
-        });
-
-        $('#kind').on('changed.bs.select', function (me) {
-            $('#payload_panel').empty();
-            if ($('#kind').val() == '0') {
-                $('#payload_panel').append('\
-                    <div class="form-group">\
-                        <div class="col-sm-2"></div>\
-                        <label class="col-sm-2 control-label"><span class="glyph-icon icon-terminal"></span>&nbsp;&nbsp;命令</label>\
-                        <div class="col-sm-6">\
-                            <input id="command" title="命令" class="form-control" name="command">\
-                        </div>\
-                    </div>'
-                );
-            } else {
-                $('#payload_panel').append('\
-                    <div class="form-group">\
-                        <div class="col-sm-2"></div>\
-                        <label class="col-sm-2 control-label"><span class="glyph-icon icon-elusive-stumbleupon"></span>&nbsp;&nbsp;文件路径</label>\
-                        <div class="col-sm-6">\
-                            <input id="path" title="操作细则文件路径" class="form-control" name="path">\
-                        </div>\
-                    </div>'
-                );
-                $('#payload_panel').append('\
-                    <div class="form-group">\
-                        <div class="col-sm-2"></div>\
-                        <label class="col-sm-2 control-label"><span class="glyph-icon icon-file-text-o"></span>&nbsp;&nbsp;文件内容</label>\
-                        <div class="col-sm-6">\
-                            <textarea id="content" title="操作细则文件内容" class="form-control" rows="6" name="content"></textarea>\
-                        </div>\
-                    </div>'
-                );
-            }
-        });
-
-        $('#add_operate_rule_form').formValidation({
-            framework: 'bootstrap4',
-            icon: {
-                valid: 'fa fa-check',
-                invalid: 'fa fa-times',
-                validating: 'fa fa-refresh'
-            },
-            // Since the Bootstrap Button hides the radio and checkbox
-            // We exclude the disabled elements only
-            excluded: ':disabled',
-            locale: 'zh_CN',
-            fields: {
-                boot_job_id: {
-                    validators: {
-                        notEmpty: {
-                        }
-                    }
-                },
-                kind: {
-                    validators: {
-                        notEmpty: {
-                        }
-                    }
-                },
-                sequence: {
-                    validators: {
-                        between: {
-                            min: 1,
-                            max: 20
-                        }
-                    }
-                }
-            }
-        })
-        .on('success.field.fv', function(e, data) {
-            if (data.fv.getInvalidFields().length > 0) {    // There is invalid field
-                data.fv.disableSubmitButtons(true);
-            }
-        });
-    });
-
-    function refresh() {
-        keyword = $('#content_search').val();
-        page = $('#pagination li.active a').text();
-        page_size = $('#page_size').val();
-        cur_url = resource_path + '?page=' + page + '&page_size=' + page_size;
-        if (keyword.length > 0) {
-            cur_url = resource_path + '?page=' + page + '&page_size=' + page_size + '&keyword=' + keyword;
-        }
-        window.location.href=cur_url;
-    }
-
-    function panel_onmouseover(me) {
-        $(me).find(".edit_trigger").css('display','inline-flex');
-    }
-
-    function panel_onmouseout(me) {
-        $(me).find(".edit_trigger").css('display','none');
-    }
-
-    $(function() { "use strict";
-        $("#update_sequence, #sequence").TouchSpin({
-            max: 20,
-            min: 1,
-            verticalbuttons: true,
-            verticalupclass: 'glyph-icon icon-plus',
-            verticaldownclass: 'glyph-icon icon-minus'
-        });
-    });
-
-    function refresh_operate_rule_boot_job_selectpicker(selectpicker) {
-        $.ajax({
-            url : '/api/boot_jobs?filter=use_for:notin:1',
-            type : 'GET',
-            contentType: "application/json; charset=utf-8",
-            dataType: 'json',
-            error : function() {
-            },
-            success : function(data, textStatus, xhr) {
-                selectpicker.empty();
-                $.each(data.data, function(k, v) {
-                    selectpicker.append(
-                        $('<option>', {value: v['id'], text: v['name'], 'data-subtext': v['remark']})
-                    );
-                });
-                selectpicker.selectpicker('refresh');
-            }
-        });
-    }
-
-    function path_update(me) {
-        var operate_rule_id = $('#operate_rule_id').val();
-        var path = $('#edit_path').val();
-        $('#edit_path_modal').modal('hide');
-        $.ajax({
-            url : '/api/operate_rule/' + operate_rule_id,
-            type : 'PATCH',
-            contentType: "application/json; charset=utf-8",
-            data : JSON.stringify({
-                path: path
-            }),
-            error : function() {
-                alter_danger('操作细则文件路径更新失败!');
-            },
-            success : function() {
-                alter_success('操作细则文件路径更新成功!');
-                refresh()
-            }
-        });
-    }
-
-    function content_update(me) {
-        var operate_rule_id = $('#operate_rule_id').val();
-        var content = $('#edit_content').val();
-        $('#edit_content_modal').modal('hide');
-        $.ajax({
-            url : '/api/operate_rule/' + operate_rule_id,
-            type : 'PATCH',
-            contentType: "application/json; charset=utf-8",
-            data : JSON.stringify({
-                content: content
-            }),
-            error : function() {
-                alter_danger('操作细则文件内容更新失败!');
-            },
-            success : function() {
-                alter_success('操作细则文件内容更新成功!');
-                refresh()
-            }
-        });
-    }
-
-    function command_update(me) {
-        var operate_rule_id = $('#operate_rule_id').val();
-        var command = $('#edit_command').val();
-        $('#edit_command_modal').modal('hide');
-        $.ajax({
-            url : '/api/operate_rule/' + operate_rule_id,
-            type : 'PATCH',
-            contentType: "application/json; charset=utf-8",
-            data : JSON.stringify({
-                command: command
-            }),
-            error : function() {
-                alter_danger('操作细则命令更新失败!');
-            },
-            success : function() {
-                alter_success('操作细则命令更新成功!');
-                refresh()
-            }
-        });
-    }
-
-    function update_kind(id) {
-        $.ajax({
-            url : '/api/operate_rule/' + id,
-            type : 'PATCH',
-            contentType: "application/json; charset=utf-8",
-            data : JSON.stringify({
-                kind: parseInt($('#update_kind').val())
-            }),
-            error : function() {
-                alter_danger('变更操作细则类型指令发送失败!');
-            },
-            success : function() {
-                alter_success('变更操作细则类型指令发送成功!');
-                refresh();
-            }
-        });
-    }
-
-    function update_sequence(id) {
-        $.ajax({
-            url : '/api/operate_rule/' + id,
-            type : 'PATCH',
-            contentType: "application/json; charset=utf-8",
-            data : JSON.stringify({
-                sequence: parseInt($('#update_sequence').val())
-            }),
-            error : function() {
-                alter_danger('变更操作细则执行序列指令发送失败!');
-            },
-            success : function() {
-                alter_success('变更操作细则执行序列指令发送成功!');
-                refresh();
-            }
-        });
-    }
-
-    function remove(id) {
-        $.ajax({
-            url : '/api/operate_rules/' + id,
-            type : 'DELETE',
-            contentType: "application/json; charset=utf-8",
-            error : function() {
-                alter_danger('操作细则删除指令发送失败!');
-            },
-            success : function() {
-                alter_success('操作细则删除指令发送成功!');
-                refresh();
-            }
-        });
-    }
-
-    function delete_at(me) {
-        var id = $('#operate_rule_id').val();
-        remove(id);
-        $('#delete_modal').modal('hide');
-    }
-
-    function update_kind_at(me) {
-        var id = $('#operate_rule_id').val();
-        update_kind(id);
-        $('#update_kind_modal').modal('hide');
-    }
-
-    function update_sequence_at(me) {
-        var id = $('#operate_rule_id').val();
-        update_sequence(id);
-        $('#update_sequence_modal').modal('hide');
-    }
-</script>
-<div class="panel">
-    <div class="panel-body">
-        <h3 class="title-hero" style="font-size: 24px;">
-            启动作业操作细则
-        </h3>
-        <div>
-            <div id="datatable-row-highlight_wrapper" class="dataTables_wrapper form-inline">
-                <div class="row" style="padding: 10px 10px 10px 0; width: 100%;">
-                    <div class="col-sm-12" style="padding-right: 0;">
-                        <div id="datatable-row-highlight_filter" class="dataTables_filter" style="display: inline-block;">
-                            <input id="content_search" type="search" class="form-control" placeholder="模糊搜索..." value="{%- if keyword -%} {{ keyword }} {%- endif -%}" style="margin-left: 0; border-radius: 0;">
-                        </div>
-                        <div class="pull-right">
-                            <button class="btn btn-default" onclick="refresh()" style="border-radius: 0;"><span class="glyph-icon icon-elusive-arrows-cw"></span></button>
-                            <a class="btn btn-info" href="javascript:;" data-toggle="modal" data-target="#add_operate_rule_modal" style="border-radius: 0; padding-left: 40px; padding-right: 40px;">添加操作细则</a>
-                        </div>
-                    </div>
-                </div>
-                <table id="operate_rule_list" class="table table-bordered table-hover" cellspacing="0" width="100%" role="grid"
-                       style="width: 100%; margin-bottom: 0; border-bottom-width: 0;">
-                <thead>
-                <tr role="row">
-                    <th style="display: none;">ID</th>
-                    <th>启动作业</th>
-                    <th width="80px;">类型</th>
-                    <th>执行序列</th>
-                    <th width="600px;">[路径/内容 | 命令]</th>
-                    <th>操作</th>
-                </tr>
-                </thead>
-                <tbody>
-                {% for item in operate_rules_ret.data %}
-                <tr role="row" class="odd">
-                    <td style="display: none;">{{ item.id }}</td>
-                    <td>
-                        <a href="/boot_jobs?boot_job_id={{ item.boot_job_id }}">
-                            {{ boot_jobs_mapping_by_id[item.boot_job_id].name }}
-                        </a>
-                    </td>
-                    <td>
-                        <a href="javascript:;" data-toggle="modal" data-target="#update_kind_modal">
-                            {% if item.kind == 0 %}
-                                <span style="color: #8800FF;">命令</span>
-                            {% elif item.kind == 1 %}
-                                <span style="color: #3333FF;">覆写文件</span>
-                            {% elif item.kind == 2 %}
-                                <span style="color: #00BB00;">追加内容</span>
-                            {% else %}
-                                <span style="color: #990000;">未知</span>
-                            {% endif %}
-                        </a>
-                    </td>
-                    <td>
-                        <a href="javascript:;" data-toggle="modal" data-target="#update_sequence_modal">
-                            {{ item.sequence }}
-                        </a>
-                    </td>
-                    <td onmouseover="panel_onmouseover(this);" onmouseout="panel_onmouseout(this);">
-                        {% if item.kind in [1, 2]%}
-                            <div class="path_or_command">{{ item.path }}<a href="javascript:;" class="edit_trigger" data-toggle="modal" data-target="#edit_path_modal" style="display: none; float: right;">
-                                    <span class="glyph-icon icon-elusive-pencil" style="width: 20px; height: 20px; margin-left: 10px; border-radius: 0; border: 1px solid rgb(220, 233, 255); background-color: #ffffff;"></span>
-                                </a>
-                            </div>
-                            <pre style="border-radius: 0; padding-right: 5px;">{{ item.content }}<a href="javascript:;" class="edit_trigger" data-toggle="modal" data-target="#edit_content_modal" style="display: none; float: right;">
-                                <span class="glyph-icon icon-elusive-pencil" style="width: 20px; height: 18px; margin-left: 10px; border-radius: 0; border: 1px solid rgb(220, 233, 255); background-color: #ffffff;"></span>
-                            </a></pre>
-                        {% elif item.kind == 0 %}
-                            <div class="path_or_command" style="background-color: #f9f2f4">{{ item.command }}<a href="javascript:;" class="edit_trigger" data-toggle="modal" data-target="#edit_command_modal" style="display: none; float: right;">
-                                    <span class="glyph-icon icon-elusive-pencil" style="width: 20px; height: 20px; margin-left: 10px; border-radius: 0; border: 1px solid rgb(220, 233, 255); background-color: #ffffff;"></span>
-                                </a>
-                            </div>
-                        {% endif %}
-                    </td>
-                    <td>
-                        <div class="dropdown inline-block">
-                            <a href="javascript:;" class="dropdown-toggle" data-toggle="dropdown">
-                                更多
-                            </a>
-                            <ul class="dropdown-menu">
-                                <li>
-                                    <a href="javascript:;" data-toggle="modal" data-target="#delete_modal"
-                                        onclick="$('#operate_rule_id').val($(this).parent().parent().parent().parent().parent().children()[0].textContent);
-                                        $('#delete_instance_desc_sub').empty();
-                                        $('#delete_instance_desc_sub').append($(this).parent().parent().parent().parent().prev().find('div').prop('outerHTML'));
-                                        $('#delete_instance_desc').text($(this).parent().parent().parent().parent().parent().children()[1].textContent)">
-                                        删除
-                                    </a>
-                                </li>
-                            </ul>
-                        </div>
-                    </td>
-                </tr>
-                {% endfor %}
-                </tbody>
-                </table>
-
-                <table class="table table-bordered" style="border-top-width: 0; z-index: 99; position: sticky; bottom: 0;">
-                    <tfoot>
-                    <tr style="height: 70px;">
-                        <th>
-                            <div class="row">
-                                <div class="col-sm-6">
-                                </div>
-                                <div class="col-sm-3" style="font-size: 12px; padding-top: 5px; text-align: right;">
-                                    共有{{ operate_rules_ret.paging.total }}条,每页显示:
-                                    <select id="page_size" name="datatable-row-highlight_length" title="page_size" class="form-control" style="height: 22px; vertical-align: baseline;">
-                                        <option value="10" {% if page_size == 10 %} selected {% endif %}>10</option>
-                                        <option value="20" {% if page_size == 20 %} selected {% endif %}>20</option>
-                                        <option value="50" {% if page_size == 50  %} selected {% endif %}>50</option>
-                                    </select>&nbsp;&nbsp;条
-                                </div>
-                                <div class="col-sm-3" style="text-align: left;">
-                                    <div class="dataTables_paginate paging_bootstrap" id="datatable-row-highlight_paginate">
-                                        <ul id="pagination" class="pagination">
-                                            <li class="{% if page == 1 %} disabled {% endif %}">
-                                                <a href="{{ resource_path }}?page={{ page - 1 }}&page_size={{ page_size }}{% if keyword %}&keyword={{ keyword }}{% endif %}{% if order_by %}&order_by={{ order_by }}{% endif %}{% if order %}&order={{ order }}{% endif %}">«</a>
-                                            </li>
-                                            {% for item in pages %}
-                                            <li class="{% if item == page %} active {% endif %}">
-                                                <a href="{{ resource_path }}?page={{ item }}&page_size={{ page_size }}{% if keyword %}&keyword={{ keyword }}{% endif %}{% if order_by %}&order_by={{ order_by }}{% endif %}{% if order %}&order={{ order }}{% endif %}">{{ item }}</a>
-                                            </li>
-                                            {% endfor %}
-                                            <li class="{% if page == last_page %} disabled {% endif %}">
-                                                <a href="{{ resource_path }}?page={{ page + 1 }}&page_size={{ page_size }}{% if keyword %}&keyword={{ keyword }}{% endif %}{% if order_by %}&order_by={{ order_by }}{% endif %}{% if order %}&order={{ order }}{% endif %}">»</a>
-                                            </li>
-                                        </ul>
-                                    </div>
-                                </div>
-                            </div>
-                        </th>
-                    </tr>
-                    </tfoot>
-                </table>
-            </div>
-        </div>
-    </div>
-</div>
-
-<input id="operate_rule_id" title="操作细则 ID" class="form-control" name="operate_rule_id" hidden>
-
-<div class="modal" id="edit_path_modal" tabindex="-1" role="dialog" style="margin-top: 100px;">
-    <div class="modal-dialog">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h4 class="modal-title">编辑操作细则文件路径:</h4>
-            </div>
-            <div class="modal-body">
-                <input id="edit_path" title="操作细则文件路径" class="form-control" name="operate_rule_path">
-            </div>
-            <div class="modal-footer">
-                <button type="button" class="btn btn-sm btn-primary" onclick="path_update();">确定</button>
-                <button type="button" class="btn btn-sm btn-default" data-dismiss="modal">取消</button>
-            </div>
-        </div>
-    </div>
-</div>
-
-<div class="modal" id="edit_content_modal" tabindex="-1" role="dialog" style="margin-top: 100px;">
-    <div class="modal-dialog">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h4 class="modal-title">编辑操作细则文件内容:</h4>
-            </div>
-            <div class="modal-body">
-                <textarea id="edit_content" title="操作细则文件内容" class="form-control" rows="6"
-                          name="operate_rule_content"></textarea>
-            </div>
-            <div class="modal-footer">
-                <button type="button" class="btn btn-sm btn-primary" onclick="content_update();">确定</button>
-                <button type="button" class="btn btn-sm btn-default" data-dismiss="modal">取消</button>
-            </div>
-        </div>
-    </div>
-</div>
-
-<div class="modal" id="edit_command_modal" tabindex="-1" role="dialog" style="margin-top: 100px;">
-    <div class="modal-dialog">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h4 class="modal-title">编辑操作细则命令:</h4>
-            </div>
-            <div class="modal-body">
-                <input id="edit_command" title="操作细则命令" class="form-control" name="operate_rule_command">
-            </div>
-            <div class="modal-footer">
-                <button type="button" class="btn btn-sm btn-primary" onclick="command_update();">确定</button>
-                <button type="button" class="btn btn-sm btn-default" data-dismiss="modal">取消</button>
-            </div>
-        </div>
-    </div>
-</div>
-
-<div class="modal" id="delete_modal" tabindex="-1" role="dialog" style="margin-top: 100px;">
-    <div class="modal-dialog">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h4 class="modal-title">删除操作细则:</h4>
-            </div>
-            <div class="modal-body">
-                <p>你确定要删除该操作细则吗?</p>
-                <h3 style="color: orangered;" id="delete_instance_desc"></h3>
-                <h6 id="delete_instance_desc_sub"></h6>
-            </div>
-            <div class="modal-footer">
-                <button type="button" class="btn btn-sm btn-primary" onclick="delete_at();">确定</button>
-                <button type="button" class="btn btn-sm btn-default" data-dismiss="modal">取消</button>
-            </div>
-        </div>
-    </div>
-</div>
-
-<div class="modal" id="add_operate_rule_modal" tabindex="-1" role="dialog" style="margin-top: 100px;">
-    <div class="modal-dialog modal-lg">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h4 class="modal-title">添加操作细则:</h4>
-            </div>
-            <div class="modal-body" style="padding-top: 0; padding-bottom: 0;">
-                <div class="example-box-wrapper">
-                    <form id="add_operate_rule_form" class="form-horizontal bordered-row" action="/operate_rule" method="post">
-                        <div class="form-group">
-                            <div class="col-sm-2"></div>
-                            <label class="col-sm-2 control-label"><span class="glyph-icon icon-elusive-compass-circled"></span>&nbsp;&nbsp;启动作业</label>
-                            <div class="col-sm-6">
-                                <select id="boot_job_id" name="boot_job_id" title="启动作业" class="selectpicker">
-                                </select>
-                            </div>
-                        </div>
-                        <div class="form-group">
-                            <div class="col-sm-2"></div>
-                            <label class="col-sm-2 control-label"><span class="glyph-icon icon-th-list"></span>&nbsp;&nbsp;类型</label>
-                            <div class="col-sm-6">
-                                <select id="kind" name="kind" title="请选择" class="selectpicker">
-                                    <option value="0" data-subtext="系统启动前,通过 guestfish 所执行的命令">命令</option>
-                                    <option value="1" data-subtext="系统启动前,通过 guestfish 替换目标文件中的内容">覆写文件</option>
-                                    <option value="2" data-subtext="系统启动前,通过 guestfish 向目标文件追加内容">追加内容</option>
-                                </select>
-                            </div>
-                        </div>
-                        <div class="form-group">
-                            <div class="col-sm-2"></div>
-                            <label class="col-sm-2 control-label"><span class="glyph-icon icon-sort-numeric-asc"></span>&nbsp;&nbsp;执行序列</label>
-                            <div class="col-sm-6">
-                                <input id="sequence" title="执行序列" class="form-control" type="text" value="1" name="sequence">
-                            </div>
-                        </div>
-                        <div id="payload_panel" class="form-group">
-                        </div>
-                        <div class="form-group">
-                            <div class="col-sm-4"></div>
-                            <div class="col-sm-6 pull-right">
-                                <button type="submit" class="btn btn-blue-alt" style="width: 180px; height: 40px; font-size: 16px;" disabled>创建</button>
-                                <button class="btn btn-default" style="width: 64px; height: 40px; font-size: 16px;" data-dismiss="modal">取消</button>
-                            </div>
-                        </div>
-                    </form>
-                </div>
-            </div>
-        </div>
-    </div>
-</div>
-
-<div class="modal" id="update_kind_modal" tabindex="-1" role="dialog" style="margin-top: 100px;">
-    <div class="modal-dialog">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h4 class="modal-title">变更操作类型:</h4>
-            </div>
-            <div class="modal-body" style="padding-top: 0;">
-                <form class="form-horizontal bordered-row">
-                    <div class="form-group">
-                        <div class="col-sm-1"></div>
-                        <label class="col-sm-3 control-label"><span class="glyph-icon icon-elusive-compass-circled"></span>&nbsp;&nbsp;标识</label>
-                        <div class="col-sm-8">
-                            <h3 style="color: orangered;" id="update_kind_instance_desc"></h3>
-                            <h6 id="update_kind_instance_desc_sub"></h6>
-                        </div>
-                    </div>
-                    <div class="form-group">
-                        <div class="col-sm-1"></div>
-                        <label class="col-sm-3 control-label"><span class="glyph-icon icon-th-list"></span>&nbsp;&nbsp;操作细则类型</label>
-                        <div class="col-sm-8">
-                            <select id="update_kind" name="use_for" title="操作类型" class="selectpicker">
-                                <option value="0" data-subtext="系统启动前,通过 guestfish 所执行的命令" selected>命令</option>
-                                <option value="1" data-subtext="系统启动前,通过 guestfish 替换目标文件中的内容">覆写文件</option>
-                                <option value="2" data-subtext="系统启动前,通过 guestfish 向目标文件追加内容">追加内容</option>
-                            </select>
-                        </div>
-                    </div>
-                </form>
-            </div>
-            <div class="modal-footer">
-                <button type="button" class="btn btn-sm btn-primary" onclick="update_kind_at();">确定</button>
-                <button type="button" class="btn btn-sm btn-default" data-dismiss="modal">取消</button>
-            </div>
-        </div>
-    </div>
-</div>
-
-<div class="modal" id="update_sequence_modal" tabindex="-1" role="dialog" style="margin-top: 100px;">
-    <div class="modal-dialog">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h4 class="modal-title">变更操作细则执行序列:</h4>
-            </div>
-            <div class="modal-body" style="padding-top: 0;">
-                <form class="form-horizontal bordered-row">
-                    <div class="form-group">
-                        <div class="col-sm-1"></div>
-                        <label class="col-sm-3 control-label"><span class="glyph-icon icon-elusive-compass-circled"></span>&nbsp;&nbsp;标识</label>
-                        <div class="col-sm-8">
-                            <h3 style="color: orangered;" id="update_sequence_instance_desc"></h3>
-                            <h6 id="update_sequence_instance_desc_sub"></h6>
-                        </div>
-                    </div>
-                    <div class="form-group">
-                        <div class="col-sm-1"></div>
-                        <label class="col-sm-3 control-label"><span class="glyph-icon icon-sort-numeric-asc"></span>&nbsp;&nbsp;执行序列</label>
-                        <div class="col-sm-8">
-                            <input id="update_sequence" title="执行序列" class="form-control" type="text" value="1" name="update_sequence">
-                        </div>
-                    </div>
-                </form>
-            </div>
-            <div class="modal-footer">
-                <button type="button" class="btn btn-sm btn-primary" onclick="update_sequence_at();">确定</button>
-                <button type="button" class="btn btn-sm btn-default" data-dismiss="modal">取消</button>
-            </div>
-        </div>
-    </div>
-</div>
-{% endblock content %}

+ 0 - 880
templates/os_templates_show.html

@@ -1,880 +0,0 @@
-{% extends "layout.html" %}
-{% block head %}
-    {{ super() }}
-    <style type="text/css">
-
-        @media (min-width: 768px) {
-            .form-horizontal .control-label {
-                text-align: left;
-            }
-        }
-
-        label>span {
-            color: deepskyblue;
-        }
-
-        .table {
-            font-size: 12px;
-            border-width: 1px;
-            line-height: 20px;
-        }
-        .table > thead > tr > th,
-        .table > tfoot > tr > th {
-            color: #999999;
-            font-weight: normal;
-            border-bottom: 0 solid #e1e6eb;
-            background-color: #F5F6FA;
-        }
-        
-        .table > tbody > tr > td {
-            color: #424547;
-        }
-
-        .table-bordered > tbody > tr {
-            padding-top: 10px;
-            padding-bottom: 10px;
-        }
-
-        .table-bordered > thead > tr > th,
-        .table-bordered > tbody > tr > th,
-        .table-bordered > tfoot > tr > th,
-        .table-bordered > thead > tr > td,
-        .table-bordered > tbody > tr > td,
-        .table-bordered > tfoot > tr > td {
-            border-style: solid;
-            border-width: 1px 0 0 0;
-        }
-
-        .btn,
-        .form-control,
-        .modal-content {
-            border-radius: 0 !important;
-        }
-
-        .btn-shortcut {
-            font-size: 12px !important;
-            padding: 0 26px;
-        }
-
-        .show {
-            display: inline-block !important;
-        }
-
-        .tr-selected td,
-        .tr-selected {
-            color: #000 !important;
-            background: #fafaff !important;
-        }
-    </style>
-
-{% endblock head %}
-{% block content %}
-
-<script type="text/javascript">
-    var page = 1;
-    var page_size = 10;
-    var keyword = '';
-    var resource_path = window.location.pathname;
-    var cur_url = resource_path;
-
-    $(document).ready(function() {
-        page_size = $('#page_size').val();
-        cur_url = resource_path + '?page=' + page + '&page_size=' + page_size;
-
-        var last_ready = null;
-        $('#content_search').keydown(function() {
-            if (last_ready !== null) {
-                clearTimeout(last_ready);
-            }
-            last_ready = setTimeout(function () {
-                keyword = $('#content_search').val();
-                cur_url = resource_path + '?page=' + page + '&page_size=' + page_size;
-                if (keyword.length > 0) {
-                    cur_url = resource_path + '?page=' + page + '&page_size=' + page_size + '&keyword=' + keyword;
-                }
-                window.location.href=cur_url;
-            }, 1000);
-        });
-
-        $('#page_size').change(function () {
-            keyword = $('#content_search').val();
-            page_size = $('#page_size').val();
-            cur_url = resource_path + '?page=' + page + '&page_size=' + page_size;
-            if (keyword.length > 0) {
-                cur_url = resource_path + '?page=' + page + '&page_size=' + page_size + '&keyword=' + keyword;
-            }
-            window.location.href=cur_url;
-        });
-
-        $("thead").on('click', ".all_selector", function() {
-            if ($("thead .all_selector").is(':checked')) {
-                $("tbody tr").find('td input[type="checkbox"]:eq(0)').prop('checked', true);
-                $(".all_selector").prop('checked', true);
-            } else {
-                $("tbody tr").find('td input[type="checkbox"]:eq(0)').prop('checked', false);
-                $(".all_selector").prop('checked', false);
-            }
-
-            select_item_action();
-        });
-
-        $("tfoot").on('click', ".all_selector", function() {
-            if ($("tfoot .all_selector").is(':checked')) {
-                $("tbody tr").find('td input[type="checkbox"]:eq(0)').prop('checked', true);
-                $(".all_selector").prop('checked', true);
-            } else {
-                $("tbody tr").find('td input[type="checkbox"]:eq(0)').prop('checked', false);
-                $(".all_selector").prop('checked', false);
-            }
-
-            select_item_action();
-        });
-
-        $("tbody tr").on('click', "input[type='checkbox']", function() {
-            select_item_action();
-        });
-
-        $('#edit_label_modal').on('show.bs.modal', function (me) {
-            $('#os_template_id').val($(me.relatedTarget).parent().parent().prev().prev().text());
-            $('#edit_label').val($(me.relatedTarget).prev().text());
-        });
-
-        $('#edit_path_modal').on('show.bs.modal', function (me) {
-            $('#os_template_id').val($(me.relatedTarget).parent().parent().prev().prev().prev().prev().text());
-            $('#edit_path').val($(me.relatedTarget).prev().text());
-        });
-
-        $('#add_os_template_modal').on('show.bs.modal', function (me) {
-            refresh_os_template_boot_job_selectpicker($('#boot_job_id'));
-        });
-
-        $('#update_boot_job_modal').on('show.bs.modal', function (me) {
-            refresh_os_template_boot_job_selectpicker($('#update_boot_job_id'));
-        });
-
-        $('#update_os_type_modal').on('show.bs.modal', function (me) {
-            $('#os_template_id').val($(me.relatedTarget).parent().prev().prev().prev().prev().prev().text());
-            $('#update_os_type_instance_desc').text($(me.relatedTarget).parent().prev().prev().prev().text());
-        });
-
-        $('#update_icon_modal').on('show.bs.modal', function (me) {
-            $('#os_template_id').val($(me.relatedTarget).parent().prev().prev().prev().prev().prev().prev().text());
-            $('#update_icon_instance_desc').text($(me.relatedTarget).parent().prev().prev().prev().prev().text());
-        });
-
-        $('#add_os_template_form').formValidation({
-            framework: 'bootstrap4',
-            icon: {
-                valid: 'fa fa-check',
-                invalid: 'fa fa-times',
-                validating: 'fa fa-refresh'
-            },
-            // Since the Bootstrap Button hides the radio and checkbox
-            // We exclude the disabled elements only
-            excluded: ':disabled',
-            locale: 'zh_CN',
-            fields: {
-                label: {
-                    validators: {
-                        notEmpty: {},
-                        stringLength: {
-                            min: 2,
-                            max: 255
-                        }
-                    }
-                },
-                icon: {
-                    validators: {
-                        notEmpty: {
-                        }
-                    }
-                },
-                path: {
-                    validators: {
-                        notEmpty: {},
-                        stringLength: {
-                            min: 2,
-                            max: 255
-                        }
-                    }
-                },
-                os_type: {
-                    validators: {
-                        notEmpty: {
-                        }
-                    }
-                },
-                boot_job_id: {
-                    validators: {
-                        notEmpty: {
-                        }
-                    }
-                }
-            }
-        })
-        .on('success.field.fv', function(e, data) {
-            if (data.fv.getInvalidFields().length > 0) {    // There is invalid field
-                data.fv.disableSubmitButtons(true);
-            }
-        })
-    });
-
-    function refresh() {
-        keyword = $('#content_search').val();
-        page = $('#pagination li.active a').text();
-        page_size = $('#page_size').val();
-        cur_url = resource_path + '?page=' + page + '&page_size=' + page_size;
-        if (keyword.length > 0) {
-            cur_url = resource_path + '?page=' + page + '&page_size=' + page_size + '&keyword=' + keyword;
-        }
-        window.location.href=cur_url;
-    }
-
-    function row_onmouseover(me) {
-        $(me).find(".edit_label_trigger, .edit_path_trigger").css('display','inline-flex');
-    }
-
-    function row_onmouseout(me) {
-        $(me).find(".edit_label_trigger, .edit_path_trigger").css('display','none');
-    }
-
-    function refresh_os_template_boot_job_selectpicker(selectpicker) {
-        $.ajax({
-            url : '/api/boot_jobs?filter=use_for:eq:0',
-            type : 'GET',
-            contentType: "application/json; charset=utf-8",
-            dataType: 'json',
-            error : function() {
-            },
-            success : function(data, textStatus, xhr) {
-                selectpicker.empty();
-                $.each(data.data, function(k, v) {
-                    selectpicker.append(
-                        $('<option>', {value: v['id'], text: v['name'], 'data-subtext': v['remark']})
-                    );
-                });
-                selectpicker.selectpicker('refresh');
-            }
-        });
-    }
-
-    function label_update(me) {
-        var os_template_id = $('#os_template_id').val();
-        var label = $('#edit_label').val();
-        $('#edit_label_modal').modal('hide');
-        $.ajax({
-            url : '/api/os_template/' + os_template_id,
-            type : 'PATCH',
-            contentType: "application/json; charset=utf-8",
-            data : JSON.stringify({
-                label: label
-            }),
-            error : function() {
-                alter_danger('模板名称更新失败!');
-            },
-            success : function() {
-                alter_success('模板名称更新成功!');
-                refresh()
-            }
-        });
-    }
-
-    function path_update(me) {
-        var os_template_id = $('#os_template_id').val();
-        var path = $('#edit_path').val();
-        $('#edit_path_modal').modal('hide');
-        $.ajax({
-            url : '/api/os_template/' + os_template_id,
-            type : 'PATCH',
-            contentType: "application/json; charset=utf-8",
-            data : JSON.stringify({
-                path: path
-            }),
-            error : function() {
-                alter_danger('模板路径更新失败!');
-            },
-            success : function() {
-                alter_success('模板路径更新成功!');
-                refresh()
-            }
-        });
-    }
-
-    function get_selected_element(checked) {
-        if (checked === null) {
-            checked = true;
-        }
-
-        if (checked) {
-            return $('tbody :checked');
-        } else {
-
-            return $('tbody input:not(:checked)');
-        }
-    }
-    
-    function highlight_selected_element() {
-        var selected_element = get_selected_element(true);
-        var no_selected_element = get_selected_element(false);
-
-        selected_element.each(function(i, e) {
-            $(e).parent().parent().toggleClass('tr-selected', true);
-        });
-
-        no_selected_element.each(function(i, e) {
-            $(e).parent().parent().toggleClass('tr-selected', false);
-        });
-    }
-    
-    function shortcut_bar_enable() {
-        var selected_element = get_selected_element(true);
-
-        if (selected_element.length > 0) {
-            $('.btn-shortcut').toggleClass('disabled', false);
-        } else {
-            $('.btn-shortcut').toggleClass('disabled', true);
-        }
-    }
-
-    function select_item_action() {
-        highlight_selected_element();
-        shortcut_bar_enable();
-    }
-
-    function enable(id) {
-        $.ajax({
-            url : '/api/os_template/' + id,
-            type : 'PATCH',
-            contentType: "application/json; charset=utf-8",
-            data : JSON.stringify({
-                active: true
-            }),
-            error : function() {
-                alter_danger('启用指令发送失败!');
-            },
-            success : function() {
-                alter_success('启用指令发送成功!');
-                refresh();
-            }
-        });
-    }
-
-    function disable(id) {
-        $.ajax({
-            url : '/api/os_template/' + id,
-            type : 'PATCH',
-            contentType: "application/json; charset=utf-8",
-            data : JSON.stringify({
-                active: false
-            }),
-            error : function() {
-                alter_danger('禁用指令发送失败!');
-            },
-            success : function() {
-                alter_success('禁用指令发送成功!');
-                refresh();
-            }
-        });
-    }
-
-    function remove(id) {
-        $.ajax({
-            url : '/api/os_templates/' + id,
-            type : 'DELETE',
-            contentType: "application/json; charset=utf-8",
-            error : function() {
-                alter_danger('模板删除指令发送失败!');
-            },
-            success : function() {
-                alter_success('模板删除指令发送成功!');
-                refresh();
-            }
-        });
-    }
-
-    function update_boot_job(id) {
-        $.ajax({
-            url : '/api/os_template/' + id,
-            type : 'PATCH',
-            contentType: "application/json; charset=utf-8",
-            data : JSON.stringify({
-                boot_job_id: parseInt($('#update_boot_job_id').val())
-            }),
-            error : function() {
-                alter_danger('变更初始化作业指令发送失败!');
-            },
-            success : function() {
-                alter_success('变更初始化作业指令发送成功!');
-                refresh();
-            }
-        });
-    }
-
-    function update_os_type(id) {
-        $.ajax({
-            url : '/api/os_template/' + id,
-            type : 'PATCH',
-            contentType: "application/json; charset=utf-8",
-            data : JSON.stringify({
-                os_type: parseInt($('#update_os_type').val())
-            }),
-            error : function() {
-                alter_danger('变更操作系统类型指令发送失败!');
-            },
-            success : function() {
-                alter_success('变更操作系统类型指令发送成功!');
-                refresh();
-            }
-        });
-    }
-
-    function update_icon(id) {
-        $.ajax({
-            url : '/api/os_template/' + id,
-            type : 'PATCH',
-            contentType: "application/json; charset=utf-8",
-            data : JSON.stringify({
-                icon: $('#update_icon').val()
-            }),
-            error : function() {
-                alter_danger('变更 ICON 指令发送失败!');
-            },
-            success : function() {
-                alter_success('变更 ICON 指令发送成功!');
-                refresh();
-            }
-        });
-    }
-
-    function enable_at(me) {
-        var id = $(me).parent().parent().parent().parent().parent().children()[0].textContent;
-        enable(id);
-    }
-
-    function disable_at(me) {
-        var id = $(me).parent().parent().parent().parent().parent().children()[0].textContent;
-        disable(id);
-    }
-
-    function delete_at(me) {
-        var id = $('#os_template_id').val();
-        remove(id);
-        $('#delete_modal').modal('hide');
-    }
-
-    function update_boot_job_at(me) {
-        var id = $('#os_template_id').val();
-        update_boot_job(id);
-        $('#update_boot_job_modal').modal('hide');
-    }
-
-    function update_os_type_at(me) {
-        var id = $('#os_template_id').val();
-        update_os_type(id);
-        $('#update_os_type_modal').modal('hide');
-    }
-
-    function update_icon_at(me) {
-        var id = $('#os_template_id').val();
-        update_icon(id);
-        $('#update_icon_modal').modal('hide');
-    }
-</script>
-<div class="panel">
-    <div class="panel-body">
-        <h3 class="title-hero" style="font-size: 24px;">
-            虚拟机模板
-        </h3>
-        <div>
-            <div id="datatable-row-highlight_wrapper" class="dataTables_wrapper form-inline">
-                <div class="row" style="padding: 10px 10px 10px 0; width: 100%;">
-                    <div class="col-sm-12" style="padding-right: 0;">
-                        <div id="datatable-row-highlight_filter" class="dataTables_filter" style="display: inline-block;">
-                            <input id="content_search" type="search" class="form-control" placeholder="模糊搜索..." value="{%- if keyword -%} {{ keyword }} {%- endif -%}" style="margin-left: 0; border-radius: 0;">
-                        </div>
-                        <div class="pull-right">
-                            <button class="btn btn-default" onclick="refresh()" style="border-radius: 0;"><span class="glyph-icon icon-elusive-arrows-cw"></span></button>
-                            <a class="btn btn-info" href="javascript:;" data-toggle="modal" data-target="#add_os_template_modal" style="border-radius: 0; padding-left: 40px; padding-right: 40px;">添加模板</a>
-                        </div>
-                    </div>
-                </div>
-                <table id="os_template_list" class="table table-bordered table-hover" cellspacing="0" width="100%" role="grid"
-                       style="width: 100%; margin-bottom: 0; border-bottom-width: 0;">
-                <thead>
-                <tr role="row">
-                    <th style="display: none;">ID</th>
-                    <th><input class="all_selector" title="选取所有" type="checkbox"></th>
-                    <th width="180px;">名称</th>
-                    <th>状态</th>
-                    <th width="500px;">路径</th>
-                    <th>操作系统类型</th>
-                    <th>ICON</th>
-                    <th>初始化作业</th>
-                    <th>操作</th>
-                </tr>
-                </thead>
-                <tbody>
-                {% for item in os_templates_ret.data %}
-                <tr role="row" class="odd" onmouseover="row_onmouseover(this);" onmouseout="row_onmouseout(this);">
-                    <td style="display: none;">{{ item.id }}</td>
-                    <td><input title="选中" type="checkbox"></td>
-                    <td>
-                        <div>
-                            <p style="display: inline-block;">{{ item.label }}</p>
-                            <a href="javascript:;" class="edit_label_trigger" data-toggle="modal" data-target="#edit_label_modal" style="display: none; float: right;">
-                                <span class="glyph-icon icon-elusive-pencil" style="width: 20px; height: 20px; margin-left: 10px; border-radius: 0; border: 1px solid rgb(220, 233, 255); background-color: #ffffff;"></span>
-                            </a>
-                        </div>
-                    </td>
-                    <td>{% if item.active == 0 %}
-                        <span style="color: #990000;">未启用</span>
-                        {% else %}
-                        <span style="color: #00BB00;">启用</span>
-                        {% endif %}
-                    </td>
-                    <td>
-                        <div>
-                            <p style="display: inline-block;">{{ item.path }}</p>
-                            <a href="javascript:;" class="edit_path_trigger" data-toggle="modal" data-target="#edit_path_modal" style="display: none; float: right;">
-                                <span class="glyph-icon icon-elusive-pencil" style="width: 20px; height: 20px; margin-left: 10px; border-radius: 0; border: 1px solid rgb(220, 233, 255); background-color: #ffffff;"></span>
-                            </a>
-                        </div>
-                    </td>
-                    <td>
-                        <a href="javascript:;" data-toggle="modal" data-target="#update_os_type_modal">
-                            {{ item.os_type }}
-                        </a>
-                    </td>
-                    <td>
-                        <a href="javascript:;" data-toggle="modal" data-target="#update_icon_modal">
-                            <span class="{{ item.icon }}"></span>
-                        </a>
-                    </td>
-                    <td>
-                        <a href="/boot_jobs?boot_job_id={{ item.boot_job_id }}">
-                            {{ boot_jobs_mapping_by_id[item.boot_job_id].name }}
-                        </a>
-                    </td>
-                    <td>
-                        <div class="dropdown inline-block">
-                            <a href="javascript:;" class="dropdown-toggle" data-toggle="dropdown">
-                                更多
-                            </a>
-                            <ul class="dropdown-menu">
-                                <li class="{% if item.active == true %} disabled {% endif %}" style="{% if item.sequence == 0 %} display: none; {% endif %}">
-                                    <a href="javascript:;" onclick="enable_at(this);">
-                                        启用
-                                    </a>
-                                </li>
-                                <li class="{% if item.active != true %} disabled {% endif %}" style="{% if item.sequence == 0 %} display: none; {% endif %}">
-                                    <a href="javascript:;" onclick="disable_at(this);">
-                                        禁用
-                                    </a>
-                                </li>
-                                <li class="divider" style="{% if item.sequence == 0 %} display: none; {% endif %}"></li>
-                                <li>
-                                    <a href="javascript:;" data-toggle="modal" data-target="#update_boot_job_modal"
-                                        onclick="$('#os_template_id').val($(this).parent().parent().parent().parent().parent().children()[0].textContent);
-                                        $('#update_boot_job_instance_desc').text($(this).parent().parent().parent().parent().parent().children()[2].textContent)">
-                                        变更初始化作业
-                                    </a>
-                                </li>
-                                <li class="divider" style="{% if item.sequence == 0 %} display: none; {% endif %}"></li>
-                                <li>
-                                    <a href="javascript:;" data-toggle="modal" data-target="#delete_modal"
-                                       onclick="$('#os_template_id').val($(this).parent().parent().parent().parent().parent().children()[0].textContent);
-                                       $('#delete_instance_desc').text($(this).parent().parent().parent().parent().parent().children()[2].textContent)">
-                                        删除
-                                    </a>
-                                </li>
-                            </ul>
-                        </div>
-                    </td>
-                </tr>
-                {% endfor %}
-                </tbody>
-                </table>
-
-                <table class="table table-bordered" style="border-top-width: 0; z-index: 99; position: sticky; bottom: 0;">
-                    <tfoot>
-                    <tr style="height: 70px;">
-                        <th><input type="checkbox" title="选取所有" class="all_selector"></th>
-                        <th>
-                            <div class="row">
-                                <div class="col-sm-6">
-                                </div>
-                                <div class="col-sm-3" style="font-size: 12px; padding-top: 5px; text-align: right;">
-                                    共有{{ os_templates_ret.paging.total }}条,每页显示:
-                                    <select id="page_size" name="datatable-row-highlight_length" title="page_size" class="form-control" style="height: 22px; vertical-align: baseline;">
-                                        <option value="10" {% if page_size == 10 %} selected {% endif %}>10</option>
-                                        <option value="20" {% if page_size == 20 %} selected {% endif %}>20</option>
-                                        <option value="50" {% if page_size == 50  %} selected {% endif %}>50</option>
-                                    </select>&nbsp;&nbsp;条
-                                </div>
-                                <div class="col-sm-3" style="text-align: left;">
-                                    <div class="dataTables_paginate paging_bootstrap" id="datatable-row-highlight_paginate">
-                                        <ul id="pagination" class="pagination">
-                                            <li class="{% if page == 1 %} disabled {% endif %}">
-                                                <a href="{{ resource_path }}?page={{ page - 1 }}&page_size={{ page_size }}{% if keyword %}&keyword={{ keyword }}{% endif %}{% if order_by %}&order_by={{ order_by }}{% endif %}{% if order %}&order={{ order }}{% endif %}">«</a>
-                                            </li>
-                                            {% for item in pages %}
-                                            <li class="{% if item == page %} active {% endif %}">
-                                                <a href="{{ resource_path }}?page={{ item }}&page_size={{ page_size }}{% if keyword %}&keyword={{ keyword }}{% endif %}{% if order_by %}&order_by={{ order_by }}{% endif %}{% if order %}&order={{ order }}{% endif %}">{{ item }}</a>
-                                            </li>
-                                            {% endfor %}
-                                            <li class="{% if page == last_page %} disabled {% endif %}">
-                                                <a href="{{ resource_path }}?page={{ page + 1 }}&page_size={{ page_size }}{% if keyword %}&keyword={{ keyword }}{% endif %}{% if order_by %}&order_by={{ order_by }}{% endif %}{% if order %}&order={{ order }}{% endif %}">»</a>
-                                            </li>
-                                        </ul>
-                                    </div>
-                                </div>
-                            </div>
-                        </th>
-                    </tr>
-                    </tfoot>
-                </table>
-            </div>
-        </div>
-    </div>
-</div>
-
-<input id="os_template_id" title="模板 ID" class="form-control" name="os_template_id" hidden>
-
-<div class="modal" id="edit_label_modal" tabindex="-1" role="dialog" style="margin-top: 100px;">
-    <div class="modal-dialog modal-sm">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h4 class="modal-title">编辑模板名称:</h4>
-            </div>
-            <div class="modal-body">
-                <input id="edit_label" title="系统模板名称" class="form-control" name="os_template_label">
-            </div>
-            <div class="modal-footer">
-                <button type="button" class="btn btn-sm btn-primary" onclick="label_update();">确定</button>
-                <button type="button" class="btn btn-sm btn-default" data-dismiss="modal">取消</button>
-            </div>
-        </div>
-    </div>
-</div>
-
-<div class="modal" id="edit_path_modal" tabindex="-1" role="dialog" style="margin-top: 100px;">
-    <div class="modal-dialog">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h4 class="modal-title">编辑模板路径:</h4>
-            </div>
-            <div class="modal-body">
-                <input id="edit_path" title="系统模板路径" class="form-control" name="os_template_path">
-            </div>
-            <div class="modal-footer">
-                <button type="button" class="btn btn-sm btn-primary" onclick="path_update();">确定</button>
-                <button type="button" class="btn btn-sm btn-default" data-dismiss="modal">取消</button>
-            </div>
-        </div>
-    </div>
-</div>
-
-<div class="modal" id="delete_modal" tabindex="-1" role="dialog" style="margin-top: 100px;">
-    <div class="modal-dialog">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h4 class="modal-title">删除模板:</h4>
-            </div>
-            <div class="modal-body">
-                <p>你确定要删除该模板吗?</p>
-                <h3 style="color: orangered;" id="delete_instance_desc"></h3>
-            </div>
-            <div class="modal-footer">
-                <button type="button" class="btn btn-sm btn-primary" onclick="delete_at();">确定</button>
-                <button type="button" class="btn btn-sm btn-default" data-dismiss="modal">取消</button>
-            </div>
-        </div>
-    </div>
-</div>
-
-<div class="modal" id="add_os_template_modal" tabindex="-1" role="dialog" style="margin-top: 100px;">
-    <div class="modal-dialog modal-lg">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h4 class="modal-title">添加模板:</h4>
-            </div>
-            <div class="modal-body" style="padding-top: 0; padding-bottom: 0;">
-                <div class="example-box-wrapper">
-                    <form id="add_os_template_form" class="form-horizontal bordered-row" action="/os_template" method="post">
-                        <div class="form-group">
-                            <div class="col-sm-2"></div>
-                            <label class="col-sm-2 control-label"><span class="glyph-icon icon-elusive-compass-circled"></span>&nbsp;&nbsp;模板名称</label>
-                            <div class="col-sm-6">
-                                <input id="label" name="label" type="text" title="模板名称" class="form-control">
-                            </div>
-                        </div>
-                        <div class="form-group">
-                            <div class="col-sm-2"></div>
-                            <label class="col-sm-2 control-label"><span class="glyph-icon icon-bookmark-o"></span>&nbsp;&nbsp;ICON</label>
-                            <div class="col-sm-6">
-                                <select id="icon" name="icon" title="模板 ICON" class="selectpicker">
-                                    <option value="icon-os icon-os-centos" data-icon="icon-os icon-os-centos" selected>CentOS</option>
-                                    <option value="icon-os icon-os-linux" data-icon="icon-os icon-os-linux">Linux</option>
-                                    <option value="icon-os icon-os-ubuntu" data-icon="icon-os icon-os-ubuntu">Ubuntu</option>
-                                    <option value="icon-os icon-os-suse" data-icon="icon-os icon-os-suse">Suse</option>
-                                    <option value="icon-os icon-os-gentoo" data-icon="icon-os icon-os-gentoo">Gentoo</option>
-                                    <option value="icon-os icon-os-debian" data-icon="icon-os icon-os-debian">Debian</option>
-                                    <option value="icon-os icon-os-redhat" data-icon="icon-os icon-os-redhat">Redhat</option>
-                                    <option value="icon-os icon-os-bsd" data-icon="icon-os icon-os-bsd">BSD</option>
-                                    <option value="icon-os icon-os-windows" data-icon="icon-os icon-os-windows">Windows</option>
-                                </select>
-                            </div>
-                        </div>
-                        <div class="form-group">
-                            <div class="col-sm-2"></div>
-                            <label class="col-sm-2 control-label"><span class="glyph-icon icon-elusive-stumbleupon"></span>&nbsp;&nbsp;模板路径</label>
-                            <div class="col-sm-6">
-                                <input id="path" name="path" type="text" title="模板路径" class="form-control">
-                            </div>
-                        </div>
-                        <div class="form-group">
-                            <div class="col-sm-2"></div>
-                            <label class="col-sm-2 control-label"><span class="glyph-icon icon-bookmark-o"></span>&nbsp;&nbsp;模板系统类型</label>
-                            <div class="col-sm-6">
-                                <select id="os_type" name="os_type" title="请选择模板系统类型" class="selectpicker">
-                                    <option value="0">Linux</option>
-                                    <option value="1">Windows</option>
-                                    <option value="2">BSD</option>
-                                    <option value="3">AIX</option>
-                                    <option value="4">HP-UNIX</option>
-                                </select>
-                            </div>
-                        </div>
-                        <div class="form-group">
-                            <div class="col-sm-2"></div>
-                            <label class="col-sm-2 control-label"><span class="glyph-icon icon-file-code-o"></span>&nbsp;&nbsp;初始化作业</label>
-                            <div class="col-sm-6">
-                                <select id="boot_job_id" name="boot_job_id" title="实例的启动作业" class="selectpicker">
-                                </select>
-                            </div>
-                        </div>
-                        <div class="form-group">
-                            <div class="col-sm-4"></div>
-                            <div class="col-sm-6 pull-right">
-                                <button type="submit" class="btn btn-blue-alt" style="width: 180px; height: 40px; font-size: 16px;" disabled>创建</button>
-                                <button class="btn btn-default" style="width: 64px; height: 40px; font-size: 16px;" data-dismiss="modal">取消</button>
-                            </div>
-                        </div>
-                    </form>
-                </div>
-            </div>
-        </div>
-    </div>
-</div>
-
-<div class="modal" id="update_boot_job_modal" tabindex="-1" role="dialog" style="margin-top: 100px;">
-    <div class="modal-dialog">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h4 class="modal-title">变更初始化作业:</h4>
-            </div>
-            <div class="modal-body" style="padding-top: 0;">
-                <form class="form-horizontal bordered-row">
-                    <div class="form-group">
-                        <div class="col-sm-1"></div>
-                        <label class="col-sm-3 control-label"><span class="glyph-icon icon-elusive-compass-circled"></span>&nbsp;&nbsp;模板名称</label>
-                        <div class="col-sm-8">
-                            <h3 style="color: orangered;" id="update_boot_job_instance_desc"></h3>
-                        </div>
-                    </div>
-                    <div class="form-group">
-                        <div class="col-sm-1"></div>
-                        <label class="col-sm-3 control-label"><span class="glyph-icon icon-file-code-o"></span>&nbsp;&nbsp;初始化作业</label>
-                        <div class="col-sm-8">
-                            <select id="update_boot_job_id" name="boot_job_id" title="实例的启动作业" class="selectpicker">
-                            </select>
-                        </div>
-                    </div>
-                </form>
-            </div>
-            <div class="modal-footer">
-                <button type="button" class="btn btn-sm btn-primary" onclick="update_boot_job_at();">确定</button>
-                <button type="button" class="btn btn-sm btn-default" data-dismiss="modal">取消</button>
-            </div>
-        </div>
-    </div>
-</div>
-
-<div class="modal" id="update_os_type_modal" tabindex="-1" role="dialog" style="margin-top: 100px;">
-    <div class="modal-dialog">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h4 class="modal-title">变更操作系统类型:</h4>
-            </div>
-            <div class="modal-body" style="padding-top: 0;">
-                <form class="form-horizontal bordered-row">
-                    <div class="form-group">
-                        <div class="col-sm-1"></div>
-                        <label class="col-sm-3 control-label"><span class="glyph-icon icon-elusive-compass-circled"></span>&nbsp;&nbsp;操作系统类型</label>
-                        <div class="col-sm-8">
-                            <h3 style="color: orangered;" id="update_os_type_instance_desc"></h3>
-                        </div>
-                    </div>
-                    <div class="form-group">
-                        <div class="col-sm-1"></div>
-                        <div class="col-sm-8">
-                            <select id="update_os_type" name="os_type" title="操作系统类型" class="selectpicker">
-                                <option value="0" selected>Linux</option>
-                                <option value="1">Windows</option>
-                                <option value="2">BSD</option>
-                                <option value="3">AIX</option>
-                                <option value="4">HP-UNIX</option>
-                            </select>
-                        </div>
-                    </div>
-                </form>
-            </div>
-            <div class="modal-footer">
-                <button type="button" class="btn btn-sm btn-primary" onclick="update_os_type_at();">确定</button>
-                <button type="button" class="btn btn-sm btn-default" data-dismiss="modal">取消</button>
-            </div>
-        </div>
-    </div>
-</div>
-
-<div class="modal" id="update_icon_modal" tabindex="-1" role="dialog" style="margin-top: 100px;">
-    <div class="modal-dialog">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h4 class="modal-title">变更 ICON:</h4>
-            </div>
-            <div class="modal-body" style="padding-top: 0;">
-                <form class="form-horizontal bordered-row">
-                    <div class="form-group">
-                        <div class="col-sm-1"></div>
-                        <label class="col-sm-3 control-label"><span class="glyph-icon icon-elusive-compass-circled"></span>&nbsp;&nbsp;模板名称</label>
-                        <div class="col-sm-8">
-                            <h3 style="color: orangered;" id="update_icon_instance_desc"></h3>
-                        </div>
-                    </div>
-                    <div class="form-group">
-                        <div class="col-sm-1"></div>
-                        <div class="col-sm-8">
-                            <select id="update_icon" name="icon" title="模板 ICON" class="selectpicker">
-                                <option value="icon-os icon-os-centos" data-icon="icon-os icon-os-centos" selected>CentOS</option>
-                                <option value="icon-os icon-os-linux" data-icon="icon-os icon-os-linux">Linux</option>
-                                <option value="icon-os icon-os-ubuntu" data-icon="icon-os icon-os-ubuntu">Ubuntu</option>
-                                <option value="icon-os icon-os-suse" data-icon="icon-os icon-os-suse">Suse</option>
-                                <option value="icon-os icon-os-gentoo" data-icon="icon-os icon-os-gentoo">Gentoo</option>
-                                <option value="icon-os icon-os-debian" data-icon="icon-os icon-os-debian">Debian</option>
-                                <option value="icon-os icon-os-redhat" data-icon="icon-os icon-os-redhat">Redhat</option>
-                                <option value="icon-os icon-os-bsd" data-icon="icon-os icon-os-bsd">BSD</option>
-                                <option value="icon-os icon-os-windows" data-icon="icon-os icon-os-windows">Windows</option>
-                            </select>
-                        </div>
-                    </div>
-                </form>
-            </div>
-            <div class="modal-footer">
-                <button type="button" class="btn btn-sm btn-primary" onclick="update_icon_at();">确定</button>
-                <button type="button" class="btn btn-sm btn-default" data-dismiss="modal">取消</button>
-            </div>
-        </div>
-    </div>
-</div>
-{% endblock content %}

+ 1 - 1
tests/test_os_template.py

@@ -16,7 +16,7 @@ __copyright__ = '(c) 2017 by James Iter.'
 class TestOSTemplate(unittest.TestCase):
 
     base_url = 'http://127.0.0.1:8008/api'
-    os_template_id = 0
+    os_template_image_id = 0
 
     def setUp(self):
         pass

+ 0 - 133
views/boot_job.py

@@ -1,133 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-
-import json
-from flask import Blueprint, render_template, url_for, request, redirect
-import requests
-from math import ceil
-
-from models.status import BootJobUseFor
-
-__author__ = 'James Iter'
-__date__ = '2017/7/15'
-__contact__ = 'james.iter.cn@gmail.com'
-__copyright__ = '(c) 2017 by James Iter.'
-
-
-blueprint = Blueprint(
-    'v_boot_job',
-    __name__,
-    url_prefix='/boot_job'
-)
-
-blueprints = Blueprint(
-    'v_boot_jobs',
-    __name__,
-    url_prefix='/boot_jobs'
-)
-
-
-def show():
-    args = list()
-    page = int(request.args.get('page', 1))
-    page_size = int(request.args.get('page_size', 10))
-    keyword = request.args.get('keyword', None)
-    boot_job_id = request.args.get('boot_job_id', None)
-    order_by = request.args.get('order_by', None)
-    order = request.args.get('order', None)
-    filters = list()
-    resource_path = request.path
-
-    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 boot_job_id is not None:
-        filters.append('id:in:' + boot_job_id.__str__())
-
-    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))
-
-    host_url = request.host_url.rstrip('/')
-
-    boot_jobs_url = host_url + url_for('api_boot_jobs.r_get_by_filter')
-    if keyword is not None:
-        boot_jobs_url = host_url + url_for('api_boot_jobs.r_content_search')
-
-    if args.__len__() > 0:
-        boot_jobs_url = boot_jobs_url + '?' + '&'.join(args)
-
-    boot_jobs_ret = requests.get(url=boot_jobs_url, cookies=request.cookies)
-    boot_jobs_ret = json.loads(boot_jobs_ret.content)
-
-    for i, boot_job in enumerate(boot_jobs_ret['data']):
-        # 去除系统级的启动作业。系统级的启动作业不展示给使用者。
-        if boot_job['use_for'] == BootJobUseFor.system.value:
-            del boot_jobs_ret['data'][i]
-
-    last_page = int(ceil(boot_jobs_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
-
-    return render_template('boot_jobs_show.html', boot_jobs_ret=boot_jobs_ret,
-                           resource_path=resource_path,
-                           page=page, page_size=page_size, keyword=keyword, pages=pages, order_by=order_by, order=order,
-                           last_page=last_page)
-
-
-def create():
-    host_url = request.host_url.rstrip('/')
-
-    if request.method == 'POST':
-        name = request.form.get('name')
-        use_for = request.form.get('use_for')
-        remark = request.form.get('remark')
-
-        payload = {
-            "name": name,
-            "use_for": int(use_for),
-            "remark": remark
-        }
-
-        url = host_url + '/api/boot_job'
-        headers = {'content-type': 'application/json'}
-        r = requests.post(url, data=json.dumps(payload), cookies=request.cookies, headers=headers)
-        j_r = json.loads(r.content)
-        return render_template('success.html', go_back_url='/boot_jobs', timeout=10000, title='提交成功',
-                               message_title='添加启动作业的请求已被接受',
-                               message='您所提交的启动作业已创建。页面将在10秒钟后自动跳转到模板列表页面!')
-
-    else:
-        return redirect(url_for('v_boot_jobs.show'))
-
-
-

+ 7 - 6
views/disk.py

@@ -208,7 +208,7 @@ def detail(uuid):
     disk_ret = json.loads(disk_ret.content)
 
     guest_ret = None
-    os_template_ret = None
+    os_template_image_ret = None
 
     config_url = host_url + url_for('api_config.r_get')
     config_ret = requests.get(url=config_url, cookies=request.cookies)
@@ -220,12 +220,13 @@ def detail(uuid):
         guest_ret = requests.get(url=guest_url, cookies=request.cookies)
         guest_ret = json.loads(guest_ret.content)
 
-        os_template_url = host_url + url_for('api_os_templates.r_get', ids=guest_ret['data']['os_template_id'].__str__())
+        os_template_image_url = host_url + url_for('api_os_templates_image.r_get',
+                                                   ids=guest_ret['data']['os_template_image_id'].__str__())
 
-        os_template_ret = requests.get(url=os_template_url, cookies=request.cookies)
-        os_template_ret = json.loads(os_template_ret.content)
+        os_template_image_ret = requests.get(url=os_template_image_url, cookies=request.cookies)
+        os_template_image_ret = json.loads(os_template_image_ret.content)
 
-    return render_template('disk_detail.html', uuid=uuid, guest_ret=guest_ret, os_template_ret=os_template_ret,
-                           disk_ret=disk_ret, config_ret=config_ret)
+    return render_template('disk_detail.html', uuid=uuid, guest_ret=guest_ret,
+                           os_template_image_ret=os_template_image_ret, disk_ret=disk_ret, config_ret=config_ret)
 
 

+ 33 - 156
views/guest.py

@@ -56,7 +56,8 @@ def show():
     if keyword is not None:
         guests_url = host_url + url_for('api_guests.r_content_search')
 
-    os_templates_url = host_url + url_for('api_os_templates.r_get_by_filter')
+    os_templates_image_url = host_url + url_for('api_os_templates_image.r_get_by_filter')
+    os_templates_profile_url = host_url + url_for('api_os_templates_profile.r_get_by_filter')
 
     if args.__len__() > 0:
         guests_url = guests_url + '?' + '&'.join(args)
@@ -71,30 +72,23 @@ def show():
     guests_ret = requests.get(url=guests_url, cookies=request.cookies)
     guests_ret = json.loads(guests_ret.content)
 
-    os_templates_ret = requests.get(url=os_templates_url, cookies=request.cookies)
-    os_templates_ret = json.loads(os_templates_ret.content)
-    os_templates_mapping_by_id = dict()
-    for os_template in os_templates_ret['data']:
-        os_templates_mapping_by_id[os_template['id']] = os_template
+    os_templates_image_ret = requests.get(url=os_templates_image_url, cookies=request.cookies)
+    os_templates_image_ret = json.loads(os_templates_image_ret.content)
+    os_templates_image_mapping_by_id = dict()
+    for os_template_image in os_templates_image_ret['data']:
+        os_templates_image_mapping_by_id[os_template_image['id']] = os_template_image
+
+    os_templates_profile_ret = requests.get(url=os_templates_profile_url, cookies=request.cookies)
+    os_templates_profile_ret = json.loads(os_templates_profile_ret.content)
+    os_templates_profile_mapping_by_id = dict()
+    for os_template_profile in os_templates_profile_ret['data']:
+        os_templates_profile_mapping_by_id[os_template_profile['id']] = os_template_profile
 
     guests_uuid = list()
 
     for guest in guests_ret['data']:
         guests_uuid.append(guest['uuid'])
 
-    guests_boot_jobs_ret = {'data': dict()}
-
-    if guests_uuid.__len__() > 0:
-        # 获取指定 Guest 的启动作业 ID
-        guests_boot_jobs_url = host_url + url_for('api_guests.r_get_boot_jobs', uuids=','.join(guests_uuid))
-        guests_boot_jobs_ret = requests.get(url=guests_boot_jobs_url, cookies=request.cookies)
-
-        guests_boot_jobs_ret = json.loads(guests_boot_jobs_ret.content)
-
-        # 统一单个、多个的返回JSON格式
-        if guests_uuid.__len__() == 1:
-            guests_boot_jobs_ret['data'] = {guests_uuid[0]: guests_boot_jobs_ret['data']}
-
     last_page = int(ceil(guests_ret['paging']['total'] / float(page_size)))
     page_length = 5
     pages = list()
@@ -117,9 +111,9 @@ def show():
                 break
 
     return render_template('guests_show.html', guests_ret=guests_ret, resource_path=resource_path,
-                           os_templates_mapping_by_id=os_templates_mapping_by_id,
-                           hosts_mapping_by_node_id=hosts_mapping_by_node_id,
-                           guests_boot_jobs_ret=guests_boot_jobs_ret, page=page,
+                           os_templates_image_mapping_by_id=os_templates_image_mapping_by_id,
+                           os_templates_profile_mapping_by_id=os_templates_profile_mapping_by_id,
+                           hosts_mapping_by_node_id=hosts_mapping_by_node_id, page=page,
                            page_size=page_size, keyword=keyword, pages=pages, last_page=last_page)
 
 
@@ -128,7 +122,7 @@ def create():
 
     if request.method == 'POST':
         ability = request.form.get('ability')
-        os_template_id = request.form.get('os_template_id')
+        os_template_image_id = request.form.get('os_template_image_id')
         quantity = request.form.get('quantity')
         password = request.form.get('password')
         remark = request.form.get('remark')
@@ -148,7 +142,7 @@ def create():
         payload = {
             "cpu": int(cpu),
             "memory": int(memory),
-            "os_template_id": int(os_template_id),
+            "os_template_image_id": int(os_template_image_id),
             "quantity": int(quantity),
             "remark": remark,
             "password": password,
@@ -183,16 +177,12 @@ def create():
                                message='您所提交的资源正在创建中。根据所提交资源的大小,需要等待几到十几分钟。页面将在10秒钟后自动跳转到实例列表页面!')
 
     else:
-        os_template_url = host_url + url_for('api_os_templates.r_get_by_filter')
         hosts_url = host_url + url_for('api_hosts.r_get_by_filter', alive=True)
 
-        os_template_ret = requests.get(url=os_template_url, cookies=request.cookies)
-        os_template_ret = json.loads(os_template_ret.content)
-
         hosts_ret = requests.get(url=hosts_url, cookies=request.cookies)
         hosts_ret = json.loads(hosts_ret.content)
 
-        return render_template('guest_create.html', os_template_ret=os_template_ret, hosts_ret=hosts_ret)
+        return render_template('guest_create.html', hosts_ret=hosts_ret)
 
 
 def port_is_opened(port):
@@ -251,10 +241,18 @@ def detail(uuid):
     guest_ret = requests.get(url=guest_url, cookies=request.cookies)
     guest_ret = json.loads(guest_ret.content)
 
-    os_template_url = host_url + url_for('api_os_templates.r_get', ids=guest_ret['data']['os_template_id'].__str__())
+    os_template_image_url = host_url + url_for('api_os_templates_image.r_get',
+                                               ids=guest_ret['data']['os_template_image_id'].__str__())
+    os_templates_profile_url = host_url + url_for('api_os_templates_profile.r_get_by_filter')
 
-    os_template_ret = requests.get(url=os_template_url, cookies=request.cookies)
-    os_template_ret = json.loads(os_template_ret.content)
+    os_template_image_ret = requests.get(url=os_template_image_url, cookies=request.cookies)
+    os_template_image_ret = json.loads(os_template_image_ret.content)
+
+    os_templates_profile_ret = requests.get(url=os_templates_profile_url, cookies=request.cookies)
+    os_templates_profile_ret = json.loads(os_templates_profile_ret.content)
+    os_templates_profile_mapping_by_id = dict()
+    for os_template_profile in os_templates_profile_ret['data']:
+        os_templates_profile_mapping_by_id[os_template_profile['id']] = os_template_profile
 
     disks_url = host_url + url_for('api_disks.r_get_by_filter', filter='guest_uuid:in:' + guest_ret['data']['uuid'])
     disks_ret = requests.get(url=disks_url, cookies=request.cookies)
@@ -264,7 +262,9 @@ def detail(uuid):
     config_ret = requests.get(url=url, cookies=request.cookies)
     config_ret = json.loads(config_ret.content)
 
-    return render_template('guest_detail.html', uuid=uuid, guest_ret=guest_ret, os_template_ret=os_template_ret,
+    return render_template('guest_detail.html', uuid=uuid, guest_ret=guest_ret,
+                           os_template_image_ret=os_template_image_ret,
+                           os_templates_profile_mapping_by_id=os_templates_profile_mapping_by_id,
                            hosts_mapping_by_node_id=hosts_mapping_by_node_id,
                            disks_ret=disks_ret, config_ret=config_ret)
 
@@ -275,126 +275,3 @@ def success():
                            message='您所提交的资源正在创建中。根据所提交资源的大小,需要等待几到十几分钟。页面将在10秒钟后自动跳转到实例列表页面!')
 
 
-def show_boot_job(uuid):
-    host_url = request.host_url.rstrip('/')
-
-    guest_url = host_url + url_for('api_guests.r_get', uuids=uuid)
-
-    guest_ret = requests.get(url=guest_url, cookies=request.cookies)
-    guest_ret = json.loads(guest_ret.content)
-
-    guest_boot_jobs_url = host_url + url_for('api_guests.r_get_boot_jobs', uuids=uuid)
-    guest_boot_jobs_ret = requests.get(url=guest_boot_jobs_url, cookies=request.cookies)
-    guest_boot_jobs_ret = json.loads(guest_boot_jobs_ret.content)
-
-    boot_jobs_url = host_url + url_for('api_boot_jobs.r_get_by_filter',
-                                       filter='id:in:' + ','.join(guest_boot_jobs_ret['data']['boot_jobs']))
-
-    boot_jobs_ret = requests.get(url=boot_jobs_url, cookies=request.cookies)
-    boot_jobs_ret = json.loads(boot_jobs_ret.content)
-    boot_jobs_mapping_by_id = dict()
-    for boot_job in boot_jobs_ret['data']:
-        boot_jobs_mapping_by_id[boot_job['id']] = boot_job
-
-    return render_template('guest_boot_jobs.html', uuid=uuid, guest_ret=guest_ret,
-                           guest_boot_jobs_ret=guest_boot_jobs_ret, boot_jobs_mapping_by_id=boot_jobs_mapping_by_id)
-
-
-def show_guests_boot_jobs():
-    page = int(request.args.get('page', 1))
-    page_size = int(request.args.get('page_size', 10))
-
-    host_url = request.host_url.rstrip('/')
-
-    # 获取所有有启动作业的 Guests uuid
-    uuids_of_all_had_boot_job_url = host_url + url_for('api_guests.r_get_uuids_of_all_had_boot_job')
-
-    uuids_of_all_had_boot_job_ret = requests.get(url=uuids_of_all_had_boot_job_url, cookies=request.cookies)
-    uuids_of_all_had_boot_job_ret = json.loads(uuids_of_all_had_boot_job_ret.content)
-    guests_uuid = uuids_of_all_had_boot_job_ret['data']
-
-    guests_boot_jobs_ret = {'data': dict()}
-
-    if guests_uuid.__len__() > 0:
-        # 获取指定 Guest 的启动作业 ID
-        guests_boot_jobs_url = host_url + url_for('api_guests.r_get_boot_jobs',
-                                                  uuids=','.join(guests_uuid[page_size * (page - 1): page_size * page]))
-        guests_boot_jobs_ret = requests.get(url=guests_boot_jobs_url, cookies=request.cookies)
-
-        guests_boot_jobs_ret = json.loads(guests_boot_jobs_ret.content)
-
-        # 统一单个、多个的返回JSON格式
-        if guests_uuid.__len__() == 1:
-            guests_boot_jobs_ret['data'] = {guests_uuid[0]: guests_boot_jobs_ret['data']}
-
-    # 取出 Guests 的启动作业 IDs,并去重
-    guests_boot_jobs_id = list()
-    for uuid, guest_boot_jobs in guests_boot_jobs_ret['data'].items():
-        guests_boot_jobs_id.extend(guest_boot_jobs['boot_jobs'])
-
-    guests_boot_jobs_id = list(set(guests_boot_jobs_id))
-
-    # 获取指定启动作业ID 的启动作业
-    boot_jobs_url = host_url + url_for('api_boot_jobs.r_get_by_filter',
-                                       filter='id:in:' + ','.join(guests_boot_jobs_id))
-
-    boot_jobs_ret = requests.get(url=boot_jobs_url, cookies=request.cookies)
-    boot_jobs_ret = json.loads(boot_jobs_ret.content)
-
-    # 启动作业 ID 与启动作业的映射
-    boot_jobs_mapping_by_id = dict()
-    for boot_job in boot_jobs_ret['data']:
-        boot_jobs_mapping_by_id[boot_job['id']] = boot_job
-
-    guests_url = host_url + url_for('api_guests.r_get_by_filter',
-                                    filter='uuid:in:' + ','.join(guests_uuid[page_size * (page - 1): page_size * page]))
-
-    guests_ret = requests.get(url=guests_url, cookies=request.cookies)
-    guests_ret = json.loads(guests_ret.content)
-
-    # Guest uuid 与 Guest 的映射
-    guests_mapping_by_uuid = dict()
-    for guest in guests_ret['data']:
-        guests_mapping_by_uuid[guest['uuid']] = guest
-
-    os_templates_url = host_url + url_for('api_os_templates.r_get_by_filter')
-    os_templates_ret = requests.get(url=os_templates_url, cookies=request.cookies)
-    os_templates_ret = json.loads(os_templates_ret.content)
-
-    # 模板与模板 ID 的映射
-    os_templates_mapping_by_id = dict()
-    for os_template in os_templates_ret['data']:
-        os_templates_mapping_by_id[os_template['id']] = os_template
-
-    paging_total = guests_uuid.__len__()
-
-    last_page = int(ceil(paging_total / float(page_size)))
-    if last_page == 0:
-        last_page = 1
-
-    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:
-                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:
-                break
-
-    return render_template('guests_boot_jobs.html', guests_boot_jobs_ret=guests_boot_jobs_ret,
-                           boot_jobs_mapping_by_id=boot_jobs_mapping_by_id,
-                           guests_mapping_by_uuid=guests_mapping_by_uuid,
-                           os_templates_mapping_by_id=os_templates_mapping_by_id, page=page, page_size=page_size,
-                           pages=pages, last_page=last_page, paging_total=paging_total)
-

+ 0 - 152
views/operate_rule.py

@@ -1,152 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-
-import json
-from flask import Blueprint, render_template, url_for, request, redirect
-import requests
-from math import ceil
-
-from models.status import OperateRuleKind, BootJobUseFor
-
-__author__ = 'James Iter'
-__date__ = '2017/7/17'
-__contact__ = 'james.iter.cn@gmail.com'
-__copyright__ = '(c) 2017 by James Iter.'
-
-
-blueprint = Blueprint(
-    'v_operate_rule',
-    __name__,
-    url_prefix='/operate_rule'
-)
-
-blueprints = Blueprint(
-    'v_operate_rules',
-    __name__,
-    url_prefix='/operate_rules'
-)
-
-
-def show():
-    args = list()
-    page = int(request.args.get('page', 1))
-    page_size = int(request.args.get('page_size', 10))
-    keyword = request.args.get('keyword', None)
-    boot_job_id = request.args.get('boot_job_id', None)
-    order_by = request.args.get('order_by', None)
-    order = request.args.get('order', None)
-    filters = list()
-    resource_path = request.path
-
-    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 boot_job_id is not None:
-        filters.append('boot_job_id:in:' + boot_job_id.__str__())
-
-    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))
-
-    host_url = request.host_url.rstrip('/')
-
-    operate_rules_url = host_url + url_for('api_operate_rules.r_get_by_filter')
-    if keyword is not None:
-        operate_rules_url = host_url + url_for('api_operate_rules.r_content_search')
-
-    boot_jobs_url = host_url + url_for('api_boot_jobs.r_get_by_filter')
-
-    if args.__len__() > 0:
-        operate_rules_url = operate_rules_url + '?' + '&'.join(args)
-
-    operate_rules_ret = requests.get(url=operate_rules_url, cookies=request.cookies)
-    operate_rules_ret = json.loads(operate_rules_ret.content)
-
-    system_level_ids = list()
-
-    boot_jobs_ret = requests.get(url=boot_jobs_url, cookies=request.cookies)
-    boot_jobs_ret = json.loads(boot_jobs_ret.content)
-    boot_jobs_mapping_by_id = dict()
-    for boot_job in boot_jobs_ret['data']:
-        boot_jobs_mapping_by_id[boot_job['id']] = boot_job
-
-        if boot_job['use_for'] == BootJobUseFor.system.value:
-            system_level_ids.append(boot_job['id'])
-
-    for i, operate_rule in enumerate(operate_rules_ret['data']):
-        # 去除系统级的启动作业。系统级的启动作业不展示给使用者。
-        if operate_rule['boot_job_id'] in system_level_ids:
-            del operate_rules_ret['data'][i]
-
-    last_page = int(ceil(operate_rules_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
-
-    return render_template('operate_rules_show.html', operate_rules_ret=operate_rules_ret,
-                           boot_jobs_mapping_by_id=boot_jobs_mapping_by_id, resource_path=resource_path,
-                           page=page, page_size=page_size, keyword=keyword, pages=pages, order_by=order_by, order=order,
-                           last_page=last_page)
-
-
-def create():
-    host_url = request.host_url.rstrip('/')
-
-    if request.method == 'POST':
-        boot_job_id = int(request.form.get('boot_job_id'))
-        kind = int(request.form.get('kind'))
-        sequence = int(request.form.get('sequence'))
-
-        payload = {
-            "boot_job_id": boot_job_id,
-            "kind": kind,
-            "sequence": sequence
-        }
-
-        if kind == OperateRuleKind.cmd.value:
-            payload['command'] = request.form.get('command', '')
-
-        else:
-            payload['path'] = request.form.get('path', '')
-            payload['content'] = request.form.get('content', '')
-
-        url = host_url + '/api/operate_rule'
-        headers = {'content-type': 'application/json'}
-        r = requests.post(url, data=json.dumps(payload), headers=headers, cookies=request.cookies)
-        j_r = json.loads(r.content)
-        return render_template('success.html', go_back_url='/operate_rules', timeout=10000, title='提交成功',
-                               message_title='添加启动作业操作细则的请求已被接受',
-                               message='您所提交的启动作业操作细则已创建。页面将在10秒钟后自动跳转到模板列表页面!')
-
-    else:
-        return redirect(url_for('v_operate_rules.show'))
-
-

+ 0 - 144
views/os_template.py

@@ -1,144 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-
-import json
-from flask import Blueprint, render_template, url_for, request, redirect
-import requests
-from math import ceil
-
-
-__author__ = 'James Iter'
-__date__ = '2017/7/11'
-__contact__ = 'james.iter.cn@gmail.com'
-__copyright__ = '(c) 2017 by James Iter.'
-
-
-blueprint = Blueprint(
-    'v_os_template',
-    __name__,
-    url_prefix='/os_template'
-)
-
-blueprints = Blueprint(
-    'v_os_templates',
-    __name__,
-    url_prefix='/os_templates'
-)
-
-
-def show():
-    args = list()
-    page = int(request.args.get('page', 1))
-    page_size = int(request.args.get('page_size', 10))
-    keyword = request.args.get('keyword', None)
-    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()
-    resource_path = request.path
-
-    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__())
-
-    if sequence is not None:
-        filters.append('sequence:in:' + sequence.__str__())
-
-    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))
-
-    host_url = request.host_url.rstrip('/')
-
-    os_templates_url = host_url + url_for('api_os_templates.r_get_by_filter')
-    if keyword is not None:
-        os_templates_url = host_url + url_for('api_os_templates.r_content_search')
-
-    boot_job_url = host_url + url_for('api_boot_jobs.r_get_by_filter')
-
-    if args.__len__() > 0:
-        os_templates_url = os_templates_url + '?' + '&'.join(args)
-
-    os_templates_ret = requests.get(url=os_templates_url, cookies=request.cookies)
-    os_templates_ret = json.loads(os_templates_ret.content)
-
-    boot_jobs_ret = requests.get(url=boot_job_url, cookies=request.cookies)
-    boot_jobs_ret = json.loads(boot_jobs_ret.content)
-    boot_jobs_mapping_by_id = dict()
-    for boot_job in boot_jobs_ret['data']:
-        boot_jobs_mapping_by_id[boot_job['id']] = boot_job
-
-    last_page = int(ceil(os_templates_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
-
-    return render_template('os_templates_show.html', os_templates_ret=os_templates_ret,
-                           boot_jobs_mapping_by_id=boot_jobs_mapping_by_id, resource_path=resource_path,
-                           page=page, page_size=page_size, keyword=keyword, pages=pages, order_by=order_by, order=order,
-                           last_page=last_page)
-
-
-def create():
-    host_url = request.host_url.rstrip('/')
-
-    if request.method == 'POST':
-        label = request.form.get('label')
-        path = request.form.get('path')
-        os_type = request.form.get('os_type')
-        icon = request.form.get('icon')
-        boot_job_id = request.form.get('boot_job_id')
-
-        payload = {
-            "label": label,
-            "path": path,
-            "os_type": int(os_type),
-            "active": True,
-            "icon": icon,
-            "boot_job_id": int(boot_job_id)
-        }
-
-        url = host_url + '/api/os_template'
-        headers = {'content-type': 'application/json'}
-        r = requests.post(url, data=json.dumps(payload), headers=headers, cookies=request.cookies)
-        j_r = json.loads(r.content)
-        return render_template('success.html', go_back_url='/os_templates', timeout=10000, title='提交成功',
-                               message_title='添加模板的请求已被接受',
-                               message='您所提交的模板已创建。页面将在10秒钟后自动跳转到模板列表页面!')
-
-    else:
-        return redirect(url_for('v_os_templates.show'))
-
-
-

+ 0 - 12
views_route_table.py

@@ -11,7 +11,6 @@ from views import dashboard
 from views import config
 from views import misc
 from views import os_template_image
-from views import os_template, boot_job, operate_rule
 
 
 __author__ = 'James Iter'
@@ -35,8 +34,6 @@ add_rule_views(guest.blueprints, '/create', views_func='guest.create', methods=[
 add_rule_views(guest.blueprints, '/success', views_func='guest.success', methods=['GET'])
 add_rule_views(guest.blueprint, '/vnc/<uuid>', views_func='guest.vnc', methods=['GET'])
 add_rule_views(guest.blueprint, '/detail/<uuid>', views_func='guest.detail', methods=['GET'])
-add_rule_views(guest.blueprint, '/boot_jobs/<uuid>', views_func='guest.show_boot_job', methods=['GET'])
-add_rule_views(guest.blueprints, '/boot_jobs_list', views_func='guest.show_guests_boot_jobs', methods=['GET'])
 
 add_rule_views(disk.blueprints, '', views_func='disk.show', methods=['GET'])
 add_rule_views(disk.blueprints, '/create', views_func='disk.create', methods=['GET', 'POST'])
@@ -47,15 +44,6 @@ add_rule_views(log.blueprints, '', views_func='log.show', methods=['GET'])
 add_rule_views(os_template_image.blueprints, '', views_func='os_template_image.show', methods=['GET'])
 add_rule_views(os_template_image.blueprint, '', views_func='os_template_image.create', methods=['POST'])
 
-add_rule_views(os_template.blueprints, '', views_func='os_template.show', methods=['GET'])
-add_rule_views(os_template.blueprint, '', views_func='os_template.create', methods=['POST'])
-
-add_rule_views(boot_job.blueprints, '', views_func='boot_job.show', methods=['GET'])
-add_rule_views(boot_job.blueprint, '', views_func='boot_job.create', methods=['POST'])
-
-add_rule_views(operate_rule.blueprints, '', views_func='operate_rule.show', methods=['GET'])
-add_rule_views(operate_rule.blueprint, '', views_func='operate_rule.create', methods=['POST'])
-
 add_rule_views(host.blueprints, '', views_func='host.show', methods=['GET'])
 add_rule_views(host.blueprint, '/detail/<node_id>', views_func='host.detail', methods=['GET'])