James Iter пре 8 година
родитељ
комит
3be0b6ea1a
12 измењених фајлова са 163 додато и 21 уклоњено
  1. 2 1
      api/guest.py
  2. 8 0
      api/os_template.py
  3. 1 1
      docs/todo.md
  4. 1 0
      misc/init.sql
  5. 7 6
      models/__init__.py
  6. 16 7
      models/guest_xml.py
  7. 4 2
      models/os_template.py
  8. 1 0
      models/rules.py
  9. 9 0
      models/status.py
  10. 25 2
      models/utils.py
  11. 87 2
      templates/os_templates_show.html
  12. 2 0
      views/os_template.py

+ 2 - 1
api/guest.py

@@ -132,7 +132,7 @@ def r_create():
             disk.guest_uuid = ''
             disk.create()
 
-            guest_xml = GuestXML(guest=guest, disk=disk, config=config)
+            guest_xml = GuestXML(guest=guest, disk=disk, config=config, os_type=os_template.os_type)
             guest.xml = guest_xml.get_domain()
 
             # 在可用计算节点中平均分配任务
@@ -169,6 +169,7 @@ def r_create():
                 'hostname': guest.on_host,
                 'name': guest.label,
                 'template_path': os_template.path,
+                'os_type': os_template.os_type,
                 'disk': disk.__dict__,
                 'xml': guest_xml.get_domain(),
                 'boot_jobs': _boot_jobs,

+ 8 - 0
api/os_template.py

@@ -44,12 +44,14 @@ def r_create():
         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)
@@ -92,6 +94,11 @@ def r_update(_id):
             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,
@@ -121,6 +128,7 @@ def r_update(_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)

+ 1 - 1
docs/todo.md

@@ -40,7 +40,7 @@
 - [x] 规范日志路径。把日志路径调整至 /var/log/ 下
 - [x] 清除15天前的监控日志
 - [ ] 初始化页面检测redis、mysql是否准备好
-- [ ] 允许删除创建失败的虚拟机
+- [x] 允许删除创建失败的虚拟机
 - [ ] 日志里面加入操作轨迹
 - [x] 日志实现简短字段及详情字段,详情字段用text,存储完整的日志文本。因为日志太长,无法在简短日志字段存放完整。
 - [x] 修改密码功能

+ 1 - 0
misc/init.sql

@@ -106,6 +106,7 @@ CREATE TABLE IF NOT EXISTS os_template(
     id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
     label VARCHAR(255) NOT NULL,
     path VARCHAR(255) NOT NULL,
+    os_type TINYINT UNSIGNED NOT NULL,
     active BOOLEAN NOT NULL DEFAULT TRUE,
     icon VARCHAR(255) NOT NULL,
     boot_job_id BIGINT UNSIGNED NOT NULL DEFAULT 0,

+ 7 - 6
models/__init__.py

@@ -39,10 +39,6 @@ from guest import (
     Guest, Disk
 )
 
-from guest_xml import (
-    GuestXML
-)
-
 from boot_job import (
     BootJob, OperateRule
 )
@@ -56,7 +52,12 @@ from status import (
     GuestState,
     ResponseState,
     DiskState,
-    LogLevel
+    LogLevel,
+    OSType
+)
+
+from guest_xml import (
+    GuestXML
 )
 
 from log import (
@@ -91,7 +92,7 @@ __copyright__ = '(c) 2017 by James Iter.'
 
 
 __all__ = [
-    'Rules', 'Utils', 'Init', 'Database', 'FilterFieldType', 'Filter', 'EmitKind', 'GuestState', 'DiskState',
+    'Rules', 'Utils', 'Init', 'Database', 'FilterFieldType', 'Filter', 'EmitKind', 'GuestState', 'DiskState', 'OSType',
     'LogLevel', 'ORM', 'User', 'Config', 'Guest', 'Disk', 'BootJob', 'OperateRule', 'OSTemplate', 'GuestXML', 'Log',
     'EventProcessor', 'ResponseState', 'CPUMemory', 'Traffic', 'DiskIO', 'HostCPUMemory', 'HostTraffic',
     'HostDiskUsageIO', 'Host'

+ 16 - 7
models/guest_xml.py

@@ -2,7 +2,7 @@
 # -*- coding: utf-8 -*-
 
 
-from models import Config, Disk
+from models import Config, Disk, OSTemplate, OSType
 from models import Guest
 from models import status
 
@@ -15,7 +15,7 @@ __copyright__ = '(c) 2017 by James Iter.'
 
 class GuestXML(object):
 
-    def __init__(self, guest=None, disk=None, config=None):
+    def __init__(self, guest=None, disk=None, config=None, os_type=None):
         assert isinstance(guest, Guest)
         assert isinstance(disk, Disk)
         assert isinstance(config, Config)
@@ -23,23 +23,22 @@ class GuestXML(object):
         self.guest = guest
         self.disk = disk
         self.config = config
+        self.os_type = os_type
 
     def get_domain(self):
-        # clock 参考链接:https://libvirt.org/formatdomain.html#elementsTime
-        # Windows Guest 设为 localtime,非 Windows Guest 都设为 utc
         return """<?xml version="1.0" encoding="utf-8"?>
             <domain type="kvm">
             {0}
-            <clock offset='utc'/>
             {1}
             {2}
             {3}
             {4}
             {5}
             {6}
+            {7}
             </domain>
-        """.format(self.get_features(), self.get_name(), self.get_uuid(), self.get_vcpu(), self.get_memory(),
-                   self.get_os(), self.get_devices())
+        """.format(self.get_features(), self.get_clock(), self.get_name(), self.get_uuid(), self.get_vcpu(),
+                   self.get_memory(), self.get_os(), self.get_devices())
 
     @staticmethod
     def get_features():
@@ -50,6 +49,16 @@ class GuestXML(object):
             </features>
         """
 
+    def get_clock(self):
+        # clock 参考链接:https://libvirt.org/formatdomain.html#elementsTime
+        # Windows Guest 设为 localtime,非 Windows Guest 都设为 utc
+        offset = 'utc'
+
+        if self.os_type == OSType.windows.value:
+            offset = 'localtime'
+
+        return """<clock offset='{0}'/>""".format(offset)
+
     def get_name(self):
         return """<name>{0}</name>""".format(self.guest.label)
 

+ 4 - 2
models/os_template.py

@@ -22,6 +22,7 @@ class OSTemplate(ORM):
         self.id = 0
         self.label = None
         self.path = None
+        self.os_type = None
         self.active = None
         self.icon = None
         self.boot_job_id = None
@@ -32,15 +33,16 @@ class OSTemplate(ORM):
             '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']
+        return ['active', 'boot_job_id', 'os_type']
 
     @staticmethod
     def get_allow_content_search_keywords():
-        return ['label', 'path']
+        return ['label', 'path', 'os_type']
 

+ 1 - 0
models/rules.py

@@ -68,6 +68,7 @@ class Rules(Enum):
     REMARK = (basestring, 'remark')
     USE_FOR = (int, 'use_for')
     LABEL = (basestring, 'label')
+    OS_TYPE = (int, 'os_type')
     ACTIVE = (bool, 'active')
     ICON = (basestring, 'icon')
 

+ 9 - 0
models/status.py

@@ -102,3 +102,12 @@ 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
+

+ 25 - 2
models/utils.py

@@ -6,7 +6,6 @@ from functools import wraps
 
 import commands
 
-import time
 from flask import make_response, g, request
 from flask.wrappers import Response
 from werkzeug.utils import import_string, cached_property
@@ -272,7 +271,31 @@ def utility_processor():
         return '<span class="{icon}" style="color: #{color};">&nbsp;&nbsp;{desc}</span>'.format(
             icon=icon, color=color, desc=desc)
 
+    def format_os_type_id_to_name(os_type):
+        from models import OSType
+        if os_type == OSType.linux.value:
+            return u'Linux'
+
+        elif os_type == OSType.windows.value:
+            return u'Windows'
+
+        elif os_type == OSType.bsd.value:
+            return u'BSD'
+
+        elif os_type == OSType.aix.value:
+            return u'AIX'
+
+        elif os_type == OSType.hp_unix.value:
+            return u'HP-UNIX'
+
+        elif os_type == OSType.unknown.value:
+            return u'Unknown'
+
+        else:
+            return u'Unknown'
+
     return dict(format_price=format_price, format_datetime_by_tus=format_datetime_by_tus,
                 format_datetime_by_ts=format_datetime_by_ts, format_guest_status=format_guest_status,
-                format_sequence_to_device_name=format_sequence_to_device_name, format_disk_state=format_disk_state)
+                format_sequence_to_device_name=format_sequence_to_device_name, format_disk_state=format_disk_state,
+                format_os_type_id_to_name=format_os_type_id_to_name)
 

+ 87 - 2
templates/os_templates_show.html

@@ -152,9 +152,14 @@
             refresh_os_template_boot_job_selectpicker($('#update_boot_job_id'));
         });
 
-        $('#update_icon_modal').on('show.bs.modal', function (me) {
+        $('#update_os_type_modal').on('show.bs.modal', function (me) {
             $('#os_template_id').val($(me.relatedTarget).parent().prev().prev().prev().prev().prev().text());
-            $('#update_icon_instance_desc').text($(me.relatedTarget).parent().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());
         });
     });
 
@@ -349,6 +354,24 @@
         });
     }
 
+    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,
@@ -389,6 +412,12 @@
         $('#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);
@@ -422,6 +451,7 @@
                     <th width="180px;">名称</th>
                     <th>状态</th>
                     <th width="500px;">路径</th>
+                    <th>操作系统类型</th>
                     <th>ICON</th>
                     <th>初始化作业</th>
                     <th>操作</th>
@@ -454,6 +484,11 @@
                             </a>
                         </div>
                     </td>
+                    <td>
+                        <a href="javascript:;" data-toggle="modal" data-target="#update_os_type_modal">
+                            {{ format_os_type_id_to_name(item.os_type) }}
+                        </a>
+                    </td>
                     <td>
                         <a href="javascript:;" data-toggle="modal" data-target="#update_icon_modal">
                             <span class="{{ item.icon }}"></span>
@@ -641,6 +676,19 @@
                                 <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" 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>
                         <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>
@@ -696,6 +744,43 @@
     </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">

+ 2 - 0
views/os_template.py

@@ -116,12 +116,14 @@ def create():
     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)