25

授之以渔-运维平台应用模块一(应用树篇)-20170719

 5 years ago
source link: https://blog.51cto.com/qdream/2405083
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

授之以渔-运维平台应用模块一(应用树篇)

写在片头:干的是运维工作,爱好动手。纯属个人项目,身兼业务需求人员,产品经理,前端,后端,测试于一体,代码层面会有逻辑问题,请各位看官见谅,下文只是记录我一个10手码农在前端踩过的坑和一些思路。

###应用树: 为了方便管理以业务为单位的服务集群,利用树形结构建立起了一种服务组织关系,方便运维日常管理。先上一张效果图:

授之以渔-运维平台应用模块一(应用树篇)

#一、 Jstree
看过XX公司的运维平台的应用树后,从此不能自拔,励志也要写出一样效果的东东。采用的jsTree,是基于javascript的一个跨浏览器树控件树行,功能强大,最重要的是免费,
左侧的树列表的生成因为会用到mysql的group by,所以采用了redis存储,需要的时候才更新服务树,减少对数据库的压力,项目用到了根据选择树节点点击事件功能。
既点击项目名称的红×××标时,会加载整个项目涉及到的服务器,效果图:

image.png

点击项目下单个主机时,会加载这个服务器的应用情况,效果图:

image.png

代码如下:

("changed.jstree", function (e, data) {
                var items = data.node.text;
                var icon = data.node.icon;
                if (icon == "fa fa-building-o font-blue"){
                    document.getElementById("group1").style.display="none";
                    document.getElementById("group2").style.display="block";
                    $.ajax({
                        type: "GET",
                        url: "../cmdb_app_info_ajax/?ip="+items,
                        dataType:'json',
                        async: false,
                        beforeSend:function(){
                            Metronic.blockUI({animate: true});
                        },
                        complete: function() {
                            Metronic.unblockUI();
                        },
                        success: function(data){
                           var  sOut= '';
                                sOut += '<tr>'
                                sOut += '<td>' + data['设备PHP版本号:'] + '</td>';
                                sOut += '<td>' + data['设备PHP路径:'] + '</td>';
                                sOut += '<td>' + data['设备PHP端口号:'] + '</td>';
                                sOut += '<tr>'
                                sOut += '<th><b>TOMCAT版本号</b></th>';
                                sOut += '<th><b>TOMCAT路径</b></th>';
                                sOut += '<th><b>TOMCAT端口号</b></th>';
                                sOut += '</tr>'
                                sOut += '<td>' + data['设备TOMCAT版本号:'] + '</td>';
                                sOut += '<td>' + data['设备TOMCAT路径:'] + '</td>';
                                sOut += '<td>' + data['设备TOMCAT端口号:'] + '</td>';
                                sOut += '<tr>'
                                sOut += '<th><b>NGINX版本号</b></th>';
                                sOut += '<th><b>NGINX路径</b></th>';
                                sOut += '<th><b>NGINX端口号</b></th>';
                                sOut += '</tr>'
                                sOut += '<td>' + data['设备NGINX版本号:'] + '</td>';
                                sOut += '<td>' + data['设备NGINX路径:'] + '</td>';
                                sOut += '<td>' + data['设备NGINX端口号:'] + '</td>';
                                sOut += '<tr>'
                                sOut += '<th><b>MYSQL版本号</b></th>';
                                sOut += '<th><b>MYSQL路径</b></th>';
                                sOut += '<th><b>MYSQL端口号</b></th>';
                                sOut += '<th><b>MYSQL数据库</b></th>';
                                sOut += '</tr>'
                                sOut += '<td>' + data['设备MYSQL版本号:'] + '</td>';
                                sOut += '<td>' + data['设备MYSQL路径:'] + '</td>';
                                sOut += '<td>' + data['设备MYSQL端口号:'] + '</td>';
                                sOut += '<td>' + data['设备MYSQL数据库:'] + '</td>';
                                sOut += '<tr>'
                                sOut += '<th><b>定时任务</b></th>';
                                sOut += '</tr>'
                                sOut += '<td>' + data['设备定时任务:'] + '</td>';
                                sOut += '</tr>'

                            $("#cmdb_app_info_get").html(sOut);

                            },
                        error: function (data) {
                            toastr.error('没有数据可以加载')
                        }
                    });
                }else if (icon == "fa fa-folder icon-state-warning icon-lg"){
                    document.getElementById("group1").style.display="block";
                    document.getElementById("group2").style.display="none";
                    $.ajax({
                        type: "GET",
                        url: "../cmdb_subtitle_info_ajax/?subtitle="+items,
                        dataType:'json',
                        async: false,
                        beforeSend:function(){
                            Metronic.blockUI({animate: true});
                        },
                        complete: function() {
                            Metronic.unblockUI();
                        },
                        success: function(data){
                            if ($('#product_tree').hasClass('dataTable')) {
                                console.log('重新加载datatable,准备初始化................')
                                $('#product_tree').dataTable().fnClearTable(false) //清空一下table
                                $('#product_tree').dataTable().fnDestroy();//还原初始化datatable
                            }
                            $("#cmdb_subtitle_info_get").html("");
                            $.each(data,function() {
                            var  sOut= '';
                                sOut += '<tr>'
                                sOut += '<td><input type="checkbox" class="checkboxes" value="1"/></td>'
                                sOut += '<td id='+this['fields']['unit_title']+'>' + '<a href="../cmdb_assets_list/?place=&status=&year=&type=&model=&search='+ this['fields']['unit_ip'] +'">' + this['fields']['unit_title'] + '</a>' + '</td>';
                                sOut += '<td>' + this['fields']['unit_subtitle'] + '</td>';
                                sOut += '<td id='+this['fields']['unit_ip']+'>' + this['fields']['unit_ip'] + '</td>';
                                sOut += '<td>' + this['fields']['unit_use_type'] + '</td>';                            
                                sOut += '<td><a class="fa fa-eye" href="#responsives" data-toggle="modal"></a></td>';
                                sOut += '</tr>'
                                $("#cmdb_subtitle_info_get").append(sOut);

                            });
                        },
                        error: function (data) {
                            toastr.error('没有数据可以加载')
                        }
                    });
                }
                $(document).ready(function(){
                    eyeClick();
                    TableManaged.init() //初始化datatables
                })

            }

捡一些重要的说:

var items = data.node.text;  //获取图标名称
var icon = data.node.icon;  //获取图标

我是通过判断图标的样式来获取用户点击的是哪里,没办法,谁叫咱是10手码农。同时有group1和gourp2两个tables,选择哪个图标就弹出哪个tables,隐藏另外的,在通过ajax后台获取数据,利用each循环获得的结果,构建出一个tr的内容,最后在添加到tables里,以此实现“山寨版”的数据加载。
####这里踩过的坑:
因为我要先加载完数据,然后才是初始化datatables表格。当点击到别的业务图标时,又要经历一次加载数据,然后初始化表格。结果datatables报错了....cannot reinitialise datatable,大概意思就是datatables不能重复初始化。
最后只能通过判断加载后的tables是否被加载后,如果加载过,先销毁,在初始化。

if ($('#product_tree').hasClass('dataTable')) {
    console.log('重新加载datatable,准备初始化................')
    $('#product_tree').dataTable().fnClearTable(false) //清空一下table
    $('#product_tree').dataTable().fnDestroy();//还原初始化datatable
}

#二、 Datatable
右边就是数据展示采用datatables,可以将任何HTML表格添加高级的交互功能,这里用到了checkbox的单选和多选。
datatables代码如下:

var TableManaged = function () {

    var initTable2 = function () {

        var table = $('#product_tree');
        table.dataTable({
            "bDestroy": true,
            "language": {
                "aria": {
                    "sortAscending": ": activate to sort column ascending",
                    "sortDescending": ": activate to sort column descending"
                },
                "emptyTable": "未有相关数据",
                "info": "当前显示 _START_ 到 _END_ 条,共 _TOTAL_ 条记录。",
                "infoEmpty": "当前显示0到0条,共0条记录",
                "infoFiltered": "(数据库中共为 _MAX_ 条记录)",
                "lengthMenu": "显示 _MENU_ 记录",
                "search": "模糊查询:",
                "zeroRecords": "对不起,查询不到任何相关数据",
                "oPaginate": {
                    "sFirst": "首页",
                    "sPrevious": " 上一页 ",
                    "sNext": " 下一页 ",
                    "sLast": " 尾页 "
                }
            },

            "bStateSave": true, // save datatable state(pagination, sort, etc) in cookie.

            "lengthMenu": [
                [5, 15, 20, -1],
                [5, 15, 20, "All"] // change per page values here
            ],
            // set the initial value
            "pageLength": 5,

            "columnDefs": [{  // set default column settings
                'orderable': false,
                'targets': [0]
            }, {
                "searchable": false,
                "targets": [0]
            }],
            "order": [
                [1, "asc"]
            ] // set first column as a default sort by asc
        });

        var tableWrapper = jQuery('#product_tree_wrapper');

        var host_list = [];
        var release_build_job = '';

        //全选
        table.find('.group-checkable').change(function () {
            var set = jQuery(this).attr("data-set");
            var checked = jQuery(this).is(":checked");
            host_list.length = 0;
            jQuery(set).each(function () {
                if (checked) {
                    $(this).attr("checked", true);
                    var hosts = $(this).parent().parent().find('td').eq(1).attr("id");
                    host_list.push(hosts);
                    document.getElementById("group3").style.display="block";
                } else {
                    $(this).attr("checked", false);
                    host_list.length = 0;
                    document.getElementById("group3").style.display="none";
                }
            });
            jQuery.uniform.update(set);

        });

        //单选
        table.on('change', 'tbody tr .checkboxes', function () {
            var hosts = $(this).parent().parent().find('td').eq(1).attr("id");
            var checked = jQuery(this).is(":checked");
            if (checked) {
                $(this).attr("checked", true);
                host_list.push(hosts);
                if (host_list.length > 1) {
                    document.getElementById("group3").style.display="block";
                }else {
                    document.getElementById("group3").style.display="none";
                }
            } else {
                $(this).attr("checked", false);
                host_list.pop(hosts);
                if (host_list.length > 1) {
                    document.getElementById("group3").style.display="block";
                }else {
                    document.getElementById("group3").style.display="none";
                }
            }

        });

        tableWrapper.find('.dataTables_length select').select2(); // initialize select2 dropdown

    return {

        //main function to initiate the module
        init: function () {
            if (!jQuery().dataTable) {
                return;
            }

            initTable2();
        }

    };

}();

捡一些重要的说:

if (host_list.length > 1) {
    document.getElementById("group3").style.display="block";
}else {
    document.getElementById("group3").style.display="none";
}

判断了checkbox选择的数量,大于1个的话,弹出gourp3(一个批量绘图按钮),效果图如下:

image.png

#三、 Multiselect
接上文,这里平台是结合了openfalcon做的监控,点击上文的监控指标项按钮,会弹出一个multiselect层,用过openfalcon的都知道,监控指标多是他的特色之一,所以在选型的时候第一个就想到得需要一个带filter功能的select控件,效果图如下:

image.png

代码如下:

            $('#get_hostlist_counter_select').multiselect({
                    buttonWidth: '500px',//按钮宽度
                    nonSelectedText: '---- 请选择监控指标 ----',
                    nSelectedText: '个被选中',
                    enableCaseInsensitiveFiltering: true,//不区分大小写
                    filterPlaceholder: '模糊查询',
                    enableFiltering: true,
                    onDropdownShow: function(event) {
                        get_hostlist_counter()
                    },
                    onChange: function (option, checked, select) {
                        var selectedOptions = $('#get_hostlist_counter_select option:selected');
                        if (selectedOptions.length >= 6) {
                            // 禁用选项
                            toastr.error('监控指标最多同时选取6个')
                            var nonSelectedOptions = $('#get_hostlist_counter_select option').filter(function() {
                            return !$(this).is(':selected');
                            });

                            nonSelectedOptions.each(function() {
                            var input = $('input[value="' + $(this).val() + '"]');
                            input.prop('disabled', true);
                            input.parent('li').addClass('disabled');
                            });
                        }
                        else {
                            // 启动选项
                            $('#get_hostlist_counter_select option').each(function() {
                            var input = $('input[value="' + $(this).val() + '"]');
                            input.prop('disabled', false);
                            input.parent('li').addClass('disabled');
                            });
                        }
                    }
            });

捡重要的说:
用到了multiselectonDropdownShowonChange两个回调函数,onDropdownShow可以理解成就是点击菜单下拉事件,触发了get_hostlist_counter()通过ajax取数据填充options,代码如下:

        function get_hostlist_counter(){
                var options_list = []
                var obj2 = new Object();
                var jsonData ={
                    'unit_title': host_list.join(','),
                };
                $("#get_hostlist_counter_select").empty();

                $.ajax({
                    async: false,
                    type: "POST",
                    url : "../openfalcon_get_endpoint_counter_ajax/",
                    data: jsonData,
                    cache: false,
                    dataType: "json",
                    beforeSend:function(){
                        Metronic.blockUI({animate: true});
                    },
                    complete: function() {
                        Metronic.unblockUI();
                    },
                    success: function(obj) {
                        if (obj['counter'] == ''){
                            toastr.error('获取监控指标为空或者异常')
                        }else{
                            for (var id in obj['counter']){
                                    obj2 = {
                                          label : obj['counter'][id],
                                          value : obj['counter'][id],
                                      };
                                options_list.push(obj2)
                                }
                            $('#get_hostlist_counter_select').multiselect('dataprovider', options_list);
                            }
                            }
                        });
            }

onChange可以理解成点击事件,监控指标这块还是怕传多了,压垮了openfalcongraph的,所以限制最多传6个,超过的话会禁用所有选项。
#四、 绘图
最后就是一些绘图了,结合着openfalcon的API做的,下篇文章再议,效果图:

image.png

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK