浏览代码

实现日志页面

James Iter 9 年之前
父节点
当前提交
b0b942d57f
共有 6 个文件被更改,包括 148 次插入611 次删除
  1. 4 0
      main.py
  2. 5 2
      models/utils.py
  3. 1 1
      templates/layout.html
  4. 44 606
      templates/log.html
  5. 91 0
      views/log.py
  6. 3 2
      views_route_table.py

+ 4 - 0
main.py

@@ -42,6 +42,8 @@ from views.guest import blueprint as view_guest_blueprint
 from views.guest import blueprints as view_guest_blueprints
 from views.disk import blueprint as view_disk_blueprint
 from views.disk import blueprints as view_disk_blueprints
+from views.log import blueprint as view_log_blueprint
+from views.log import blueprints as view_log_blueprints
 
 from websockify.websocketproxy import WebSocketProxy
 
@@ -113,6 +115,8 @@ try:
     app.register_blueprint(view_guest_blueprints)
     app.register_blueprint(view_disk_blueprint)
     app.register_blueprint(view_disk_blueprints)
+    app.register_blueprint(view_log_blueprint)
+    app.register_blueprint(view_log_blueprints)
 
 except:
     logger.error(traceback.format_exc())

+ 5 - 2
models/utils.py

@@ -140,6 +140,9 @@ def utility_processor():
     def format_price(amount, currency=u'¥'):
         return u'{0:.2f}{1}'.format(amount, currency)
 
+    def format_datetime_by_ts(ts, fmt='%Y-%m-%d %H:%M'):
+        return time.strftime(fmt, time.localtime(ts))
+
     def format_datetime_by_tus(tus, fmt='%y-%m-%d %H:%M'):
         return time.strftime(fmt, time.localtime(tus/1000/1000))
 
@@ -260,6 +263,6 @@ def utility_processor():
             icon=icon, color=color, desc=desc)
 
     return dict(format_price=format_price, format_datetime_by_tus=format_datetime_by_tus,
-                format_guest_status=format_guest_status, format_sequence_to_device_name=format_sequence_to_device_name,
-                format_disk_state=format_disk_state)
+                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)
 

+ 1 - 1
templates/layout.html

@@ -312,7 +312,7 @@
                             </a>
                         </li>
                         <li>
-                            <a href="{{ url_for('v_guests.show') }}" title="JimVLog">
+                            <a href="{{ url_for('v_logs.show') }}" title="JimVLog">
                                 <i class="glyph-icon icon-linecons-eye"></i>
                                 <span>平台日志</span>
                             </a>

+ 44 - 606
templates/log.html

@@ -22,7 +22,6 @@
         .table-bordered > tbody > tr {
             padding-top: 10px;
             padding-bottom: 10px;
-            height: 106px;
         }
 
         .table-bordered > thead > tr > th,
@@ -96,76 +95,12 @@
             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);
         });
-
-        $('#edit_remark_modal').on('show.bs.modal', function (me) {
-            $('#instance_uuid').val($(me.relatedTarget).parent().parent().prev().prev().text());
-            $('#edit_remark').val($(me.relatedTarget).prev().text());
-        });
-
-        $('#migrate_host').chosen({
-            "disable_search": true
-        });
-
-        $('#migrate_modal').on('show.bs.modal', function (me) {
-            $('#migrate_instance_uuid').val(
-                $(me.relatedTarget).parent().parent().parent().parent().parent().children()[0].textContent);
-            $.ajax({
-                url : '/api/hosts',
-                type : 'GET',
-                contentType: "application/json; charset=utf-8",
-                dataType: 'json',
-                error : function() {
-                },
-                success : function(data, textStatus, xhr) {
-                    $('#migrate_host').empty();
-                    $.each(data.data, function(k, v) {
-                        $('#migrate_host').append(
-                            $('<option>', {value: v['hostname'], text: v['hostname']})
-                        );
-                    });
-                    $('#migrate_host').trigger("chosen:updated");
-                }
-            });
-        });
-
-        $('#reset_password_modal').on('show.bs.modal', function (me) {
-            $('#reset_password_instance_uuid').val(
-                $(me.relatedTarget).parent().parent().parent().parent().parent().children()[0].textContent);
-        });
     });
 
     function refresh() {
@@ -178,366 +113,11 @@
         }
         window.location.href=cur_url;
     }
-
-    function row_onmouseover(me) {
-        $(me).find(".edit_remark_trigger").css('display','inline-flex');
-    }
-
-    function row_onmouseout(me) {
-        $(me).find(".edit_remark_trigger").css('display','none');
-    }
-
-    function remark_update(me) {
-        var uuid = $('#instance_uuid').val();
-        var remark = $('#edit_remark').val();
-        $('#edit_remark_modal').modal('hide');
-        $.ajax({
-            url : '/api/guest/' + uuid,
-            type : 'PATCH',
-            contentType: "application/json; charset=utf-8",
-            data : JSON.stringify({
-                remark: remark
-            }),
-            error : function() {
-                alter_danger('实例备注更新失败!');
-            },
-            success : function() {
-                $('#guest_list tbody').find('td:contains(' + uuid + ')').next().next().find('p').text(remark);
-                alter_success('实例备注更新成功!');
-            }
-        });
-    }
-
-    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 boot(uuids) {
-        $.ajax({
-            url : '/api/guests/_boot/' + uuids.join(','),
-            type : 'PUT',
-            contentType: "application/json; charset=utf-8",
-            error : function() {
-                alter_danger('实例启动指令发送失败!');
-            },
-            success : function() {
-                alter_success('实例启动指令发送成功!');
-            }
-        });
-    }
-
-    function reboot(uuids) {
-        $.ajax({
-            url : '/api/guests/_reboot/' + uuids.join(','),
-            type : 'PUT',
-            contentType: "application/json; charset=utf-8",
-            error : function() {
-                alter_danger('实例重启指令发送失败!');
-            },
-            success : function() {
-                alter_success('实例重启指令发送成功!');
-            }
-        });
-    }
-
-    function force_reboot(uuids) {
-        $.ajax({
-            url : '/api/guests/_force_reboot/' + uuids.join(','),
-            type : 'PUT',
-            contentType: "application/json; charset=utf-8",
-            error : function() {
-                alter_danger('实例强制重启指令发送失败!');
-            },
-            success : function() {
-                alter_success('实例强制重启指令发送成功!');
-            }
-        });
-    }
-
-    function shutdown(uuids) {
-        $.ajax({
-            url : '/api/guests/_shutdown/' + uuids.join(','),
-            type : 'PUT',
-            contentType: "application/json; charset=utf-8",
-            error : function() {
-                alter_danger('实例停止指令发送失败!');
-            },
-            success : function() {
-                alter_success('实例停止指令发送成功!');
-            }
-        });
-    }
-
-    function force_shutdown(uuids) {
-        $.ajax({
-            url : '/api/guests/_force_shutdown/' + uuids.join(','),
-            type : 'PUT',
-            contentType: "application/json; charset=utf-8",
-            error : function() {
-                alter_danger('实例强制停止指令发送失败!');
-            },
-            success : function() {
-                alter_success('实例强制停止指令发送成功!');
-            }
-        });
-    }
-
-    function suspend(uuids) {
-        $.ajax({
-            url : '/api/guests/_suspend/' + uuids.join(','),
-            type : 'PUT',
-            contentType: "application/json; charset=utf-8",
-            error : function() {
-                alter_danger('实例暂停指令发送失败!');
-            },
-            success : function() {
-                alter_success('实例暂停指令发送成功!');
-            }
-        });
-    }
-
-    function resume(uuids) {
-        $.ajax({
-            url : '/api/guests/_resume/' + uuids.join(','),
-            type : 'PUT',
-            contentType: "application/json; charset=utf-8",
-            error : function() {
-                alter_danger('实例恢复指令发送失败!');
-            },
-            success : function() {
-                alter_success('实例恢复指令发送成功!');
-            }
-        });
-    }
-
-    function remove(uuids) {
-        $.ajax({
-            url : '/api/guests/' + uuids.join(','),
-            type : 'DELETE',
-            contentType: "application/json; charset=utf-8",
-            error : function() {
-                alter_danger('实例删除指令发送失败!');
-            },
-            success : function() {
-                alter_success('实例删除指令发送成功!');
-            }
-        });
-    }
-
-    function migrate(uuids, host) {
-        $.ajax({
-            url : '/api/guests/_migrate/' + uuids.join(',') + '/' + host,
-            type : 'PUT',
-            contentType: "application/json; charset=utf-8",
-            error : function() {
-                alter_danger('实例迁移指令发送失败!');
-            },
-            success : function() {
-                alter_success('实例迁移指令发送成功!');
-            }
-        });
-    }
-
-    function reset_password(uuids, password) {
-        $.ajax({
-            url : '/api/guests/_reset_password/' + uuids.join(',') + '/' + password,
-            type : 'PUT',
-            contentType: "application/json; charset=utf-8",
-            error : function() {
-                alter_danger('实例重置密码指令发送失败!');
-            },
-            success : function() {
-                alter_success('实例重置密码指令发送成功!');
-            }
-        });
-    }
-
-    function boot_at(me) {
-        var uuid = $(me).parent().parent().parent().parent().parent().children()[0].textContent;
-        boot([uuid]);
-    }
-
-    function reboot_at(me) {
-        var uuid = $(me).parent().parent().parent().parent().parent().children()[0].textContent;
-        reboot([uuid]);
-    }
-
-    function force_reboot_at(me) {
-        var uuid = $(me).parent().parent().parent().parent().parent().children()[0].textContent;
-        force_reboot([uuid]);
-    }
-
-    function shutdown_at(me) {
-        var uuid = $(me).parent().parent().parent().parent().parent().children()[0].textContent;
-        shutdown([uuid]);
-    }
-
-    function force_shutdown_at(me) {
-        var uuid = $(me).parent().parent().parent().parent().parent().children()[0].textContent;
-        force_shutdown([uuid]);
-    }
-
-    function suspend_at(me) {
-        var uuid = $(me).parent().parent().parent().parent().parent().children()[0].textContent;
-        suspend([uuid]);
-    }
-
-    function resume_at(me) {
-        var uuid = $(me).parent().parent().parent().parent().parent().children()[0].textContent;
-        resume([uuid]);
-    }
-
-    function delete_at(me) {
-        var uuid = $(me).parent().parent().parent().parent().parent().children()[0].textContent;
-        remove([uuid]);
-    }
-
-    function migrate_go() {
-        var migrate_type = $('#migrate_type').val();
-        var uuids = [];
-        if (migrate_type === 'single') {
-            uuids = [$('#migrate_instance_uuid').val()];
-        } else {
-            uuids = get_checked_uuids();
-        }
-        var host = $('#migrate_host').val();
-        $('#migrate_modal').modal('hide');
-        migrate([uuids], host);
-    }
-
-    function reset_password_go() {
-        var migrate_type = $('#reset_password_type').val();
-        var uuids = [];
-        if (migrate_type === 'single') {
-            uuids = [$('#reset_password_instance_uuid').val()];
-        } else {
-            uuids = get_checked_uuids();
-        }
-        var password = $('#new_password').val();
-        $('#reset_password_modal').modal('hide');
-        reset_password([uuids], password);
-    }
-
-    function get_selected_uuids() {
-        var selected_element = get_selected_element(true);
-        var uuids = [];
-
-        selected_element.each(function(i, e) {
-            var uuid = $(e).parent().prev().text();
-            uuids.push(uuid);
-        });
-
-        return uuids;
-    }
-
-    function get_checked_uuids() {
-        var uuids = get_selected_uuids();
-
-        if (uuids.length < 1) {
-            alter_warning('未选择任何可操作的实例!');
-            return false;
-        }
-
-        return uuids;
-    }
-
-    function batch_boot() {
-        var uuids = get_checked_uuids();
-        if (uuids) {
-            boot(uuids);
-        }
-    }
-
-    function batch_reboot() {
-        var uuids = get_checked_uuids();
-        if (uuids) {
-            reboot(uuids);
-        }
-    }
-
-    function batch_force_reboot() {
-        var uuids = get_checked_uuids();
-        if (uuids) {
-            force_reboot(uuids);
-        }
-    }
-
-    function batch_shutdown() {
-        var uuids = get_checked_uuids();
-        if (uuids) {
-            shutdown(uuids);
-        }
-    }
-
-    function batch_force_shutdown() {
-        var uuids = get_checked_uuids();
-        if (uuids) {
-            force_shutdown(uuids);
-        }
-    }
-
-    function batch_suspend() {
-        var uuids = get_checked_uuids();
-        if (uuids) {
-            suspend(uuids);
-        }
-    }
-
-    function batch_resume() {
-        var uuids = get_checked_uuids();
-        if (uuids) {
-            resume(uuids);
-        }
-    }
-
-    function batch_delete() {
-        var uuids = get_checked_uuids();
-        if (uuids) {
-            remove(uuids);
-        }
-    }
 </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">
@@ -548,102 +128,55 @@
                         </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 add-page-transition" href="/guests/create" data-transition="pt-page-moveFromRight-init" style="border-radius: 0; padding-left: 40px; padding-right: 40px;">创建实例</a>
                         </div>
                     </div>
                 </div>
-                <table id="guest_list" class="table table-bordered table-hover" cellspacing="0" width="100%" role="grid"
+                <table id="log_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><input class="all_selector" title="选取所有" type="checkbox"></th>
-                    <th width="180px;">名称</th>
-                    <th></th>
-                    <th>状态</th>
-                    <th>计算节点</th>
-                    <th>配置</th>
-                    <th>IP</th>
-                    <th>密码</th>
-                    <th>创建时间</th>
-                    <th>操作</th>
+                    <th>ID</th>
+                    <th>类型</th>
+                    <th>时间</th>
+                    <th>源</th>
+                    <th>日志</th>
                 </tr>
                 </thead>
                 <tbody>
-                {% for item in guests_ret.data %}
-                <tr role="row" class="odd" onmouseover="row_onmouseover(this);" onmouseout="row_onmouseout(this);">
-                    <td style="display: none;">{{ item.uuid }}</td>
-                    <td><input title="选中" type="checkbox"></td>
-                    <td>
-                        <div>
-                            <a href="/guest/detail/{{ item.uuid }}">{{ item.label }}</a>
-                        </div>
-                        <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><span class="{{ os_template_mapping_by_id[item.os_template_id].icon }}"></span></td>
-                    <td>{{ format_guest_status(item.status)|safe }}</td>
-                    <td>{{ item.on_host }}</td>
-                    <td>CPU :&nbsp;&nbsp;{{ item.cpu }}&nbsp;核<br />内存 :&nbsp;&nbsp;{{ item.memory }}&nbsp;GB</td>
-                    <td>{{ item.ip }}</td>
-                    <td>{{ item.password }}</td>
-                    <td>{{ format_datetime_by_tus(item.create_time) }}</td>
+                {% for item in logs_ret.data %}
+                <tr role="row" class="odd" style="
+                        {% if item.type == 3 %}
+                            background-color: #ffffff;
+                        {% elif item.type == 4 %}
+                            background-color: #bbf6ff;
+                        {% elif item.type == 0 %}
+                            background-color: #ffbaba;
+                        {% elif item.type == 1 %}
+                            background-color: #fd4949;
+                        {% elif item.type == 2 %}
+                            background-color: #fff580;
+                        {% else %}
+                            background-color: #ababab;
+                        {% endif %}">
+                    <td>{{ item.id }}</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.status not in [4, 5, 6] %} disabled {% endif %}">
-                                    <a href="javascript:;" onclick="boot_at(this);">启动</a>
-                                </li>
-                                <li class="{% if item.status not in [1] %} disabled {% endif %}">
-                                    <a href="javascript:;" onclick="reboot_at(this);">重启</a>
-                                </li>
-                                <li class="{% if item.status not in [1] %} disabled {% endif %}">
-                                    <a href="javascript:;" onclick="force_reboot_at(this);">强制重启</a>
-                                </li>
-                                <li class="{% if item.status not in [1] %} disabled {% endif %}">
-                                    <a href="javascript:;" onclick="shutdown_at(this);">停止</a>
-                                </li>
-                                <li class="{% if item.status not in [1] %} disabled {% endif %}">
-                                    <a href="javascript:;" onclick="force_shutdown_at(this);">强制停止</a>
-                                </li>
-                                <li class="divider"></li>
-                                <li class="{% if item.status not in [1] %} disabled {% endif %}">
-                                    <a href="javascript:;" onclick="suspend_at(this);">暂停</a>
-                                </li>
-                                <li class="{% if item.status not in [3] %} disabled {% endif %}">
-                                    <a href="javascript:;" onclick="resume_at(this);">恢复</a>
-                                </li>
-                                <li class="divider"></li>
-                                <li class="">
-                                    <a href="/guest/vnc/{{ item.uuid }}" target="_blank">远程连接</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')">
-                                        密码重置
-                                    </a>
-                                </li>
-                                <li class="{% if item.status not in [1, 3, 4, 5, 6] %} disabled {% endif %}">
-                                    <a href="javascript:;" data-toggle="modal" data-target="#migrate_modal"
-                                       onclick="$('#migrate_type').val('single')">
-                                        迁移到
-                                    </a>
-                                </li>
-                                <li class="divider"></li>
-                                <li class="{% if item.status not in [4, 5, 6, 255] %} disabled {% endif %}">
-                                    <a href="javascript:;" onclick="delete_at(this);">删除</a>
-                                </li>
-                            </ul>
-                        </div>
+                        {% if item.type == 3 %}
+                            Info
+                        {% elif item.type == 4 %}
+                            Debug
+                        {% elif item.type == 0 %}
+                            Critical
+                        {% elif item.type == 1 %}
+                            Error
+                        {% elif item.type == 2 %}
+                            Warn
+                        {% else %}
+                            Unknown
+                        {% endif %}
                     </td>
+                    <td>{{ format_datetime_by_ts(item.timestamp) }}</td>
+                    <td>{{ item.host }}</td>
+                    <td>{{ item.message }}</td>
                 </tr>
                 {% endfor %}
                 </tbody>
@@ -652,67 +185,31 @@
                 <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" onclick="batch_boot();">启动</button>
-                                    <button class="btn btn-default btn-shortcut disabled" onclick="batch_reboot();">重启</button>
-                                    <button class="btn btn-default btn-shortcut disabled" onclick="batch_shutdown();">停止</button>
-                                    <button class="btn btn-default btn-shortcut disabled" onclick="batch_suspend();">暂停</button>
-                                    <button class="btn btn-default btn-shortcut disabled" onclick="batch_resume();">恢复</button>
-                                    <div class="dropup inline-block">
-                                        <button href="javascript:;" class="dropdown-toggle btn btn-default btn-shortcut disabled" data-toggle="dropdown">
-                                            更多
-                                        </button>
-                                        <ul class="dropdown-menu" style="width: 60px;">
-                                            <li>
-                                                <a href="javascript:;" onclick="batch_delete();">删除</a>
-                                            </li>
-                                            <li class="divider"></li>
-                                            <li>
-                                                <a href="javascript:;" onclick="$('#migrate_type').val('batch');"
-                                                   data-toggle="modal" data-target="#migrate_modal" >
-                                                    迁移到
-                                                </a>
-                                            </li>
-                                            <li>
-                                                <a href="javascript:;" onclick="$('#reset_password_type').val('batch');"
-                                                   data-toggle="modal" data-target="#reset_password_modal">
-                                                    密码重置
-                                                </a>
-                                            </li>
-                                            <li class="divider"></li>
-                                            <li>
-                                                <a href="javascript:;" onclick="batch_force_shutdown();">强制停止</a>
-                                            </li>
-                                            <li>
-                                                <a href="javascript:;" onclick="batch_force_reboot();">强制重启</a>
-                                            </li>
-                                        </ul>
-                                    </div>
                                 </div>
                                 <div class="col-sm-3" style="font-size: 12px; padding-top: 5px; text-align: right;">
-                                    共有{{ guests_ret.paging.total }}条,每页显示:
+                                    共有{{ logs_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>
+                                        <option value="100" {% if page_size == 100  %} selected {% endif %}>100</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>
+                                                <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 %}">{{ item }}</a>
+                                                <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 %}">»</a>
+                                                <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>
@@ -726,63 +223,4 @@
         </div>
     </div>
 </div>
-
-<div class="modal" id="edit_remark_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="instance_uuid" title="实例 UUID" class="form-control" name="uuid" hidden>
-                <input id="edit_remark" title="实例名称" class="form-control" name="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="migrate_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="migrate_type" title="迁移类型" class="form-control" name="migrate_type" value="single" hidden>
-                <input id="migrate_instance_uuid" title="实例 UUID" class="form-control" name="uuid" hidden>
-                <select id="migrate_host" name="migrate_host" title="可迁移宿主机" class="chosen-select">
-                </select>
-            </div>
-            <div class="modal-footer">
-                <button type="button" class="btn btn-sm btn-primary" onclick="migrate_go();">确定</button>
-                <button type="button" class="btn btn-sm btn-default" data-dismiss="modal">取消</button>
-            </div>
-        </div>
-    </div>
-</div>
-
-<div class="modal" id="reset_password_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">
-                <div class="form-group">
-                    <input id="reset_password_type" title="重置密码类型" class="form-control" name="reset_password_type" value="single" hidden>
-                    <input id="reset_password_instance_uuid" title="实例 UUID" class="form-control" name="uuid" hidden>
-                    <input id="new_password" title="新密码" class="form-control" name="new_password" type="password" placeholder="New password">
-                </div>
-            </div>
-            <div class="modal-footer">
-                <button type="button" class="btn btn-sm btn-primary" onclick="reset_password_go();">确定</button>
-                <button type="button" class="btn btn-sm btn-default" data-dismiss="modal">取消</button>
-            </div>
-        </div>
-    </div>
-</div>
 {% endblock content %}

+ 91 - 0
views/log.py

@@ -0,0 +1,91 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+
+import json
+from flask import Blueprint, render_template, url_for, request
+import requests
+from math import ceil
+
+
+__author__ = 'James Iter'
+__date__ = '2017/7/9'
+__contact__ = 'james.iter.cn@gmail.com'
+__copyright__ = '(c) 2017 by James Iter.'
+
+
+blueprint = Blueprint(
+    'v_log',
+    __name__,
+    url_prefix='/log'
+)
+
+blueprints = Blueprint(
+    'v_logs',
+    __name__,
+    url_prefix='/logs'
+)
+
+
+def show():
+    args = list()
+    page = int(request.args.get('page', 1))
+    page_size = int(request.args.get('page_size', 50))
+    keyword = request.args.get('keyword', None)
+    order_by = request.args.get('order_by', None)
+    order = request.args.get('order', 'desc')
+    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 order_by is not None:
+        args.append('order_by=' + order_by)
+
+    if order is not None:
+        args.append('order=' + order)
+
+    host_url = request.host_url.rstrip('/')
+
+    logs_url = host_url + url_for('api_logs.r_get_by_filter')
+    if keyword is not None:
+        logs_url = host_url + url_for('api_logs.r_content_search')
+
+    if args.__len__() > 0:
+        logs_url = logs_url + '?' + '&'.join(args)
+
+    logs_ret = requests.get(url=logs_url)
+    logs_ret = json.loads(logs_ret.content)
+
+    last_page = int(ceil(logs_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:
+                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('log.html', logs_ret=logs_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)
+
+

+ 3 - 2
views_route_table.py

@@ -1,9 +1,8 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
-
 from models.utils import add_rule_views
-from views import guest, disk
+from views import guest, disk, log
 
 
 __author__ = 'James Iter'
@@ -22,3 +21,5 @@ add_rule_views(disk.blueprints, '', views_func='disk.show', methods=['GET'])
 add_rule_views(disk.blueprints, '/create', views_func='disk.create', methods=['GET', 'POST'])
 add_rule_views(disk.blueprint, '/detail/<uuid>', views_func='disk.detail', methods=['GET', 'POST'])
 
+add_rule_views(log.blueprints, '', views_func='log.show', methods=['GET'])
+