├── .idea ├── .name ├── dataSources.xml ├── dictionaries │ └── liuhui.xml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── lh-falcon.iml ├── markdown-navigator.xml ├── markdown-navigator │ └── profiles_settings.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── README.md ├── agent ├── README.md ├── __init__.py ├── conf │ ├── __init__.py │ └── agent.ini ├── funcs │ ├── __init__.py │ ├── cpustat.py │ ├── host.py │ ├── ifstat.py │ ├── loadavg.py │ └── meminfo.py └── http │ ├── __init__.py │ └── data_report.py ├── alarm ├── __init__.py ├── admin.py ├── apps.py ├── forms.py ├── migrations │ └── __init__.py ├── models.py ├── tests.py ├── urls │ ├── __init__.py │ ├── api_urls.py │ └── view_urls.py └── views │ ├── __init__.py │ ├── alarm_event.py │ ├── trigger.py │ └── trigger_condition.py ├── common ├── __init__.py ├── admin.py ├── api │ ├── __init__.py │ └── asset.py ├── apps.py ├── forms.py ├── graph.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_auto_20180601_0801.py │ ├── 0003_auto_20180601_1149.py │ └── __init__.py ├── mixins.py ├── models.py ├── serializers.py ├── templatetags │ ├── __init__.py │ └── mytags.py ├── tests.py ├── urls │ ├── __init__.py │ ├── api_urls.py │ └── view_urls.py └── views │ ├── __init__.py │ ├── host.py │ ├── hostgroup.py │ ├── items.py │ └── template.py ├── dashboard ├── __init__.py ├── admin.py ├── apps.py ├── migrations │ └── __init__.py ├── models.py ├── tests.py ├── urls.py └── views.py ├── lh-falcon ├── __init__.py ├── settings.py ├── urls.py └── wsgi.py ├── manage.py ├── media └── default.png ├── static ├── css │ ├── bootstrap.css │ ├── bootstrap.css.map │ ├── bootstrap.min.css │ ├── bootstrap.min.css.map │ ├── dataTables.bootstrap.css │ ├── dataTables.bootstrap.min.css │ ├── font-awesome.css │ ├── font-awesome.css.map │ ├── font-awesome.min.css │ ├── host.css │ ├── index.css │ ├── ionicons.css │ ├── ionicons.min.css │ ├── jquery-jvectormap.css │ ├── morris.css │ ├── select2.css │ ├── select2.min.css │ └── table.css ├── dist │ ├── css │ │ ├── AdminLTE.css │ │ ├── AdminLTE.min.css │ │ ├── adminlte.css.map │ │ ├── adminlte.min.css.map │ │ ├── alt │ │ │ ├── AdminLTE-bootstrap-social.css │ │ │ ├── AdminLTE-bootstrap-social.min.css │ │ │ ├── AdminLTE-fullcalendar.css │ │ │ ├── AdminLTE-fullcalendar.min.css │ │ │ ├── AdminLTE-select2.css │ │ │ ├── AdminLTE-select2.min.css │ │ │ ├── AdminLTE-without-plugins.css │ │ │ └── AdminLTE-without-plugins.min.css │ │ └── skins │ │ │ ├── _all-skins.css │ │ │ ├── _all-skins.min.css │ │ │ ├── skin-black-light.css │ │ │ ├── skin-black-light.min.css │ │ │ ├── skin-black.css │ │ │ ├── skin-black.min.css │ │ │ ├── skin-blue-light.css │ │ │ ├── skin-blue-light.min.css │ │ │ ├── skin-blue.css │ │ │ ├── skin-blue.min.css │ │ │ ├── skin-green-light.css │ │ │ ├── skin-green-light.min.css │ │ │ ├── skin-green.css │ │ │ ├── skin-green.min.css │ │ │ ├── skin-purple-light.css │ │ │ ├── skin-purple-light.min.css │ │ │ ├── skin-purple.css │ │ │ ├── skin-purple.min.css │ │ │ ├── skin-red-light.css │ │ │ ├── skin-red-light.min.css │ │ │ ├── skin-red.css │ │ │ ├── skin-red.min.css │ │ │ ├── skin-yellow-light.css │ │ │ ├── skin-yellow-light.min.css │ │ │ ├── skin-yellow.css │ │ │ └── skin-yellow.min.css │ ├── img │ │ ├── avatar.png │ │ ├── avatar04.png │ │ ├── avatar2.png │ │ ├── avatar3.png │ │ ├── avatar5.png │ │ ├── boxed-bg.jpg │ │ ├── boxed-bg.png │ │ ├── credit │ │ │ ├── american-express.png │ │ │ ├── cirrus.png │ │ │ ├── mastercard.png │ │ │ ├── mestro.png │ │ │ ├── paypal.png │ │ │ ├── paypal2.png │ │ │ └── visa.png │ │ ├── default-50x50.gif │ │ ├── icons.png │ │ ├── photo1.png │ │ ├── photo2.png │ │ ├── photo3.jpg │ │ ├── photo4.jpg │ │ ├── user1-128x128.jpg │ │ ├── user2-160x160.jpg │ │ ├── user3-128x128.jpg │ │ ├── user4-128x128.jpg │ │ ├── user5-128x128.jpg │ │ ├── user6-128x128.jpg │ │ ├── user7-128x128.jpg │ │ └── user8-128x128.jpg │ └── js │ │ ├── adminlte.js │ │ ├── adminlte.min.js │ │ ├── demo.js │ │ └── pages │ │ ├── dashboard.js │ │ └── dashboard2.js ├── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ ├── fontawesome-webfont.woff2 │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ ├── glyphicons-halflings-regular.woff2 │ ├── ionicons.eot │ ├── ionicons.svg │ ├── ionicons.ttf │ └── ionicons.woff ├── images │ ├── Flow.png │ ├── login.jpg │ ├── login_img.png │ ├── logo.png │ └── sprite_0.png ├── js │ ├── Chart.js │ ├── Chart.min.js │ ├── bootstrap.js │ ├── bootstrap.min.js │ ├── common.js │ ├── dataTables.bootstrap.js │ ├── dataTables.bootstrap.min.js │ ├── dist │ │ ├── core.js │ │ ├── jquery.js │ │ ├── jquery.min.js │ │ ├── jquery.min.map │ │ ├── jquery.slim.js │ │ ├── jquery.slim.min.js │ │ └── jquery.slim.min.map │ ├── fastclick.js │ ├── jquery.dataTables.js │ ├── jquery.dataTables.min.js │ ├── jquery.flot.categories.js │ ├── jquery.flot.js │ ├── jquery.flot.pie.js │ ├── jquery.flot.resize.js │ ├── jquery.js │ ├── jquery.min.js │ ├── jquery.slimscroll.js │ ├── jquery.slimscroll.min.js │ ├── jquery.sparkline.js │ ├── jquery.sparkline.min.js │ ├── jquery2.2.2.min.js │ ├── login.js │ ├── moment.min.js │ ├── morris.js │ ├── morris.min.js │ ├── raphael.js │ ├── raphael.min.js │ ├── select2.full.js │ └── select2.full.min.js └── plugins │ ├── bootstrap-slider │ ├── bootstrap-slider.js │ └── slider.css │ ├── bootstrap-wysihtml5 │ ├── bootstrap3-wysihtml5.all.js │ ├── bootstrap3-wysihtml5.all.min.js │ ├── bootstrap3-wysihtml5.css │ └── bootstrap3-wysihtml5.min.css │ ├── iCheck │ ├── all.css │ ├── flat │ │ ├── _all.css │ │ ├── aero.css │ │ ├── aero.png │ │ ├── aero@2x.png │ │ ├── blue.css │ │ ├── blue.png │ │ ├── blue@2x.png │ │ ├── flat.css │ │ ├── flat.png │ │ ├── flat@2x.png │ │ ├── green.css │ │ ├── green.png │ │ ├── green@2x.png │ │ ├── grey.css │ │ ├── grey.png │ │ ├── grey@2x.png │ │ ├── orange.css │ │ ├── orange.png │ │ ├── orange@2x.png │ │ ├── pink.css │ │ ├── pink.png │ │ ├── pink@2x.png │ │ ├── purple.css │ │ ├── purple.png │ │ ├── purple@2x.png │ │ ├── red.css │ │ ├── red.png │ │ ├── red@2x.png │ │ ├── yellow.css │ │ ├── yellow.png │ │ └── yellow@2x.png │ ├── futurico │ │ ├── futurico.css │ │ ├── futurico.png │ │ └── futurico@2x.png │ ├── icheck.js │ ├── icheck.min.js │ ├── line │ │ ├── _all.css │ │ ├── aero.css │ │ ├── blue.css │ │ ├── green.css │ │ ├── grey.css │ │ ├── line.css │ │ ├── line.png │ │ ├── line@2x.png │ │ ├── orange.css │ │ ├── pink.css │ │ ├── purple.css │ │ ├── red.css │ │ └── yellow.css │ ├── minimal │ │ ├── _all.css │ │ ├── aero.css │ │ ├── aero.png │ │ ├── aero@2x.png │ │ ├── blue.css │ │ ├── blue.png │ │ ├── blue@2x.png │ │ ├── green.css │ │ ├── green.png │ │ ├── green@2x.png │ │ ├── grey.css │ │ ├── grey.png │ │ ├── grey@2x.png │ │ ├── minimal.css │ │ ├── minimal.png │ │ ├── minimal@2x.png │ │ ├── orange.css │ │ ├── orange.png │ │ ├── orange@2x.png │ │ ├── pink.css │ │ ├── pink.png │ │ ├── pink@2x.png │ │ ├── purple.css │ │ ├── purple.png │ │ ├── purple@2x.png │ │ ├── red.css │ │ ├── red.png │ │ ├── red@2x.png │ │ ├── yellow.css │ │ ├── yellow.png │ │ └── yellow@2x.png │ ├── polaris │ │ ├── polaris.css │ │ ├── polaris.png │ │ └── polaris@2x.png │ └── square │ │ ├── _all.css │ │ ├── aero.css │ │ ├── aero.png │ │ ├── aero@2x.png │ │ ├── blue.css │ │ ├── blue.png │ │ ├── blue@2x.png │ │ ├── green.css │ │ ├── green.png │ │ ├── green@2x.png │ │ ├── grey.css │ │ ├── grey.png │ │ ├── grey@2x.png │ │ ├── orange.css │ │ ├── orange.png │ │ ├── orange@2x.png │ │ ├── pink.css │ │ ├── pink.png │ │ ├── pink@2x.png │ │ ├── purple.css │ │ ├── purple.png │ │ ├── purple@2x.png │ │ ├── red.css │ │ ├── red.png │ │ ├── red@2x.png │ │ ├── square.css │ │ ├── square.png │ │ ├── square@2x.png │ │ ├── yellow.css │ │ ├── yellow.png │ │ └── yellow@2x.png │ ├── input-mask │ ├── jquery.inputmask.date.extensions.js │ ├── jquery.inputmask.extensions.js │ ├── jquery.inputmask.js │ ├── jquery.inputmask.numeric.extensions.js │ ├── jquery.inputmask.phone.extensions.js │ ├── jquery.inputmask.regex.extensions.js │ └── phone-codes │ │ ├── phone-be.json │ │ ├── phone-codes.json │ │ └── readme.txt │ ├── jQueryUI │ ├── jquery-ui.js │ └── jquery-ui.min.js │ ├── jvectormap │ ├── jquery-jvectormap-1.2.2.css │ ├── jquery-jvectormap-1.2.2.min.js │ ├── jquery-jvectormap-usa-en.js │ └── jquery-jvectormap-world-mill-en.js │ ├── pace │ ├── pace.css │ ├── pace.js │ ├── pace.min.css │ └── pace.min.js │ └── timepicker │ ├── bootstrap-timepicker.css │ ├── bootstrap-timepicker.js │ ├── bootstrap-timepicker.min.css │ └── bootstrap-timepicker.min.js ├── templates ├── alarm │ ├── alarm_event_edit.html │ ├── alarm_event_list.html │ ├── trigger_add.html │ ├── trigger_condition_add.html │ ├── trigger_condition_edit.html │ ├── trigger_condition_list.html │ ├── trigger_edit.html │ └── trigger_list.html ├── base.html ├── grid.html ├── host │ ├── host_add.html │ ├── host_detail.html │ ├── host_edit.html │ ├── host_item.html │ ├── host_list.html │ ├── hostgroup_add.html │ ├── hostgroup_detail.html │ ├── hostgroup_edit.html │ ├── hostgroup_list.html │ ├── item_add.html │ ├── item_list.html │ ├── template_add.html │ ├── template_edit.html │ └── template_list.html ├── index.html ├── item.html └── users │ ├── login.html │ ├── user_add.html │ ├── user_detail.html │ ├── user_edit.html │ ├── user_list.html │ ├── usergroup_add.html │ ├── usergroup_detail.html │ ├── usergroup_edit.html │ └── usergroup_list.html ├── transfer ├── __init__.py ├── admin.py ├── apps.py ├── migrations │ └── __init__.py ├── models.py ├── rrd_graph.py ├── rrdtool_action.py ├── tests.py ├── urls.py └── views.py └── users ├── __init__.py ├── admin.py ├── apps.py ├── forms.py ├── migrations ├── 0001_initial.py └── __init__.py ├── models.py ├── tests.py ├── urls ├── __init__.py ├── api_urls.py └── view_urls.py └── views ├── __init__.py ├── user.py └── usergroup.py /.idea/.name: -------------------------------------------------------------------------------- 1 | lh-falcon -------------------------------------------------------------------------------- /.idea/dataSources.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | sqlite.xerial 6 | true 7 | true 8 | $PROJECT_DIR$/lh-falcon/settings.py 9 | org.sqlite.JDBC 10 | jdbc:sqlite:$PROJECT_DIR$/db.sqlite3 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/dictionaries/liuhui.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/lh-falcon.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | 30 | -------------------------------------------------------------------------------- /.idea/markdown-navigator/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | AngularJS 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lh-falcon 2 | * 基于Python开发的服务器监控平台。参考了小米的open-falcon架构设计思路,实现包括系统性能,端口和进程存活,应用服务等监控。 3 | 根据不同的告警的条件和策略及时给用户短信或邮件发送报警信息;实现监控数据的存储和查询;对不同服务器所需监控的指标和报警策略 4 | 阀值进行优雅的配置;监控数据的实时查询,将聚合的数据绘图展示给用户,让用户更好的对服务器性能有直观的了解。 5 | # 基本思路 6 | * 服务器监控平台要实现一个完整的监控流程,一条服务器监控数据的收集、到数据的转发和存储、到监控查询数据的绘图显示,到最后的报警 7 | 策略和条件的配置已经分组报警。涉及的主要功能如下: 8 | 1. Agent数据采集:实现agent部署所有机器上复杂采集服务器各种信息。比如:cpu、内存、磁盘、io、load、网络、端口存活、进程存活等,每隔一段时间push给transfer。 9 | 2. Transfer:数据中转站,主要负责将agent push上来的数据转发 10 | 3. Servers:接收到transfer转发来的数据,但只存储服务器大概的信息。并将每个host分到不同的hostgroup,方便其他组件查询以及分组配置。 11 | 4. Alarm报警:接收transfer转发来的数据,从Hbs中获取触发报警的条件和策略,并报警通知的用户和用户组。 12 | 5. Users用户管理:将用户分配到不同的UserGroup,给其他组件提供不同用户和用户组的操作,比如:报警时只需要指定某些人或某些组,并邮件通知。 13 | 6. Dashboard面板:面向用户的查询界面,用户可以看到push到graph中的所有数据,并查看其趋势图。 14 | -------------------------------------------------------------------------------- /agent/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /agent/conf/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /agent/conf/agent.ini: -------------------------------------------------------------------------------- 1 | [server] 2 | item_report_api = http://172.16.162.1:8000/api/items/report/ 3 | 4 | [interval] 5 | item = 60 6 | 7 | -------------------------------------------------------------------------------- /agent/funcs/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /agent/funcs/cpustat.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | class Cpustat: 5 | """get cpu infomations""" 6 | def __init__(self): 7 | self.cpu_user = self.get_cpu_user() 8 | self.cpu_nice = self.get_cpu_nice() 9 | self.cpu_system = self.get_cpu_system() 10 | self.cpu_idle = self.get_cpu_idle() 11 | self.cpu_iowait = self.get_cpu_iowait() 12 | self.cpu_irq = self.get_cpu_irq() 13 | self.cpu_softirq = self.get_cpu_softirq() 14 | self.cpu_steal = self.get_cpu_steal() 15 | self.cpu_guest = self.get_cpu_guest() 16 | self.cpu_switches= self.get_cpu_switches() 17 | 18 | def get_cpu_times(self): 19 | with open('/proc/stat') as f: 20 | lines = f.readlines()[0] 21 | return lines.split() 22 | 23 | def get_cpu_user(self): 24 | return int(self.get_cpu_times()[1]) 25 | 26 | def get_cpu_nice(self): 27 | return int(self.get_cpu_times()[2]) 28 | 29 | def get_cpu_system(self): 30 | return int(self.get_cpu_times()[3]) 31 | 32 | def get_cpu_idle(self): 33 | return int(self.get_cpu_times()[4]) 34 | 35 | def get_cpu_iowait(self): 36 | return int(self.get_cpu_times()[5]) 37 | 38 | def get_cpu_irq(self): 39 | return int(self.get_cpu_times()[6]) 40 | 41 | def get_cpu_softirq(self): 42 | return int(self.get_cpu_times()[7]) 43 | 44 | def get_cpu_steal(self): 45 | return int(self.get_cpu_times()[8]) 46 | 47 | def get_cpu_guest(self): 48 | return int(self.get_cpu_times()[9]) 49 | 50 | def get_cpu_switches(self): 51 | return int(self.get_cpu_times()[10]) 52 | 53 | 54 | -------------------------------------------------------------------------------- /agent/funcs/host.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import uuid 4 | import socket 5 | import platform 6 | 7 | 8 | class Host: 9 | """get host information to padding host model""" 10 | def __init__(self): 11 | self.hostname = self.get_hostname() 12 | self.ipaddress = self.get_ipaddress() 13 | self.macadddress = self.get_macipaddress() 14 | self.os_type = self.get_os_type() 15 | self.os_version = self.get_os_version() 16 | 17 | def get_hostname(self): 18 | """get hostname""" 19 | return socket.getfqdn(socket.gethostname()) 20 | 21 | def get_macipaddress(self): 22 | """get macipaddress""" 23 | mac_ip = uuid.UUID(int = uuid.getnode()).hex[-12:] 24 | return ':'.join([mac_ip[i:i+2] for i in range(0, 11, 2)]) 25 | 26 | def get_ipaddress(self): 27 | """get host ipaddress""" 28 | return socket.gethostbyname(self.hostname) 29 | 30 | def get_os_type(self): 31 | """get system type""" 32 | return platform.system() 33 | 34 | def get_os_version(self): 35 | """get system version""" 36 | return platform.version() 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /agent/funcs/ifstat.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | class IfStat: 5 | def get_if_stat_info(self): 6 | if_stat_info = {} 7 | with open('/proc/net/dev') as f: 8 | lines = f.readlines() 9 | for line in lines[2:]: 10 | line = line.split(':') 11 | addr_name, info = line[0].strip(), line[1].split() 12 | if_stat_info[addr_name] = info 13 | return if_stat_info 14 | 15 | def get_if_addrs(self): 16 | """get host all addrs""" 17 | return self.get_if_stat_info().keys() 18 | 19 | def get_net_if_in_sizes(self, addr): 20 | return int(self.get_if_stat_info()[addr][0]) 21 | 22 | def get_net_if_in_packets(self, addr): 23 | return int(self.get_if_stat_info()[addr][1]) 24 | 25 | def get_net_if_in_errors(self, addr): 26 | return int(self.get_if_stat_info()[addr][2]) 27 | 28 | def get_net_if_in_dropped(self, addr): 29 | return int(self.get_if_stat_info()[addr][3]) 30 | 31 | def get_net_if_in_fifo_errs(self, addr): 32 | return int(self.get_if_stat_info()[addr][4]) 33 | 34 | def get_net_if_in_frame_errs(self, addr): 35 | return int(self.get_if_stat_info()[addr][5]) 36 | 37 | def get_net_if_in_compressed_errs(self, addr): 38 | return int(self.get_if_stat_info()[addr][6]) 39 | 40 | def get_net_if_in_multicast(self, addr): 41 | return int(self.get_if_stat_info()[addr][7]) 42 | 43 | def get_net_if_out_sizes(self, addr): 44 | return int(self.get_if_stat_info()[addr][8]) 45 | 46 | def get_net_if_out_packets(self, addr): 47 | return int(self.get_if_stat_info()[addr][9]) 48 | 49 | def get_net_if_out_errors(self, addr): 50 | return int(self.get_if_stat_info()[addr][10]) 51 | 52 | def get_net_if_out_dropped(self, addr): 53 | return int(self.get_if_stat_info()[addr][11]) 54 | 55 | def get_net_if_out_fifo_errs(self, addr): 56 | return int(self.get_if_stat_info()[addr][12]) 57 | 58 | def get_net_if_out_frame_errs(self, addr): 59 | return int(self.get_if_stat_info()[addr][13]) 60 | 61 | def get_net_if_out_compressed_errs(self, addr): 62 | return int(self.get_if_stat_info()[addr][14]) 63 | 64 | def get_net_if_out_multicast(self, addr): 65 | return int(self.get_if_stat_info()[addr][15]) 66 | 67 | def get_net_if_total_sizes(self, addr): 68 | return self.get_net_if_in_sizes(addr) + self.get_net_if_out_sizes(addr) 69 | 70 | def get_net_if_total_dropped(self, addr): 71 | return self.get_net_if_in_dropped(addr) + self.get_net_if_out_dropped(addr) 72 | 73 | def get_net_if_total_errors(self, addr): 74 | return self.get_net_if_in_errors(addr) + self.get_net_if_out_errors(addr) 75 | 76 | def get_net_if_total_packets(self, addr): 77 | return self.get_net_if_in_packets(addr) + self.get_net_if_out_packets(addr) 78 | -------------------------------------------------------------------------------- /agent/funcs/loadavg.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | class LoadAvg: 5 | 6 | def __init__(self): 7 | self.load_1min = self.get_load_1min() 8 | self.load_5min = self.get_load_5min() 9 | self.load_15min = self.get_load_15min() 10 | 11 | def get_load_info(self): 12 | with open('/proc/loadavg') as f: 13 | lines = f.readlines() 14 | return lines 15 | 16 | def get_load_1min(self): 17 | return float(self.get_load_info()[0].split()[0]) 18 | 19 | def get_load_5min(self): 20 | return float(self.get_load_info()[0].split()[1]) 21 | 22 | def get_load_15min(self): 23 | return float(self.get_load_info()[0].split()[2]) -------------------------------------------------------------------------------- /agent/funcs/meminfo.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | 4 | class Meminfo: 5 | 6 | def __init__(self): 7 | self.mem_memtotal = self.get_mem_memtotal() 8 | self.mem_memused = self.get_mem_memused() 9 | self.mem_memused_percent = self.get_mem_memused_percent() 10 | self.mem_memfree = self.get_mem_memfree() 11 | self.mem_memfree_percent = self.get_mem_memfree_percent() 12 | self.mem_swaptotal = self.get_mem_swaptotal() 13 | self.mem_swapused = self.get_mem_swapused() 14 | self.mem_swapused_percent = self.get_mem_swapused_percent() 15 | self.mem_swapfree = self.get_mem_swapfree() 16 | self.mem_swapfree_percent = self.get_mem_swapfree_percent() 17 | 18 | def get_meminfo(self): 19 | meminfo = {} 20 | with open('/proc/meminfo') as f: 21 | lines = f.readlines() 22 | for line in lines: 23 | if line.startswith('MemFree'): 24 | meminfo['memfree'] = line.split()[1] 25 | if line.startswith('MemTotal'): 26 | meminfo['memtotal'] = line.split()[1] 27 | if line.startswith('Buffers'): 28 | meminfo['buffers'] = line.split()[1] 29 | if line.startswith('Cached'): 30 | meminfo['cached'] = line.split()[1] 31 | if line.startswith('SwapFree'): 32 | meminfo['swapfree'] = line.split()[1] 33 | if line.startswith('SwapTotal'): 34 | meminfo['swaptotal'] = line.split()[1] 35 | return meminfo 36 | 37 | def get_mem_memtotal(self): 38 | return int(self.get_meminfo()['memtotal']) 39 | 40 | def get_mem_memused(self): 41 | return self.get_mem_memtotal() - self.get_mem_memfree() 42 | 43 | def get_mem_memused_percent(self): 44 | return int(float('%.2f' % (self.get_mem_memused() / self.get_mem_memtotal())) * 100) 45 | 46 | def get_mem_memfree(self): 47 | meminfo = self.get_meminfo() 48 | return int(meminfo['memfree']) + int(meminfo['buffers']) + int(meminfo['cached']) 49 | 50 | def get_mem_memfree_percent(self): 51 | return int(float('%.2f' % (self.get_mem_memfree() / self.get_mem_memtotal())) * 100) 52 | 53 | def get_mem_swaptotal(self): 54 | return int(self.get_meminfo()['swaptotal']) 55 | 56 | def get_mem_swapused(self): 57 | return self.get_mem_swaptotal() - self.get_mem_swapfree() 58 | 59 | def get_mem_swapused_percent(self): 60 | return int(float('%.2f' % (self.get_mem_swapused() / self.get_mem_swaptotal())) * 100) 61 | 62 | def get_mem_swapfree(self): 63 | return int(self.get_meminfo()['swapfree']) 64 | 65 | def get_mem_swapfree_percent(self): 66 | return int(float('%.2f' % (self.get_mem_swapfree() / self.get_mem_swaptotal())) * 100) 67 | -------------------------------------------------------------------------------- /agent/http/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | -------------------------------------------------------------------------------- /alarm/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/alarm/__init__.py -------------------------------------------------------------------------------- /alarm/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /alarm/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class AlarmConfig(AppConfig): 5 | name = 'alarm' 6 | -------------------------------------------------------------------------------- /alarm/forms.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | from django import forms 4 | from django.forms.widgets import * 5 | 6 | from common.models import TriggerCondition, Trigger, AlarmEvent 7 | 8 | 9 | class TriggerForm(forms.ModelForm): 10 | class Meta: 11 | model = Trigger 12 | fields = ['item','host','expression','value','time'] 13 | widgets = { 14 | 'item': Select(attrs={'class': 'form-control select','placeholder': 'item'}), 15 | 'host': Select(attrs={'class': 'form-control select','placeholder': 'host'}), 16 | 'expression': Select(attrs={'class': 'form-control select','placeholder': 'expression'}), 17 | 'value': TextInput(attrs={'class': 'form-control', 'placeholder': 'value'}), 18 | 'time': TextInput(attrs={'class': 'form-control', 'placeholder': 'time'}), 19 | } 20 | labels = { 21 | 'item': '指标', 22 | 'host': '主机', 23 | 'expression': '语法', 24 | 'value': '阀值', 25 | 'time': '次数', 26 | } 27 | 28 | 29 | class TriggerConditionForm(forms.ModelForm): 30 | class Meta: 31 | model = TriggerCondition 32 | fields = ['trigger', 'max_time', 'sendee'] 33 | widgets = { 34 | 'trigger': Select(attrs={'class': 'form-control select','placeholder': 'trigger'}), 35 | 'max_time': TextInput(attrs={'class': 'form-control','placeholder': 'max_time'}), 36 | 'sendee': Select(attrs={'class': 'form-control select','placeholder': 'sendee'}), 37 | } 38 | labels = { 39 | 'trigger': '触发器', 40 | 'max_time': '最多次数', 41 | 'sendee': '告警接收人', 42 | } 43 | 44 | 45 | -------------------------------------------------------------------------------- /alarm/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/alarm/migrations/__init__.py -------------------------------------------------------------------------------- /alarm/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /alarm/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /alarm/urls/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | -------------------------------------------------------------------------------- /alarm/urls/api_urls.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | from django.conf.urls import url, include 4 | -------------------------------------------------------------------------------- /alarm/urls/view_urls.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | from django.conf.urls import url 4 | from alarm.views import trigger, trigger_condition, alarm_event 5 | 6 | 7 | urlpatterns = [ 8 | # trigger action url 9 | url(r'trigger/list/$', trigger.TriggerListView.as_view(), name='trigger_list'), 10 | url(r'trigger/add/$', trigger.TriggerAddView.as_view(), name='trigger_add'), 11 | url(r'trigger/edit/(?P\d+)/$', trigger.TriggerUpdateView.as_view(), name='trigger_edit'), 12 | url(r'trigger/del/(?P\d+)/$', trigger.TriggerDelView.as_view(), name='trigger_del'), 13 | 14 | # trigger_condition action url 15 | url(r'trigger/condition/list/$', trigger_condition.TriggerConditionListView.as_view(), name='trigger_condition_list'), 16 | url(r'trigger/condition/add/$', trigger_condition.TriggerCondtionAddView.as_view(), name='trigger_condition_add'), 17 | url(r'trigger/condition/edit/(?P\d+)/$', trigger_condition.TriggerConditionUpdateView.as_view(), name='trigger_condition_edit'), 18 | url(r'trigger/condition/del/(?P\d+)/$', trigger_condition.TriggerConditionDelView.as_view(), name='trigger_condition_del'), 19 | 20 | # alarm_event action url 21 | url(r'alarmevent/list/$', alarm_event.AlarmEventListView.as_view(), name='alarm_event_list'), 22 | url(r'alarmevent/edit/(?P\d+)/$', alarm_event.AlarmEventUpdateView.as_view(), name='alarm_event_edit'), 23 | url(r'alarmevent/del/(?P\d+)/$', alarm_event.AlarmEventDelView.as_view(), name='alarm_event_del'), 24 | ] -------------------------------------------------------------------------------- /alarm/views/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | -------------------------------------------------------------------------------- /alarm/views/alarm_event.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | from django.views.generic import ListView, CreateView, UpdateView, DeleteView 4 | from django.urls import reverse_lazy 5 | 6 | from common.models import AlarmEvent 7 | from common.forms import HostForm 8 | from common.mixins import BaseMixin 9 | 10 | 11 | class AlarmEventListView(BaseMixin, ListView): 12 | model = AlarmEvent 13 | template_name = 'alarm/alarm_event_list.html' 14 | context_object_name = 'alarm_event_list' 15 | paginate_by = 10 16 | 17 | def get_queryset(self): 18 | alarm_event_list = AlarmEvent.objects.order_by('-start_time') 19 | return alarm_event_list 20 | 21 | def get_context_data(self, **kwargs): 22 | kwargs['paginate_by'] = self.paginate_by 23 | return super(AlarmEventListView, self).get_context_data(**kwargs) 24 | 25 | 26 | class AlarmEventUpdateView(BaseMixin, UpdateView): 27 | model = AlarmEvent 28 | template_name = 'alarm/alarm_event_edit.html' 29 | form_class = HostForm 30 | pk_url_kwarg = 'alarm_event_id' 31 | success_url = reverse_lazy('alarm:alarm_event_list') 32 | 33 | 34 | class AlarmEventDelView(BaseMixin, DeleteView): 35 | model = AlarmEvent 36 | pk_url_kwarg = 'alarm_event_id' 37 | success_url = reverse_lazy('alarm:alarm_event_list') -------------------------------------------------------------------------------- /alarm/views/trigger.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | from django.views.generic import ListView, CreateView, UpdateView, DeleteView 4 | from django.urls import reverse_lazy 5 | 6 | from common.models import Trigger 7 | from alarm.forms import TriggerForm 8 | from common.mixins import BaseMixin 9 | 10 | 11 | class TriggerListView(BaseMixin, ListView): 12 | model = Trigger 13 | template_name = 'alarm/trigger_list.html' 14 | context_object_name = 'trigger_list' 15 | paginate_by = 10 16 | 17 | def get_queryset(self): 18 | trigger_list = Trigger.objects.order_by('-create_time') 19 | return trigger_list 20 | 21 | def get_context_data(self, **kwargs): 22 | kwargs['paginate_by'] = self.paginate_by 23 | return super(TriggerListView, self).get_context_data(**kwargs) 24 | 25 | 26 | class TriggerAddView(BaseMixin, CreateView): 27 | template_name = 'alarm/trigger_add.html' 28 | form_class = TriggerForm 29 | success_url = reverse_lazy('alarm:trigger_list') 30 | success_message = '触发器添加成功!' 31 | 32 | 33 | class TriggerUpdateView(BaseMixin, UpdateView): 34 | model = Trigger 35 | template_name = 'alarm/trigger_edit.html' 36 | form_class = TriggerForm 37 | pk_url_kwarg = 'trigger_id' 38 | success_url = reverse_lazy('alarm:trigger_list') 39 | success_message = '修改触发器信息成功!' 40 | 41 | 42 | class TriggerDelView(BaseMixin, DeleteView): 43 | model = Trigger 44 | pk_url_kwarg = 'trigger_id' 45 | success_url = reverse_lazy('alarm:trigger_list') 46 | -------------------------------------------------------------------------------- /alarm/views/trigger_condition.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | from django.views.generic import ListView, CreateView, UpdateView, DeleteView 4 | from django.urls import reverse_lazy 5 | 6 | from common.models import TriggerCondition 7 | from alarm.forms import TriggerConditionForm 8 | from common.mixins import BaseMixin 9 | 10 | 11 | class TriggerConditionListView(BaseMixin, ListView): 12 | model = TriggerCondition 13 | template_name = 'alarm/trigger_condition_list.html' 14 | context_object_name = 'trigger_condition_list' 15 | paginate_by = 10 16 | 17 | def get_queryset(self): 18 | trigger_condition_list = TriggerCondition.objects.order_by('-create_time') 19 | return trigger_condition_list 20 | 21 | def get_context_data(self, **kwargs): 22 | kwargs['paginate_by'] = self.paginate_by 23 | return super(TriggerConditionListView, self).get_context_data(**kwargs) 24 | 25 | 26 | class TriggerCondtionAddView(BaseMixin, CreateView): 27 | template_name = 'alarm/trigger_condition_add.html' 28 | form_class = TriggerConditionForm 29 | success_url = reverse_lazy('alarm:trigger_condition_list') 30 | success_message = '触发条件添加成功!' 31 | 32 | 33 | class TriggerConditionUpdateView(BaseMixin, UpdateView): 34 | model = TriggerCondition 35 | template_name = 'alarm/trigger_condition_edit.html' 36 | form_class = TriggerConditionForm 37 | pk_url_kwarg = 'trigger_condition_id' 38 | success_url = reverse_lazy('alarm:trigger_condition_list') 39 | success_message = '修改触发条件信息成功!' 40 | 41 | 42 | class TriggerConditionDelView(BaseMixin, DeleteView): 43 | model = TriggerCondition 44 | pk_url_kwarg = 'trigger_condition_id' 45 | success_url = reverse_lazy('alarm:trigger_condition_list') 46 | -------------------------------------------------------------------------------- /common/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/common/__init__.py -------------------------------------------------------------------------------- /common/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /common/api/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | -------------------------------------------------------------------------------- /common/api/asset.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | from rest_framework import viewsets 4 | from rest_framework import permissions 5 | from rest_framework.authentication import SessionAuthentication, BasicAuthentication, TokenAuthentication 6 | # from rest_framework.response import Response 7 | # from rest_framework.decorators import api_view 8 | 9 | from ..models import Asset, AssetGroup, IDC 10 | from ..serializers.host import AssetSerializer, AssetGroupSerializer, IDCSerializer 11 | from ..pagination import AssetsPagination 12 | from assets.tasks import create_or_update_asset_info 13 | 14 | 15 | class AssetViewSet(viewsets.ModelViewSet): 16 | queryset = Asset.objects.all() 17 | serializer_class = AssetSerializer 18 | permission_classes = (permissions.IsAuthenticatedOrReadOnly,) 19 | pagination_class = AssetsPagination 20 | 21 | def perform_create(self, serializer): 22 | create_or_update_asset_info(serializer) 23 | 24 | 25 | 26 | class AssetGroupViewSet(viewsets.ModelViewSet): 27 | queryset = AssetGroup.objects.all() 28 | serializer_class = AssetGroupSerializer 29 | permission_classes = (permissions.IsAuthenticatedOrReadOnly,) 30 | pagination_class = AssetsPagination 31 | 32 | 33 | class IDCViewSet(viewsets.ModelViewSet): 34 | queryset = IDC.objects.all() 35 | serializer_class = IDCSerializer 36 | permission_classes = (permissions.IsAuthenticatedOrReadOnly,) 37 | pagination_class = AssetsPagination 38 | 39 | 40 | -------------------------------------------------------------------------------- /common/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class CommonConfig(AppConfig): 5 | name = 'common' 6 | -------------------------------------------------------------------------------- /common/forms.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | from django import forms 4 | from django.forms.widgets import * 5 | 6 | from common.models import Host, HostGroup, Items, Template 7 | 8 | 9 | class HostAddForm(forms.ModelForm): 10 | class Meta: 11 | model = Host 12 | fields = ['hostname', 'hostgroups'] 13 | widgets = { 14 | 'hostname': TextInput(attrs={'class': 'form-control','placeholder': 'hostname'}), 15 | 'hostgroups': Select(attrs={'class': 'form-control','placeholder': 'hostgroup'}), 16 | } 17 | labels = { 18 | 'hostname': '主机名', 19 | 'hostgroups': '所属主机组', 20 | } 21 | 22 | 23 | class HostForm(forms.ModelForm): 24 | class Meta: 25 | model = Host 26 | fields = ['hostname','ipaddress','macaddress','os_type','os_version'] 27 | widgets = { 28 | 'hostname': TextInput(attrs={'class': 'form-control','placeholder': 'hostname'}), 29 | 'ipaddress': TextInput(attrs={'class': 'form-control', 'data-inputmask': "'alias': 'ip'", 30 | 'data-mask': '', 'placeholder': 'ipaddress'}), 31 | 'macaddress': TextInput(attrs={'class': 'form-control', 'placeholder': 'macaddress'}), 32 | 'os_type': TextInput(attrs={'class': 'form-control', 'placeholder': 'os_type'}), 33 | 'os_version': TextInput(attrs={'class': 'form-control', 'placeholder': 'os_version'}), 34 | } 35 | labels = { 36 | 'hostname': '主机名', 37 | 'ipaddress': 'IP地址', 38 | 'macaddress': 'MAC地址', 39 | 'os_type': '操作系统类型', 40 | 'os_version': '操作系统版本', 41 | } 42 | 43 | 44 | class HostGroupForm(forms.ModelForm): 45 | class Meta: 46 | model = HostGroup 47 | fields = ['name', 'template', 'description'] 48 | widgets = { 49 | 'name': TextInput(attrs={'class': 'form-control','placeholder': 'hostgroup name'}), 50 | 'template': Select(attrs={'class': 'form-control','placeholder': 'template'}), 51 | 'description': Textarea(attrs={'class': 'form-control', 'placeholder': 'description'}), 52 | } 53 | labels = { 54 | 'name': '主机组名', 55 | 'template': '模版', 56 | 'description': '描述', 57 | } 58 | 59 | 60 | class ItemForm(forms.ModelForm): 61 | class Meta: 62 | model = Items 63 | fields = ['name'] 64 | widgets = {'name': TextInput(attrs={'class': 'form-control','placeholder': 'item name'})} 65 | 66 | 67 | class TemplateForm(forms.ModelForm): 68 | class Meta: 69 | model = Template 70 | fields = ['name', 'item'] 71 | widgets = { 72 | 'name': TextInput(attrs={'class': 'form-control','placeholder': 'name'}), 73 | 'item': Select(attrs={'class': 'form-control select2', 'id': 'items', 'multiple': 'multiple', 'placeholder': 'hostgroup name'}), 74 | } 75 | 76 | -------------------------------------------------------------------------------- /common/graph.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | import rrdtool 4 | import time 5 | 6 | def main(): 7 | title = "load 1min data (" + time.strftime('%Y-%m-%d', time.localtime(time.time())) + ")" 8 | name = "/Users/liuhui/PycharmProjects/lh-falcon/rrddatas/ansible.node2.com/net_if_in_sizes.rrd" 9 | rrdtool.graph('Flow.png', '--start', '-1h', '--vertical-label=Bytes/s', 10 | '--x-grid', 'MINUTE:12:HOUR:1:HOUR:1:0:%H', 11 | '--width', '750', '--height', '300', '--title', title, 12 | 'DEF:metric=%s:metric:AVERAGE' % (name), 13 | # 'DEF:outoctets=%s:eth0_out:AVERAGE' % (name), 14 | 'AREA:metric#00FF00:load 1min', 15 | # 'LINE1:outoctets#0000FF:Out traffic', 16 | 'HRULE:190#FF0000:Load value\\r', 17 | 'CDEF:data=metric,8,*', 18 | # 'CDEF:outbits=outoctets,8,*', 19 | 'COMMENT:\\r', 20 | ) 21 | 22 | -------------------------------------------------------------------------------- /common/migrations/0002_auto_20180601_0801.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.4 on 2018-06-01 08:01 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | initial = True 13 | 14 | dependencies = [ 15 | ('common', '0001_initial'), 16 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 17 | ] 18 | 19 | operations = [ 20 | migrations.AddField( 21 | model_name='triggercondition', 22 | name='sendee', 23 | field=models.ManyToManyField(to=settings.AUTH_USER_MODEL, verbose_name='告警接收人'), 24 | ), 25 | migrations.AddField( 26 | model_name='triggercondition', 27 | name='trigger', 28 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='common.Trigger', verbose_name='Trigger'), 29 | ), 30 | migrations.AddField( 31 | model_name='trigger', 32 | name='host', 33 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='common.Host', verbose_name='主机'), 34 | ), 35 | migrations.AddField( 36 | model_name='trigger', 37 | name='item', 38 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='common.Items', verbose_name='指标'), 39 | ), 40 | migrations.AddField( 41 | model_name='template', 42 | name='creator', 43 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='创建者'), 44 | ), 45 | migrations.AddField( 46 | model_name='template', 47 | name='item', 48 | field=models.ManyToManyField(to='common.Items', verbose_name='指标'), 49 | ), 50 | migrations.AddField( 51 | model_name='hostgroup', 52 | name='template', 53 | field=models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, to='common.Template', verbose_name='绑定模版'), 54 | ), 55 | migrations.AddField( 56 | model_name='host', 57 | name='hostgroups', 58 | field=models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, to='common.HostGroup', verbose_name='主机组'), 59 | ), 60 | migrations.AddField( 61 | model_name='alarmevent', 62 | name='host', 63 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='common.Host', verbose_name='告警主机'), 64 | ), 65 | migrations.AddField( 66 | model_name='alarmevent', 67 | name='item', 68 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='common.Items', verbose_name='告警指标'), 69 | ), 70 | ] 71 | -------------------------------------------------------------------------------- /common/migrations/0003_auto_20180601_1149.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.4 on 2018-06-01 11:49 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('common', '0002_auto_20180601_0801'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterModelOptions( 16 | name='items', 17 | options={'verbose_name': '指标', 'verbose_name_plural': '指标'}, 18 | ), 19 | migrations.AlterModelOptions( 20 | name='template', 21 | options={'verbose_name': '模版', 'verbose_name_plural': '模版'}, 22 | ), 23 | migrations.AlterField( 24 | model_name='host', 25 | name='ipaddress', 26 | field=models.GenericIPAddressField(blank=True, null=True, unique=True, verbose_name='IP地址'), 27 | ), 28 | migrations.AlterField( 29 | model_name='host', 30 | name='macaddress', 31 | field=models.CharField(blank=True, max_length=32, null=True, unique=True, verbose_name='MAC地址'), 32 | ), 33 | migrations.AlterField( 34 | model_name='host', 35 | name='os_type', 36 | field=models.CharField(blank=True, max_length=32, null=True, verbose_name='操作系统类型'), 37 | ), 38 | migrations.AlterField( 39 | model_name='host', 40 | name='os_version', 41 | field=models.CharField(blank=True, max_length=64, null=True, verbose_name='操作系统版本'), 42 | ), 43 | ] 44 | -------------------------------------------------------------------------------- /common/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/common/migrations/__init__.py -------------------------------------------------------------------------------- /common/mixins.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | from django.contrib.auth.mixins import LoginRequiredMixin 4 | from django.contrib.auth.mixins import AccessMixin 5 | from django.contrib.auth.mixins import PermissionRequiredMixin 6 | from django.contrib.auth.models import Permission, User 7 | from django.shortcuts import redirect,HttpResponseRedirect 8 | from django.urls import reverse 9 | 10 | from common.models import Host, HostGroup, Trigger, TriggerCondition, AlarmEvent, Items, Template 11 | from users.models import User,UserGroup 12 | 13 | 14 | class BaseMixin(LoginRequiredMixin): 15 | login_url = 'users:login' 16 | def get_context_data(self, *args, **kwargs): 17 | context = super(BaseMixin, self).get_context_data(*args, **kwargs) 18 | context['host_count'] = Host.objects.all().count() 19 | context['hostgroup_count'] = HostGroup.objects.all().count() 20 | context['item_count'] = Items.objects.all().count() 21 | context['template_count'] = Template.objects.all().count() 22 | 23 | context['user_count'] = User.objects.all().count() 24 | context['usergroup_count'] = UserGroup.objects.all().count() 25 | 26 | context['trigger_count'] = Trigger.objects.all().count() 27 | context['trigger_condition_count'] = TriggerCondition.objects.all().count() 28 | context['alarm_event_count'] = AlarmEvent.objects.all().count() 29 | 30 | return context 31 | 32 | 33 | class ActionPermissionRequiredMixin(PermissionRequiredMixin): 34 | """ 35 | CBV mixin which verifies that the current user has all specified 36 | permissions. 37 | """ 38 | def dispatch(self, request, *args, **kwargs): 39 | try: 40 | pk = self.kwargs.get(self.pk_url_kwarg) 41 | if not request.user.is_staff and not request.user.is_superuser \ 42 | and str(request.user.id) != pk: 43 | return HttpResponseRedirect(reverse('permission:no_action_permission')) 44 | except: 45 | if not request.user.is_staff and not request.user.is_superuser: 46 | return HttpResponseRedirect(reverse('permission:no_action_permission')) 47 | return super(PermissionRequiredMixin, self).dispatch(request, *args, **kwargs) 48 | -------------------------------------------------------------------------------- /common/serializers.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | from rest_framework import serializers 4 | 5 | from common.models import Host, HostGroup, Items, Trigger, Template, Event 6 | 7 | 8 | class HostSerializer(serializers.HyperlinkedModelSerializer): 9 | class Meta: 10 | model = Host 11 | fields = ('id', 'hostname', 'ipaddress', 'macaddress', 'os_type', 'os_version') 12 | 13 | 14 | class HostGroupSerializer(serializers.HyperlinkedModelSerializer): 15 | class Meta: 16 | model = HostGroup 17 | fields = ('name', 'description') 18 | 19 | 20 | class ItemsSerializers(serializers.HyperlinkedModelSerializer): 21 | class Meta: 22 | model = Items 23 | fields = ('name') 24 | 25 | 26 | class TriggerSerializers(serializers.HyperlinkedModelSerializer): 27 | class Meta: 28 | model = Trigger 29 | fields = ('item', 'expression', 'value', 'time') 30 | 31 | 32 | class TemplateSerializers(serializers.HyperlinkedModelSerializer): 33 | class Meta: 34 | model = Template 35 | fields = ('name', 'creator', 'trigger') 36 | 37 | 38 | class EventSerializers(serializers.HyperlinkedModelSerializer): 39 | class Meta: 40 | model = Event 41 | fields = ('trigger', 'host', 'item') 42 | 43 | 44 | -------------------------------------------------------------------------------- /common/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | -------------------------------------------------------------------------------- /common/templatetags/mytags.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | from django import template 4 | 5 | register = template.Library() 6 | 7 | 8 | @register.assignment_tag 9 | def pagination(page_obj,paginator,num_of_displaypages=10,num_of_backpages=4): 10 | 11 | num_of_frontpages = num_of_backpages 12 | html = '' 13 | 14 | if paginator.num_pages <= num_of_displaypages + num_of_backpages: 15 | for page_num in range(1, paginator.num_pages + 1): 16 | html += '
  • %s
  • ' % (page_num, page_num) 17 | return html 18 | elif page_obj.number <= num_of_displaypages - num_of_backpages + 2: 19 | for page_num in range(1, num_of_displaypages + 1): 20 | html += '
  • %s
  • ' % (page_num, page_num) 21 | html += ''' 22 |
  • ...
  • 23 |
  • %s
  • 24 | ''' % (paginator.num_pages, paginator.num_pages, paginator.num_pages) 25 | return html 26 | elif num_of_displaypages - num_of_backpages + 2 < page_obj.number <= paginator.num_pages - num_of_backpages - 2: 27 | html = ''' 28 |
  • 1
  • 29 |
  • ...
  • 30 | ''' 31 | for i in range(page_obj.number - num_of_frontpages, page_obj.number + num_of_backpages + 1): 32 | html += '
  • %s
  • ' % (i, i) 33 | html += ''' 34 |
  • ...
  • 35 |
  • %s
  • 36 | ''' % (paginator.num_pages, paginator.num_pages, paginator.num_pages) 37 | return html 38 | else: 39 | html = ''' 40 |
  • 1
  • 41 |
  • ...
  • 42 | ''' 43 | for i in range(page_obj.number - num_of_frontpages, paginator.num_pages + 1): 44 | html += '
  • %s
  • ' % (i, i) 45 | return html -------------------------------------------------------------------------------- /common/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /common/urls/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | -------------------------------------------------------------------------------- /common/urls/api_urls.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | from django.conf.urls import url, include 4 | -------------------------------------------------------------------------------- /common/urls/view_urls.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | from django.conf.urls import url 4 | from common.views import host, hostgroup, items, template 5 | 6 | 7 | urlpatterns = [ 8 | # host action url 9 | url(r'host/list/$', host.HostListView.as_view(), name='host_list'), 10 | url(r'host/add/$', host.HostAddView.as_view(), name='host_add'), 11 | url(r'host/edit/(?P\d+)/$', host.HostUpdateView.as_view(), name='host_edit'), 12 | url(r'host/del/(?P\d+)/$', host.HostDelView.as_view(), name='host_del'), 13 | url(r'host/search/$', host.SearchHostView.as_view(), name='host_search'), 14 | url(r'host/item_list/(?P\d+)/$', host.HostItemView.as_view(), name='host_item'), 15 | url(r'host/item_graph/(?P\d+)/$', host.HostItemView.as_view(), name='host_item_graph'), 16 | 17 | # hostgroup action url 18 | url(r'hostgroup/list/$', hostgroup.HostGroupListView.as_view(), name='hostgroup_list'), 19 | url(r'hostgroup/add/$', hostgroup.HostGroupAddView.as_view(), name='hostgroup_add'), 20 | url(r'hostgroup/edit/(?P\d+)/$', hostgroup.HostGroupUpdateView.as_view(), name='hostgroup_edit'), 21 | url(r'hostgroup/del/(?P\d+)/$', hostgroup.HostGroupDelView.as_view(), name='hostgroup_del'), 22 | 23 | # item action url 24 | url(r'item/list/$', items.ItemsListView.as_view(), name='item_list'), 25 | url(r'item/add/$', items.ItemsAddView.as_view(), name='item_add'), 26 | url(r'item/del/(?P\d+)/$', items.ItemsDelView.as_view(), name='item_del'), 27 | 28 | # template action url 29 | url(r'template/list/$', template.TemplateListView.as_view(), name='template_list'), 30 | url(r'template/add/$', template.TemplateAddView.as_view(), name='template_add'), 31 | url(r'template/edit/(?P\d+)/$', template.TemplateUpdateView.as_view(), name='template_edit'), 32 | url(r'template/del/(?P\d+)/$', template.TemplateDelView.as_view(), name='template_del'), 33 | ] -------------------------------------------------------------------------------- /common/views/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'liuhui' 2 | -------------------------------------------------------------------------------- /common/views/hostgroup.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*_ 2 | 3 | from django.core.urlresolvers import reverse_lazy 4 | from django.views.generic import ListView, CreateView, UpdateView, DetailView, DeleteView 5 | 6 | from common.mixins import BaseMixin 7 | from common.models import HostGroup 8 | from common.forms import HostGroupForm 9 | 10 | 11 | class HostGroupListView(BaseMixin, ListView): 12 | model = HostGroup 13 | template_name = 'host/hostgroup_list.html' 14 | context_object_name = 'hostgroup_list' 15 | paginate_by = 10 16 | 17 | def get_queryset(self): 18 | hostgroup_list = HostGroup.objects.filter().order_by('-create_time') 19 | return hostgroup_list 20 | 21 | def get_context_data(self, **kwargs): 22 | kwargs['paginate_by'] = self.paginate_by 23 | return super(HostGroupListView, self).get_context_data(**kwargs) 24 | 25 | 26 | class HostGroupAddView(BaseMixin, CreateView): 27 | template_name = 'host/hostgroup_add.html' 28 | form_class = HostGroupForm 29 | success_url = reverse_lazy('common:hostgroup_list') 30 | success_messages = '添加主机组成功!' 31 | 32 | 33 | class HostGroupUpdateView(BaseMixin, UpdateView): 34 | model = HostGroup 35 | template_name = 'host/hostgroup_edit.html' 36 | form_class = HostGroupForm 37 | pk_url_kwarg = 'hostgroup_id' 38 | success_url = reverse_lazy('common:hostgroup_list') 39 | success_message = '修改主机组信息成功!' 40 | 41 | 42 | class HostGroupDelView(BaseMixin, DeleteView): 43 | model = HostGroup 44 | pk_url_kwarg = 'hostgroup_id' 45 | success_url = reverse_lazy('common:hostgroup_list') 46 | 47 | -------------------------------------------------------------------------------- /common/views/items.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | from django.views.generic import ListView, CreateView, DeleteView 4 | from django.urls import reverse_lazy 5 | 6 | from common.models import Items 7 | from common.forms import ItemForm 8 | from common.mixins import BaseMixin 9 | 10 | 11 | class ItemsListView(BaseMixin, ListView): 12 | model = Items 13 | template_name = 'host/item_list.html' 14 | context_object_name = 'item_list' 15 | paginate_by = 10 16 | 17 | def get_queryset(self): 18 | item_list = Items.objects.order_by('-create_time') 19 | return item_list 20 | 21 | def get_context_data(self, **kwargs): 22 | kwargs['paginate_by'] = self.paginate_by 23 | return super(ItemsListView, self).get_context_data(**kwargs) 24 | 25 | 26 | class ItemsAddView(BaseMixin, CreateView): 27 | template_name = 'host/item_add.html' 28 | form_class = ItemForm 29 | success_url = reverse_lazy('common:item_list') 30 | success_message = '指标添加成功!' 31 | 32 | 33 | class ItemsDelView(BaseMixin, DeleteView): 34 | model = Items 35 | pk_url_kwarg = 'item_id' 36 | success_url = reverse_lazy('common:item_list') 37 | -------------------------------------------------------------------------------- /common/views/template.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | from django.views.generic import ListView, CreateView, UpdateView, DeleteView, FormView 4 | from django.urls import reverse_lazy, reverse 5 | from django.shortcuts import HttpResponseRedirect, redirect 6 | 7 | from common.models import Template, Items 8 | from common.forms import TemplateForm 9 | from common.mixins import BaseMixin 10 | from users.models import User 11 | 12 | 13 | class TemplateListView(BaseMixin, ListView): 14 | model = Template 15 | template_name = 'host/template_list.html' 16 | context_object_name = 'template_list' 17 | paginate_by = 10 18 | 19 | def get_queryset(self): 20 | template_list = Template.objects.order_by('-create_time') 21 | return template_list 22 | 23 | def get_context_data(self, **kwargs): 24 | kwargs['paginate_by'] = self.paginate_by 25 | return super(TemplateListView, self).get_context_data(**kwargs) 26 | 27 | 28 | class TemplateAddView(BaseMixin, FormView): 29 | form_class = TemplateForm 30 | template_name = 'host/template_add.html' 31 | redirect_field_name = 'next' 32 | 33 | def get_context_data(self, **kwargs): 34 | return super(TemplateAddView, self).get_context_data(**kwargs) 35 | 36 | def post(self, request, *args, **kwargs): 37 | print(self.request.POST) 38 | name = self.request.POST.get('name') 39 | username = self.request.user 40 | items = self.request.POST.getlist('item') 41 | user = User.objects.get(username=username) 42 | 43 | template = Template.objects.create(name=name, creator=user) 44 | for item in items: 45 | template.item.add(Items.objects.get(id=int(item))) 46 | return redirect('common:template_list') 47 | 48 | 49 | class TemplateUpdateView(BaseMixin, UpdateView): 50 | model = Template 51 | template_name = 'host/template_edit.html' 52 | form_class = TemplateForm 53 | pk_url_kwarg = 'template_id' 54 | success_url = reverse_lazy('common:template_list') 55 | success_message = '修改模版信息成功!' 56 | 57 | def post(self, request, *args, **kwargs): 58 | if self.request.method == 'POST': 59 | items = self.request.POST.getlist('item') 60 | id = self.kwargs.get(self.pk_url_kwarg) 61 | template = Template.objects.get(id=id) 62 | for item in items: 63 | template.item.add(Items.objects.get(id=int(item))) 64 | return redirect('common:template_list') 65 | 66 | 67 | class TemplateDelView(BaseMixin, DeleteView): 68 | model = Template 69 | pk_url_kwarg = 'template_id' 70 | success_url = reverse_lazy('common:template_list') 71 | -------------------------------------------------------------------------------- /dashboard/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/dashboard/__init__.py -------------------------------------------------------------------------------- /dashboard/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /dashboard/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class DashboardConfig(AppConfig): 5 | name = 'dashboard' 6 | -------------------------------------------------------------------------------- /dashboard/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/dashboard/migrations/__init__.py -------------------------------------------------------------------------------- /dashboard/models.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/dashboard/models.py -------------------------------------------------------------------------------- /dashboard/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /dashboard/urls.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | from django.conf.urls import url 4 | 5 | from dashboard import views 6 | 7 | 8 | urlpatterns = [ 9 | url(r'^$', views.IndexView.as_view(), name='index'), 10 | url(r'^item/search/$', views.ItemSearchPictureView.as_view()) 11 | ] 12 | -------------------------------------------------------------------------------- /dashboard/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | 5 | from django.views.generic import TemplateView 6 | 7 | from users.models import User 8 | from common.models import Host 9 | from common.mixins import BaseMixin 10 | 11 | 12 | class IndexView(BaseMixin, TemplateView): 13 | template_name = 'index.html' 14 | 15 | def get_context_data(self, *args, **kwargs): 16 | kwargs['host_normal_count'] = Host.objects.filter(is_normal=True).count() 17 | kwargs['host_measure_count'] = Host.objects.filter(is_normal=False).count() 18 | kwargs['user_is_staff_count'] = User.objects.filter(is_active=True).count() 19 | kwargs['user_is_not_staff_count'] = User.objects.filter(is_staff=False).count() 20 | return super(IndexView, self).get_context_data(**kwargs) 21 | 22 | 23 | class ItemSearchPictureView(BaseMixin, TemplateView): 24 | template_name = 'item.html' 25 | 26 | def get_context_data(self, *args, **kwargs): 27 | kwargs['name'] = 'ansible.node2.com 流量指标' 28 | kwargs['item_picture_url'] = 'Flow.png' 29 | return super(ItemSearchPictureView, self).get_context_data(**kwargs) 30 | 31 | 32 | -------------------------------------------------------------------------------- /lh-falcon/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/lh-falcon/__init__.py -------------------------------------------------------------------------------- /lh-falcon/urls.py: -------------------------------------------------------------------------------- 1 | """lh-falcon URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/1.11/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.conf.urls import url, include 14 | 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) 15 | """ 16 | from django.conf.urls import url, include 17 | from django.contrib import admin 18 | from django.conf.urls.static import static 19 | from django.conf import settings 20 | 21 | urlpatterns = [ 22 | url(r'^admin/', admin.site.urls), 23 | url(r'user/', include('users.urls.view_urls', namespace='users')), 24 | url(r'common/', include('common.urls.view_urls', namespace='common')), 25 | url(r'alarm/', include('alarm.urls.view_urls', namespace='alarm')), 26 | url(r'', include('dashboard.urls')), 27 | url(r'', include('transfer.urls')), 28 | ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 29 | -------------------------------------------------------------------------------- /lh-falcon/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for lh-falcon project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lh-falcon.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lh-falcon.settings") 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError: 10 | # The above import may fail for some other reason. Ensure that the 11 | # issue is really that Django is missing to avoid masking other 12 | # exceptions on Python 2. 13 | try: 14 | import django 15 | except ImportError: 16 | raise ImportError( 17 | "Couldn't import Django. Are you sure it's installed and " 18 | "available on your PYTHONPATH environment variable? Did you " 19 | "forget to activate a virtual environment?" 20 | ) 21 | raise 22 | execute_from_command_line(sys.argv) 23 | -------------------------------------------------------------------------------- /media/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/media/default.png -------------------------------------------------------------------------------- /static/css/host.css: -------------------------------------------------------------------------------- 1 | .hos { 2 | margin-top: 7px; 3 | } -------------------------------------------------------------------------------- /static/css/morris.css: -------------------------------------------------------------------------------- 1 | .morris-hover{position:absolute;z-index:1000}.morris-hover.morris-default-style{border-radius:10px;padding:6px;color:#666;background:rgba(255,255,255,0.8);border:solid 2px rgba(230,230,230,0.8);font-family:sans-serif;font-size:12px;text-align:center}.morris-hover.morris-default-style .morris-hover-row-label{font-weight:bold;margin:0.25em 0} 2 | .morris-hover.morris-default-style .morris-hover-point{white-space:nowrap;margin:0.1em 0} 3 | -------------------------------------------------------------------------------- /static/css/table.css: -------------------------------------------------------------------------------- 1 | table { 2 | border-collapse: collapse; 3 | border-spacing: 0; 4 | } 5 | -------------------------------------------------------------------------------- /static/dist/css/alt/AdminLTE-fullcalendar.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Plugin: Full Calendar 3 | * --------------------- 4 | */ 5 | .fc-button { 6 | background: #f4f4f4; 7 | background-image: none; 8 | color: #444; 9 | border-color: #ddd; 10 | border-bottom-color: #ddd; 11 | } 12 | .fc-button:hover, 13 | .fc-button:active, 14 | .fc-button.hover { 15 | background-color: #e9e9e9; 16 | } 17 | .fc-header-title h2 { 18 | font-size: 15px; 19 | line-height: 1.6em; 20 | color: #666; 21 | margin-left: 10px; 22 | } 23 | .fc-header-right { 24 | padding-right: 10px; 25 | } 26 | .fc-header-left { 27 | padding-left: 10px; 28 | } 29 | .fc-widget-header { 30 | background: #fafafa; 31 | } 32 | .fc-grid { 33 | width: 100%; 34 | border: 0; 35 | } 36 | .fc-widget-header:first-of-type, 37 | .fc-widget-content:first-of-type { 38 | border-left: 0; 39 | border-right: 0; 40 | } 41 | .fc-widget-header:last-of-type, 42 | .fc-widget-content:last-of-type { 43 | border-right: 0; 44 | } 45 | .fc-toolbar { 46 | padding: 10px; 47 | margin: 0; 48 | } 49 | .fc-day-number { 50 | font-size: 20px; 51 | font-weight: 300; 52 | padding-right: 10px; 53 | } 54 | .fc-color-picker { 55 | list-style: none; 56 | margin: 0; 57 | padding: 0; 58 | } 59 | .fc-color-picker > li { 60 | float: left; 61 | font-size: 30px; 62 | margin-right: 5px; 63 | line-height: 30px; 64 | } 65 | .fc-color-picker > li .fa { 66 | -webkit-transition: -webkit-transform linear 0.3s; 67 | -moz-transition: -moz-transform linear 0.3s; 68 | -o-transition: -o-transform linear 0.3s; 69 | transition: transform linear 0.3s; 70 | } 71 | .fc-color-picker > li .fa:hover { 72 | -webkit-transform: rotate(30deg); 73 | -ms-transform: rotate(30deg); 74 | -o-transform: rotate(30deg); 75 | transform: rotate(30deg); 76 | } 77 | #add-new-event { 78 | -webkit-transition: all linear 0.3s; 79 | -o-transition: all linear 0.3s; 80 | transition: all linear 0.3s; 81 | } 82 | .external-event { 83 | padding: 5px 10px; 84 | font-weight: bold; 85 | margin-bottom: 4px; 86 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); 87 | text-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); 88 | border-radius: 3px; 89 | cursor: move; 90 | } 91 | .external-event:hover { 92 | box-shadow: inset 0 0 90px rgba(0, 0, 0, 0.2); 93 | } 94 | -------------------------------------------------------------------------------- /static/dist/css/alt/AdminLTE-fullcalendar.min.css: -------------------------------------------------------------------------------- 1 | .fc-button{background:#f4f4f4;background-image:none;color:#444;border-color:#ddd;border-bottom-color:#ddd}.fc-button:hover,.fc-button:active,.fc-button.hover{background-color:#e9e9e9}.fc-header-title h2{font-size:15px;line-height:1.6em;color:#666;margin-left:10px}.fc-header-right{padding-right:10px}.fc-header-left{padding-left:10px}.fc-widget-header{background:#fafafa}.fc-grid{width:100%;border:0}.fc-widget-header:first-of-type,.fc-widget-content:first-of-type{border-left:0;border-right:0}.fc-widget-header:last-of-type,.fc-widget-content:last-of-type{border-right:0}.fc-toolbar{padding:10px;margin:0}.fc-day-number{font-size:20px;font-weight:300;padding-right:10px}.fc-color-picker{list-style:none;margin:0;padding:0}.fc-color-picker>li{float:left;font-size:30px;margin-right:5px;line-height:30px}.fc-color-picker>li .fa{-webkit-transition:-webkit-transform linear .3s;-moz-transition:-moz-transform linear .3s;-o-transition:-o-transform linear .3s;transition:transform linear .3s}.fc-color-picker>li .fa:hover{-webkit-transform:rotate(30deg);-ms-transform:rotate(30deg);-o-transform:rotate(30deg);transform:rotate(30deg)}#add-new-event{-webkit-transition:all linear .3s;-o-transition:all linear .3s;transition:all linear .3s}.external-event{padding:5px 10px;font-weight:bold;margin-bottom:4px;box-shadow:0 1px 1px rgba(0,0,0,0.1);text-shadow:0 1px 1px rgba(0,0,0,0.1);border-radius:3px;cursor:move}.external-event:hover{box-shadow:inset 0 0 90px rgba(0,0,0,0.2)} -------------------------------------------------------------------------------- /static/dist/css/alt/AdminLTE-select2.min.css: -------------------------------------------------------------------------------- 1 | .select2-container--default.select2-container--focus,.select2-selection.select2-container--focus,.select2-container--default:focus,.select2-selection:focus,.select2-container--default:active,.select2-selection:active{outline:none}.select2-container--default .select2-selection--single,.select2-selection .select2-selection--single{border:1px solid #d2d6de;border-radius:0;padding:6px 12px;height:34px}.select2-container--default.select2-container--open{border-color:#3c8dbc}.select2-dropdown{border:1px solid #d2d6de;border-radius:0}.select2-container--default .select2-results__option--highlighted[aria-selected]{background-color:#3c8dbc;color:white}.select2-results__option{padding:6px 12px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--single .select2-selection__rendered{padding-left:0;padding-right:0;height:auto;margin-top:-4px}.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered{padding-right:6px;padding-left:20px}.select2-container--default .select2-selection--single .select2-selection__arrow{height:28px;right:3px}.select2-container--default .select2-selection--single .select2-selection__arrow b{margin-top:0}.select2-dropdown .select2-search__field,.select2-search--inline .select2-search__field{border:1px solid #d2d6de}.select2-dropdown .select2-search__field:focus,.select2-search--inline .select2-search__field:focus{outline:none}.select2-container--default.select2-container--focus .select2-selection--multiple,.select2-container--default .select2-search--dropdown .select2-search__field{border-color:#3c8dbc !important}.select2-container--default .select2-results__option[aria-disabled=true]{color:#999}.select2-container--default .select2-results__option[aria-selected=true]{background-color:#ddd}.select2-container--default .select2-results__option[aria-selected=true],.select2-container--default .select2-results__option[aria-selected=true]:hover{color:#444}.select2-container--default .select2-selection--multiple{border:1px solid #d2d6de;border-radius:0}.select2-container--default .select2-selection--multiple:focus{border-color:#3c8dbc}.select2-container--default.select2-container--focus .select2-selection--multiple{border-color:#d2d6de}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#3c8dbc;border-color:#367fa9;padding:1px 10px;color:#fff}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove{margin-right:5px;color:rgba(255,255,255,0.7)}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover{color:#fff}.select2-container .select2-selection--single .select2-selection__rendered{padding-right:10px} -------------------------------------------------------------------------------- /static/dist/css/skins/skin-green.min.css: -------------------------------------------------------------------------------- 1 | .skin-green .main-header .navbar{background-color:#00a65a}.skin-green .main-header .navbar .nav>li>a{color:#fff}.skin-green .main-header .navbar .nav>li>a:hover,.skin-green .main-header .navbar .nav>li>a:active,.skin-green .main-header .navbar .nav>li>a:focus,.skin-green .main-header .navbar .nav .open>a,.skin-green .main-header .navbar .nav .open>a:hover,.skin-green .main-header .navbar .nav .open>a:focus,.skin-green .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-green .main-header .navbar .sidebar-toggle{color:#fff}.skin-green .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-green .main-header .navbar .sidebar-toggle{color:#fff}.skin-green .main-header .navbar .sidebar-toggle:hover{background-color:#008d4c}@media (max-width:767px){.skin-green .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-green .main-header .navbar .dropdown-menu li a{color:#fff}.skin-green .main-header .navbar .dropdown-menu li a:hover{background:#008d4c}}.skin-green .main-header .logo{background-color:#008d4c;color:#fff;border-bottom:0 solid transparent}.skin-green .main-header .logo:hover{background-color:#008749}.skin-green .main-header li.user-header{background-color:#00a65a}.skin-green .content-header{background:transparent}.skin-green .wrapper,.skin-green .main-sidebar,.skin-green .left-side{background-color:#222d32}.skin-green .user-panel>.info,.skin-green .user-panel>.info>a{color:#fff}.skin-green .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-green .sidebar-menu>li>a{border-left:3px solid transparent}.skin-green .sidebar-menu>li:hover>a,.skin-green .sidebar-menu>li.active>a,.skin-green .sidebar-menu>li.menu-open>a{color:#fff;background:#1e282c}.skin-green .sidebar-menu>li.active>a{border-left-color:#00a65a}.skin-green .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-green .sidebar a{color:#b8c7ce}.skin-green .sidebar a:hover{text-decoration:none}.skin-green .sidebar-menu .treeview-menu>li>a{color:#8aa4af}.skin-green .sidebar-menu .treeview-menu>li.active>a,.skin-green .sidebar-menu .treeview-menu>li>a:hover{color:#fff}.skin-green .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px 10px}.skin-green .sidebar-form input[type="text"],.skin-green .sidebar-form .btn{box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px}.skin-green .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-green .sidebar-form input[type="text"]:focus,.skin-green .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-green .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-green .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0} -------------------------------------------------------------------------------- /static/dist/css/skins/skin-red.min.css: -------------------------------------------------------------------------------- 1 | .skin-red .main-header .navbar{background-color:#dd4b39}.skin-red .main-header .navbar .nav>li>a{color:#fff}.skin-red .main-header .navbar .nav>li>a:hover,.skin-red .main-header .navbar .nav>li>a:active,.skin-red .main-header .navbar .nav>li>a:focus,.skin-red .main-header .navbar .nav .open>a,.skin-red .main-header .navbar .nav .open>a:hover,.skin-red .main-header .navbar .nav .open>a:focus,.skin-red .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-red .main-header .navbar .sidebar-toggle{color:#fff}.skin-red .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-red .main-header .navbar .sidebar-toggle{color:#fff}.skin-red .main-header .navbar .sidebar-toggle:hover{background-color:#d73925}@media (max-width:767px){.skin-red .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-red .main-header .navbar .dropdown-menu li a{color:#fff}.skin-red .main-header .navbar .dropdown-menu li a:hover{background:#d73925}}.skin-red .main-header .logo{background-color:#d73925;color:#fff;border-bottom:0 solid transparent}.skin-red .main-header .logo:hover{background-color:#d33724}.skin-red .main-header li.user-header{background-color:#dd4b39}.skin-red .content-header{background:transparent}.skin-red .wrapper,.skin-red .main-sidebar,.skin-red .left-side{background-color:#222d32}.skin-red .user-panel>.info,.skin-red .user-panel>.info>a{color:#fff}.skin-red .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-red .sidebar-menu>li>a{border-left:3px solid transparent}.skin-red .sidebar-menu>li:hover>a,.skin-red .sidebar-menu>li.active>a,.skin-red .sidebar-menu>li.menu-open>a{color:#fff;background:#1e282c}.skin-red .sidebar-menu>li.active>a{border-left-color:#dd4b39}.skin-red .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-red .sidebar a{color:#b8c7ce}.skin-red .sidebar a:hover{text-decoration:none}.skin-red .sidebar-menu .treeview-menu>li>a{color:#8aa4af}.skin-red .sidebar-menu .treeview-menu>li.active>a,.skin-red .sidebar-menu .treeview-menu>li>a:hover{color:#fff}.skin-red .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px 10px}.skin-red .sidebar-form input[type="text"],.skin-red .sidebar-form .btn{box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px}.skin-red .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-red .sidebar-form input[type="text"]:focus,.skin-red .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-red .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-red .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0} -------------------------------------------------------------------------------- /static/dist/img/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/dist/img/avatar.png -------------------------------------------------------------------------------- /static/dist/img/avatar04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/dist/img/avatar04.png -------------------------------------------------------------------------------- /static/dist/img/avatar2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/dist/img/avatar2.png -------------------------------------------------------------------------------- /static/dist/img/avatar3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/dist/img/avatar3.png -------------------------------------------------------------------------------- /static/dist/img/avatar5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/dist/img/avatar5.png -------------------------------------------------------------------------------- /static/dist/img/boxed-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/dist/img/boxed-bg.jpg -------------------------------------------------------------------------------- /static/dist/img/boxed-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/dist/img/boxed-bg.png -------------------------------------------------------------------------------- /static/dist/img/credit/american-express.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/dist/img/credit/american-express.png -------------------------------------------------------------------------------- /static/dist/img/credit/cirrus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/dist/img/credit/cirrus.png -------------------------------------------------------------------------------- /static/dist/img/credit/mastercard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/dist/img/credit/mastercard.png -------------------------------------------------------------------------------- /static/dist/img/credit/mestro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/dist/img/credit/mestro.png -------------------------------------------------------------------------------- /static/dist/img/credit/paypal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/dist/img/credit/paypal.png -------------------------------------------------------------------------------- /static/dist/img/credit/paypal2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/dist/img/credit/paypal2.png -------------------------------------------------------------------------------- /static/dist/img/credit/visa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/dist/img/credit/visa.png -------------------------------------------------------------------------------- /static/dist/img/default-50x50.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/dist/img/default-50x50.gif -------------------------------------------------------------------------------- /static/dist/img/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/dist/img/icons.png -------------------------------------------------------------------------------- /static/dist/img/photo1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/dist/img/photo1.png -------------------------------------------------------------------------------- /static/dist/img/photo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/dist/img/photo2.png -------------------------------------------------------------------------------- /static/dist/img/photo3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/dist/img/photo3.jpg -------------------------------------------------------------------------------- /static/dist/img/photo4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/dist/img/photo4.jpg -------------------------------------------------------------------------------- /static/dist/img/user1-128x128.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/dist/img/user1-128x128.jpg -------------------------------------------------------------------------------- /static/dist/img/user2-160x160.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/dist/img/user2-160x160.jpg -------------------------------------------------------------------------------- /static/dist/img/user3-128x128.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/dist/img/user3-128x128.jpg -------------------------------------------------------------------------------- /static/dist/img/user4-128x128.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/dist/img/user4-128x128.jpg -------------------------------------------------------------------------------- /static/dist/img/user5-128x128.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/dist/img/user5-128x128.jpg -------------------------------------------------------------------------------- /static/dist/img/user6-128x128.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/dist/img/user6-128x128.jpg -------------------------------------------------------------------------------- /static/dist/img/user7-128x128.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/dist/img/user7-128x128.jpg -------------------------------------------------------------------------------- /static/dist/img/user8-128x128.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/dist/img/user8-128x128.jpg -------------------------------------------------------------------------------- /static/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /static/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /static/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /static/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /static/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /static/fonts/ionicons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/fonts/ionicons.eot -------------------------------------------------------------------------------- /static/fonts/ionicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/fonts/ionicons.ttf -------------------------------------------------------------------------------- /static/fonts/ionicons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/fonts/ionicons.woff -------------------------------------------------------------------------------- /static/images/Flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/images/Flow.png -------------------------------------------------------------------------------- /static/images/login.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/images/login.jpg -------------------------------------------------------------------------------- /static/images/login_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/images/login_img.png -------------------------------------------------------------------------------- /static/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/images/logo.png -------------------------------------------------------------------------------- /static/images/sprite_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuipy/lh-falcon/181ec242c625f336711e4c99db97d45157fa1ecf/static/images/sprite_0.png -------------------------------------------------------------------------------- /static/js/dataTables.bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | DataTables Bootstrap 3 integration 3 | ©2011-2015 SpryMedia Ltd - datatables.net/license 4 | */ 5 | (function(b){"function"===typeof define&&define.amd?define(["jquery","datatables.net"],function(a){return b(a,window,document)}):"object"===typeof exports?module.exports=function(a,d){a||(a=window);if(!d||!d.fn.dataTable)d=require("datatables.net")(a,d).$;return b(d,a,a.document)}:b(jQuery,window,document)})(function(b,a,d,m){var f=b.fn.dataTable;b.extend(!0,f.defaults,{dom:"<'row'<'col-sm-6'l><'col-sm-6'f>><'row'<'col-sm-12'tr>><'row'<'col-sm-5'i><'col-sm-7'p>>",renderer:"bootstrap"});b.extend(f.ext.classes, 6 | {sWrapper:"dataTables_wrapper form-inline dt-bootstrap",sFilterInput:"form-control input-sm",sLengthSelect:"form-control input-sm",sProcessing:"dataTables_processing panel panel-default"});f.ext.renderer.pageButton.bootstrap=function(a,h,r,s,j,n){var o=new f.Api(a),t=a.oClasses,k=a.oLanguage.oPaginate,u=a.oLanguage.oAria.paginate||{},e,g,p=0,q=function(d,f){var l,h,i,c,m=function(a){a.preventDefault();!b(a.currentTarget).hasClass("disabled")&&o.page()!=a.data.action&&o.page(a.data.action).draw("page")}; 7 | l=0;for(h=f.length;l",{"class":t.sPageButton+" "+g,id:0===r&&"string"===typeof c?a.sTableId+"_"+c:null}).append(b("",{href:"#", 8 | "aria-controls":a.sTableId,"aria-label":u[c],"data-dt-idx":p,tabindex:a.iTabIndex}).html(e)).appendTo(d),a.oApi._fnBindAction(i,{action:c},m),p++)}},i;try{i=b(h).find(d.activeElement).data("dt-idx")}catch(v){}q(b(h).empty().html('