Przeglądaj źródła

实现磁盘详情页

James Iter 9 lat temu
rodzic
commit
23eea6913f

+ 1 - 1
static/themes/components/default.css

@@ -14,7 +14,7 @@ body .content-box-header > .ui-tabs-nav li.ui-tabs-active > a:hover,
 .pagination > li > span,
 .btn-link,
 a {
-    color: #8da0aa;
+    color: #5e6c73;
 }
 /* Primary font color */
 

+ 0 - 1
templates/disk_create.html

@@ -1,5 +1,4 @@
 {% extends "layout.html" %}
-{% block title %} Guest {% endblock %}
 {% block head %}
     {{ super() }}
 

+ 543 - 0
templates/disk_detail.html

@@ -0,0 +1,543 @@
+{% 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;
+        }
+
+        .btn,
+        .form-group>div>div,
+        .form-control {
+            border-radius: 0;
+        }
+
+        .table {
+            font-size: 12px;
+            border-width: 1px;
+            line-height: 22px;
+        }
+
+        .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;
+        }
+
+        .guest-label {
+            color: #999999;
+        }
+
+        .guest-desc {
+            color: #333333;
+        }
+
+    </style>
+{% endblock head %}
+{% block body %}
+<script>
+    var resource_path = window.location.pathname;
+    var resource_path_array = resource_path.split('/');
+    var uuid = resource_path_array[resource_path_array.length - 1];
+
+    var datetime_format_type = 'time';
+
+    function format_time(timestamp, i) {
+        if (datetime_format_type === 'time') {
+            return new Date(timestamp).format('HH:MM');
+        } else {
+            return new Date(timestamp).format('mm-dd');
+        }
+    }
+
+    function render_disk_throughput_chart(disk_uuid, granularity, dev_id, dev_name) {
+        var url = '/api/performance/disk_io/last_hour/' + disk_uuid;
+        if (granularity === 'hour') {
+            url = '/api/performance/disk_io/last_hour/' + disk_uuid
+        } else if (granularity === 'six_hours') {
+            url = '/api/performance/disk_io/last_six_hours/' + disk_uuid
+        } else if (granularity === 'day') {
+            url = '/api/performance/disk_io/last_day/' + disk_uuid
+        } else {
+            url = '/api/performance/disk_io/last_seven_days/' + disk_uuid
+        }
+
+        $.ajax({
+            url : url,
+            type : 'GET',
+            contentType: "application/json; charset=utf-8",
+            dataType: 'json',
+            error : function() {
+                alter_danger('获取图表数据失败失败!');
+            },
+            success : function(data, textStatus, xhr) {
+
+                if (data.data.length > 1) {
+                    if ((data.data[data.data.length - 1]['timestamp'] - data.data[0]['timestamp']) >= 86400) {
+                        datetime_format_type = 'date';
+                    }
+                }
+
+                var option = {
+                    color: ['#3BC0FF', '#FAB406'],
+                    title: {
+                        text: '磁盘吞吐量 - ' + dev_name
+                    },
+                    legend: {
+                        data:['读(MBps)', '写(MBps)'],
+                        bottom: 5
+                    },
+                    toolbox: {
+                        feature: {
+                            dataZoom: {
+                                yAxisIndex: 'none'
+                            },
+                            magicType: {type: ['line', 'bar']},
+                            saveAsImage: {
+                                show: true,
+                                pixelRatio: 4
+                            }
+                        }
+                    },
+                    tooltip: {
+                        show: true,
+                        trigger: 'axis',
+                        formatter: (function (params, ticket, callback) {
+                            var readSeriesName = params[0].seriesName;
+                            var writeSeriesName = params[1].seriesName;
+                            var read = params[0].data[1];
+                            var write = params[1].data[1];
+                            var readMarker = params[0].marker;
+                            var writeMarker = params[1].marker;
+                            var datetime = new Date(params[0].data[0]).format('yyyy-mm-dd HH:MM:ss');
+
+                            if (read === null) {
+                                read = '无数据';
+                            }
+
+                            if (write === null) {
+                                write = '无数据';
+                            }
+
+                            return datetime + '<br />' + [readMarker, readSeriesName, read].join(' ') +
+                                '<br />' + [writeMarker, writeSeriesName, write].join(' ')
+                        })
+                    },
+                    xAxis: {
+                        type: 'time',
+                        axisTick: {
+                            show: false
+                        },
+                        axisLine: {
+                            lineStyle: {
+                                opacity: 0.1,
+                                color: '#3d4245'
+                            }
+                        },
+                        splitLine: {
+                            show: false
+                        },
+                        axisLabel: {
+                            formatter: format_time
+                        }
+                    },
+                    yAxis: {
+                        min: 0,
+                        max: 'dataMax',
+                        minInterval: 2,
+                        axisTick: {
+                            show: false
+                        },
+                        axisLine: {
+                            lineStyle: {
+                                opacity: 0,
+                                color: '#8c8f91'
+                            }
+                        },
+                        splitLine: {
+                            show: true,
+                            lineStyle: {
+                                width: 1,
+                                opacity: 0.5
+                            }
+                        }
+                    },
+                    series: [{
+                        name: '读(MBps)',
+                        type: 'line',
+                        showSymbol: false,
+                        smooth: true,
+                        lineStyle: {
+                            normal: {
+                                width: 1
+                            }
+                        },
+                        data: (function () {
+                            var d = [];
+                            $.each(data.data, function(i, ele) {
+                                d.push([
+                                    ele['timestamp'] * 1000,
+                                    ele['rd_bytes'] !== null ? Math.floor(ele['rd_bytes'] / 1024 / 1024 * 1000) / 1000 : null
+                                ])
+                            });
+                            return d;
+                        })()
+                    },{
+                        name: '写(MBps)',
+                        type: 'line',
+                        showSymbol: false,
+                        smooth: true,
+                        lineStyle: {
+                            normal: {
+                                width: 1
+                            }
+                        },
+                        data: (function () {
+                            var d = [];
+                            $.each(data.data, function(i, ele) {
+                                d.push([
+                                    ele['timestamp'] * 1000,
+                                    ele['wr_bytes'] !== null ? Math.floor(ele['wr_bytes'] / 1024 / 1024 * 1000) / 1000 : null
+                                ])
+                            });
+                            return d;
+                        })()
+                    }]
+                };
+
+                var disk_chart = echarts.init(document.getElementById(dev_id));
+                disk_chart.setOption(option);
+            }
+        });
+    }
+
+    function render_disk_iops_chart(disk_uuid, granularity, dev_id, dev_name) {
+        var url = '/api/performance/disk_io/last_hour/' + disk_uuid;
+        if (granularity === 'hour') {
+            url = '/api/performance/disk_io/last_hour/' + disk_uuid
+        } else if (granularity === 'six_hours') {
+            url = '/api/performance/disk_io/last_six_hours/' + disk_uuid
+        } else if (granularity === 'day') {
+            url = '/api/performance/disk_io/last_day/' + disk_uuid
+        } else {
+            url = '/api/performance/disk_io/last_seven_days/' + disk_uuid
+        }
+
+        $.ajax({
+            url : url,
+            type : 'GET',
+            contentType: "application/json; charset=utf-8",
+            dataType: 'json',
+            error : function() {
+                alter_danger('获取图表数据失败失败!');
+            },
+            success : function(data, textStatus, xhr) {
+
+                if (data.data.length > 1) {
+                    if ((data.data[data.data.length - 1]['timestamp'] - data.data[0]['timestamp']) >= 86400) {
+                        datetime_format_type = 'date';
+                    }
+                }
+
+                var option = {
+                    color: ['#3BC0FF', '#FAB406'],
+                    title: {
+                        text: '磁盘IOPS - ' + dev_name
+                    },
+                    legend: {
+                        data:['读(IOPS)', '写(IOPS)'],
+                        bottom: 5
+                    },
+                    toolbox: {
+                        feature: {
+                            dataZoom: {
+                                yAxisIndex: 'none'
+                            },
+                            magicType: {type: ['line', 'bar']},
+                            saveAsImage: {
+                                show: true,
+                                pixelRatio: 4
+                            }
+                        }
+                    },
+                    tooltip: {
+                        show: true,
+                        trigger: 'axis',
+                        formatter: (function (params, ticket, callback) {
+                            var readSeriesName = params[0].seriesName;
+                            var writeSeriesName = params[1].seriesName;
+                            var read = params[0].data[1];
+                            var write = params[1].data[1];
+                            var readMarker = params[0].marker;
+                            var writeMarker = params[1].marker;
+                            var datetime = new Date(params[0].data[0]).format('yyyy-mm-dd HH:MM:ss');
+
+                            if (read === null) {
+                                read = '无数据';
+                            }
+
+                            if (write === null) {
+                                write = '无数据';
+                            }
+
+                            return datetime + '<br />' + [readMarker, readSeriesName, read].join(' ') +
+                                '<br />' + [writeMarker, writeSeriesName, write].join(' ')
+                        })
+                    },
+                    xAxis: {
+                        type: 'time',
+                        axisTick: {
+                            show: false
+                        },
+                        axisLine: {
+                            lineStyle: {
+                                opacity: 0.1,
+                                color: '#3d4245'
+                            }
+                        },
+                        splitLine: {
+                            show: false
+                        },
+                        axisLabel: {
+                            formatter: format_time
+                        }
+                    },
+                    yAxis: {
+                        min: 0,
+                        max: 'dataMax',
+                        minInterval: 2,
+                        axisTick: {
+                            show: false
+                        },
+                        axisLine: {
+                            lineStyle: {
+                                opacity: 0,
+                                color: '#8c8f91'
+                            }
+                        },
+                        splitLine: {
+                            show: true,
+                            lineStyle: {
+                                width: 1,
+                                opacity: 0.5
+                            }
+                        }
+                    },
+                    series: [{
+                        name: '读(IOPS)',
+                        type: 'line',
+                        showSymbol: false,
+                        smooth: true,
+                        lineStyle: {
+                            normal: {
+                                width: 1
+                            }
+                        },
+                        data: (function () {
+                            var d = [];
+                            $.each(data.data, function(i, ele) {
+                                d.push([
+                                    ele['timestamp'] * 1000,
+                                    ele['rd_req'] !== null ? ele['rd_req'] : null
+                                ])
+                            });
+                            return d;
+                        })()
+                    },{
+                        name: '写(IOPS)',
+                        type: 'line',
+                        showSymbol: false,
+                        smooth: true,
+                        lineStyle: {
+                            normal: {
+                                width: 1
+                            }
+                        },
+                        data: (function () {
+                            var d = [];
+                            $.each(data.data, function(i, ele) {
+                                d.push([
+                                    ele['timestamp'] * 1000,
+                                    ele['wr_req'] !== null ? ele['wr_req'] : null
+                                ])
+                            });
+                            return d;
+                        })()
+                    }]
+                };
+
+                var disk_chart = echarts.init(document.getElementById(dev_id));
+                disk_chart.setOption(option);
+            }
+        });
+    }
+
+    function render_disks_chart(uuid, granularity) {
+        var dev_table = [];
+
+        for (var i = 1; i <= 26; i++) {
+            dev_table.push('vd' + String.fromCharCode(0x60+i));
+        }
+
+        var dev_name = dev_table[{{ disk_ret.data.sequence }}];
+        var dev_id_throughput = 'disk_throughput_chart';
+        var dev_id_iops = 'disk_iops_chart';
+
+        render_disk_throughput_chart(uuid, granularity, dev_id_throughput, dev_name);
+        render_disk_iops_chart(uuid, granularity, dev_id_iops, dev_name);
+    }
+
+    $(document).ready(function() {
+        $('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);
+        });
+
+        render_disks_chart(uuid, 'hour')
+    });
+    
+    function refresh_render_with_specify_granularity(granularity) {
+        render_disks_chart(uuid, granularity)
+    }
+</script>
+<div class="container" style="padding-top: 100px; width: 90%; max-width: 100%;">
+    <div class="panel">
+        <div class="panel-body">
+            <a href="javascript:history.go(-1)" class="btn btn-xs btn-default add-page-transition" data-transition="pt-page-moveFromLeft-init" style="margin-bottom: 4px; margin-left: 10px;">
+                <span class="glyph-icon icon-separator" style="transform: rotateY(-180deg);">
+                    <i class="glyph-icon icon-level-up"></i>
+                </span>
+                <span class="button-content">
+                    返回
+                </span>
+            </a>
+            <div class="row" style="margin-top: 10px;">
+                <div class="col-xs-4">
+                    <div style="background: #F5f6FA; border: 1px solid #e1e6eb; padding: 10px; font-size: 14px; width: 100%;">
+                        <span>
+                        磁盘信息
+                        </span>
+                    </div>
+                    <table class="table table-bordered">
+                        <tbody>
+                        <tr>
+                            <td>
+                                <span class="guest-label">UUID:&nbsp;&nbsp;&nbsp;&nbsp;</span>
+                                <span class="guest-desc">
+                                {{ disk_ret.data.uuid }}
+                                </span>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>
+                                <span class="guest-label">名称:&nbsp;&nbsp;&nbsp;&nbsp;</span>
+                                <span class="guest-desc">
+                                {{ disk_ret.data.remark }}
+                                </span>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>
+                                <span class="guest-label">状态:&nbsp;&nbsp;&nbsp;&nbsp;</span>
+                                <span class="guest-desc">
+                                {{ format_disk_state(disk_ret.data.state)|safe }}
+                                </span>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>
+                                <span class="guest-label">磁盘属性:&nbsp;&nbsp;&nbsp;&nbsp;</span>
+                                <span class="guest-desc">
+                                    {% if disk_ret.data.sequence == 0 %}
+                                        系统盘
+                                    {% else %}
+                                        数据盘
+                                    {% endif %}
+                                </span>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>
+                                <span class="guest-label">磁盘容量:&nbsp;&nbsp;&nbsp;&nbsp;</span>
+                                <span class="guest-desc">
+                                {{ disk_ret.data.size }} GB
+                                </span>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>
+                                <span class="guest-label">被挂载于:&nbsp;&nbsp;&nbsp;&nbsp;</span>
+                                <span class="guest-desc">
+                                    {% if disk_ret.data.sequence != -1 %}
+                                        <a href="/guest/detail/{{ disk_ret.data.guest_uuid }}">{{ guest_ret.data.label }}/{{ guest_ret.data.remark }}</a>
+                                    {% else %}
+                                    {% endif %}
+                                </span>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>
+                                <span class="guest-label">设备号:&nbsp;&nbsp;&nbsp;&nbsp;</span>
+                                <span class="guest-desc">
+                                {{ format_sequence_to_device_name(disk_ret.data.sequence) }}
+                                </span>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>
+                                <span class="guest-label">创建时间:&nbsp;&nbsp;&nbsp;&nbsp;</span>
+                                <span class="guest-desc">
+                                {{ format_datetime_by_tus(disk_ret.data.create_time) }}
+                                </span>
+                            </td>
+                        </tr>
+                        </tbody>
+
+                    </table>
+                </div>
+                <div class="col-xs-8">
+                    <div class="row" style="margin-bottom: 20px;">
+                        <div class="col-xs-8"></div>
+                        <div class="col-xs-4">
+                            <div class="btn-group btn-group-justified btn-group-xs">
+                                <a class="btn btn-default" role="button" onclick="refresh_render_with_specify_granularity('hour');">1小时</a>
+                                <a class="btn btn-default" role="button" onclick="refresh_render_with_specify_granularity('six_hours');">6小时</a>
+                                <a class="btn btn-default" role="button" onclick="refresh_render_with_specify_granularity('day');">1天</a>
+                                <a class="btn btn-default" role="button" onclick="refresh_render_with_specify_granularity('seven_days');">7天</a>
+                            </div>
+                        </div>
+                    </div>
+                    <div>
+                        <div id="disk_throughput_chart" style="width: 800px;height:280px;"></div>
+                        <div id="disk_iops_chart" style="width: 800px;height:280px;"></div>
+                    </div>
+                    <div id="disks_chart"></div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+{% endblock body %}

+ 0 - 1
templates/disk_show.html

@@ -1,5 +1,4 @@
 {% extends "layout.html" %}
-{% block title %} Disk {% endblock %}
 {% block head %}
     {{ super() }}
     <style type="text/css">

+ 0 - 1
templates/guest_create.html

@@ -1,5 +1,4 @@
 {% extends "layout.html" %}
-{% block title %} Guest {% endblock %}
 {% block head %}
     {{ super() }}
 

+ 2 - 3
templates/guest_detail.html

@@ -1,5 +1,4 @@
 {% extends "layout.html" %}
-{% block title %} Guest {% endblock %}
 {% block head %}
     {{ super() }}
 
@@ -877,13 +876,13 @@
     function refresh_render_with_specify_granularity(granularity) {
         render_cpu_chart(uuid, granularity);
         render_traffic_chart(uuid, granularity);
-        render_disks_chart(uuid, 'hour')
+        render_disks_chart(uuid, granularity)
     }
 </script>
 <div class="container" style="padding-top: 100px; width: 90%; max-width: 100%;">
     <div class="panel">
         <div class="panel-body">
-            <a href="/guests" class="btn btn-xs btn-default add-page-transition" data-transition="pt-page-moveFromLeft-init" style="margin-bottom: 4px; margin-left: 10px;">
+            <a href="javascript:history.go(-1)" class="btn btn-xs btn-default add-page-transition" data-transition="pt-page-moveFromLeft-init" style="margin-bottom: 4px; margin-left: 10px;">
                 <span class="glyph-icon icon-separator" style="transform: rotateY(-180deg);">
                     <i class="glyph-icon icon-level-up"></i>
                 </span>

+ 0 - 1
templates/guest_show.html

@@ -1,5 +1,4 @@
 {% extends "layout.html" %}
-{% block title %} Guest {% endblock %}
 {% block head %}
     {{ super() }}
     <style type="text/css">

+ 1 - 1
templates/layout.html

@@ -34,7 +34,7 @@
 
     <meta charset="UTF-8">
     <!--[if IE]><meta http-equiv='X-UA-Compatible' content='IE=edge,chrome=1'><![endif]-->
-    <title>{% block title %}{% endblock %} - JimV </title>
+    <title>JimV</title>
     <meta name="description" content="">
     <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=yes">
 

+ 0 - 1
templates/success.html

@@ -1,5 +1,4 @@
 {% extends "layout.html" %}
-{% block title %} 成功 {% endblock %}
 {% block head %}
     {{ super() }}
 <style type="text/css">

+ 26 - 0
views/disk.py

@@ -136,3 +136,29 @@ def create():
         return render_template('disk_create.html')
 
 
+def detail(uuid):
+    host_url = request.host_url.rstrip('/')
+
+    disk_url = host_url + url_for('api_disks.r_get', uuids=uuid)
+
+    disk_ret = requests.get(url=disk_url)
+    disk_ret = json.loads(disk_ret.content)
+
+    guest_ret = None
+    os_template_ret = None
+
+    if disk_ret['data']['sequence'] != -1:
+        guest_url = host_url + url_for('api_guests.r_get', uuids=disk_ret['data']['guest_uuid'])
+
+        guest_ret = requests.get(url=guest_url)
+        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_ret = requests.get(url=os_template_url)
+        os_template_ret = json.loads(os_template_ret.content)
+
+    return render_template('disk_detail.html', uuid=uuid, guest_ret=guest_ret, os_template_ret=os_template_ret,
+                           disk_ret=disk_ret)
+
+

+ 1 - 0
views_route_table.py

@@ -20,4 +20,5 @@ add_rule_views(guest.blueprint, '/detail/<uuid>', views_func='guest.detail', met
 
 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'])