intranet-system 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/intranet/resources/haml/system_home.haml +26 -0
- data/lib/intranet/resources/locales/en.yml +12 -0
- data/lib/intranet/resources/locales/fr.yml +12 -0
- data/lib/intranet/resources/www/jsystem.js +78 -0
- data/lib/intranet/resources/www/style.css +56 -0
- data/lib/intranet/system/linux_stats_provider.rb +92 -0
- data/lib/intranet/system/responder.rb +122 -0
- data/lib/intranet/system/version.rb +19 -0
- data/spec/intranet/system/linux_stats_provider_spec.rb +89 -0
- data/spec/intranet/system/responder_spec.rb +148 -0
- data/spec/intranet/system/sample_cpuinfo.txt +54 -0
- data/spec/intranet/system/sample_df-lB1_1.txt +12 -0
- data/spec/intranet/system/sample_df-lB1_2.txt +11 -0
- data/spec/intranet/system/sample_loadavg.txt +1 -0
- data/spec/intranet/system/sample_meminfo.txt +46 -0
- data/spec/intranet/system/sample_swaps.txt +3 -0
- data/spec/spec_helper.rb +49 -0
- metadata +151 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3d4d7f6e26e171a12ece83efe7be3a993a52d726
|
4
|
+
data.tar.gz: 3e8a424dd4e72f670443dbeb707c7200845bf1e3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 933eeeb020b40518af27883e09c79828b095b68b3e9f8c3f643bf9b462aeed3069c8b32b2a58c65baecd3a7cf627baed50131fd20d75c119726a0e988d2fcd49
|
7
|
+
data.tar.gz: 5dba5bd6d1a2e746de3c5f24a33c3b10805f3076e92dbdc366a83dca2b4d8617feddbe4632578602b25d291b9fa8836165d4a2819eede89eb36c3b9a33ae9435
|
@@ -0,0 +1,26 @@
|
|
1
|
+
%section
|
2
|
+
= to_markup 'title_and_breadcrumb', {title: title, nav: nav}
|
3
|
+
|
4
|
+
%h3#cpu_model= I18n.t('system.processor') + ' : '
|
5
|
+
%p#load1min.meter
|
6
|
+
%span.desc= '1 min'
|
7
|
+
%span.info= '0.00'
|
8
|
+
%meter{value: 0, min: 0, max: 100, low: 80, high: 90, optimum: 50, title: '0 %'}
|
9
|
+
%p#load5min.meter
|
10
|
+
%span.desc= '5 min'
|
11
|
+
%span.info= '0.00'
|
12
|
+
%meter{value: 0, min: 0, max: 100, low: 80, high: 90, optimum: 50, title: '0 %'}
|
13
|
+
%p#load15min.meter
|
14
|
+
%span.desc= '15 min'
|
15
|
+
%span.info= '0.00'
|
16
|
+
%meter{value: 0, min: 0, max: 100, low: 80, high: 90, optimum: 50, title: '0 %'}
|
17
|
+
|
18
|
+
%h3= I18n.t('system.memory')
|
19
|
+
%p#ram.meter
|
20
|
+
%span.desc= 'RAM'
|
21
|
+
%span.info= '0 M' + I18n.t('system.byte_abbrev') + ' ' + I18n.t('system.out_of') + ' 0 M' + I18n.t('system.byte_abbrev')
|
22
|
+
%meter{value: 0, min: 0, max: 100, low: 80, high: 90, optimum: 50, title: '0 %'}
|
23
|
+
%div#swaps.loading
|
24
|
+
|
25
|
+
%h3= I18n.t('system.storage')
|
26
|
+
%div#storage.loading
|
@@ -0,0 +1,78 @@
|
|
1
|
+
/**
|
2
|
+
* jsystem.js
|
3
|
+
* JavaScript functions for the system monitor.
|
4
|
+
*/
|
5
|
+
"use strict";
|
6
|
+
|
7
|
+
const refreshIntervalMs = 5000;
|
8
|
+
|
9
|
+
const orderToString = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi'];
|
10
|
+
|
11
|
+
function humanizeSize(size, order) {
|
12
|
+
if (size < 1024)
|
13
|
+
return Math.ceil(size) + ' ' + orderToString[order] + byte_abbrev;
|
14
|
+
else if (size < 10 * 1024)
|
15
|
+
return Math.ceil(size / 102.4) / 10 + ' ' + orderToString[order + 1] + byte_abbrev;
|
16
|
+
else
|
17
|
+
return humanizeSize(size / 1024, order + 1)
|
18
|
+
}
|
19
|
+
|
20
|
+
function updateMarkdownContent(json) {
|
21
|
+
/* CPU */
|
22
|
+
const cpuNumber = json.cpu.nb_cores;
|
23
|
+
|
24
|
+
if (document.getElementById('cpu_model').textContent.endsWith(' : ')) {
|
25
|
+
document.getElementById('cpu_model').textContent += json.cpu.model;
|
26
|
+
}
|
27
|
+
|
28
|
+
const loadAvgTriplet = ['load1min', 'load5min', 'load15min'];
|
29
|
+
for (let i = 0; i < loadAvgTriplet.length; i++) {
|
30
|
+
const load = json.loadavg[i];
|
31
|
+
document.getElementById(loadAvgTriplet[i]).getElementsByClassName('info')[0].textContent = load;
|
32
|
+
document.getElementById(loadAvgTriplet[i]).getElementsByTagName('meter')[0].max = 100 * cpuNumber;
|
33
|
+
document.getElementById(loadAvgTriplet[i]).getElementsByTagName('meter')[0].optimum = 50 * cpuNumber;
|
34
|
+
document.getElementById(loadAvgTriplet[i]).getElementsByTagName('meter')[0].low = 80 * cpuNumber;
|
35
|
+
document.getElementById(loadAvgTriplet[i]).getElementsByTagName('meter')[0].high = 90 * cpuNumber;
|
36
|
+
document.getElementById(loadAvgTriplet[i]).getElementsByTagName('meter')[0].value = Math.ceil(100 * load);
|
37
|
+
document.getElementById(loadAvgTriplet[i]).getElementsByTagName('meter')[0].title = Math.ceil(100 * load) + ' %';
|
38
|
+
}
|
39
|
+
|
40
|
+
/* RAM */
|
41
|
+
const percent = Math.ceil(100 * json.ram.used / json.ram.total);
|
42
|
+
const string = humanizeSize(json.ram.used, 0) + ' ' + out_of + ' ' + humanizeSize(json.ram.total, 0);
|
43
|
+
document.getElementById('ram').getElementsByClassName('info')[0].textContent = string;
|
44
|
+
document.getElementById('ram').getElementsByTagName('meter')[0].value = percent;
|
45
|
+
document.getElementById('ram').getElementsByTagName('meter')[0].title = percent + ' %';
|
46
|
+
|
47
|
+
/* SWAPs */
|
48
|
+
var swapHtml = '';
|
49
|
+
for (const [mountpoint, swap] of Object.entries(json.swaps)) {
|
50
|
+
const percent = Math.ceil(100 * swap.used / swap.total);
|
51
|
+
const string = humanizeSize(swap.used, 0) + ' ' + out_of + ' ' + humanizeSize(swap.total, 0);
|
52
|
+
swapHtml += '<p class="meter"><span class="desc">Swap : ' + mountpoint + '</span><span class="info">' + string + '</span>';
|
53
|
+
swapHtml += '<meter value="' + percent + '" min="0" max="100" low="80" high="90" optimum="50" title="' + percent + ' %">' + percent + '%</meter></p>';
|
54
|
+
}
|
55
|
+
document.getElementById('swaps').classList.remove('loading');
|
56
|
+
document.getElementById('swaps').innerHTML = swapHtml;
|
57
|
+
|
58
|
+
/* Partitions */
|
59
|
+
var storageHtml = '';
|
60
|
+
for (const [mountpoint, partition] of Object.entries(json.partitions)) {
|
61
|
+
const percent = Math.ceil(100 * partition.used / partition.total);
|
62
|
+
const string = humanizeSize(partition.used, 0) + ' ' + out_of + ' ' + humanizeSize(partition.total, 0);
|
63
|
+
storageHtml += '<p class="meter"><span class="desc">' + mountpoint + '</span><span class="info">' + string + '</span>';
|
64
|
+
storageHtml += '<meter value="' + percent + '" min="0" max="100" low="80" high="90" optimum="50" title="' + percent + ' %">' + percent + '%</meter></p>';
|
65
|
+
}
|
66
|
+
document.getElementById('storage').classList.remove('loading');
|
67
|
+
document.getElementById('storage').innerHTML = storageHtml;
|
68
|
+
}
|
69
|
+
|
70
|
+
function updateSystemStats() {
|
71
|
+
fetch('api')
|
72
|
+
.then(response => response.json())
|
73
|
+
.then(data => updateMarkdownContent(data))
|
74
|
+
}
|
75
|
+
|
76
|
+
/* Code executed after page load */
|
77
|
+
updateSystemStats();
|
78
|
+
setInterval(updateSystemStats, refreshIntervalMs);
|
@@ -0,0 +1,56 @@
|
|
1
|
+
/**
|
2
|
+
* system/style.css
|
3
|
+
* Design for the System module of the IntraNet.
|
4
|
+
*/
|
5
|
+
|
6
|
+
/******************************* System monitor *******************************/
|
7
|
+
|
8
|
+
p.meter {
|
9
|
+
margin: 0px 50px 2em; /* top sides bottom */
|
10
|
+
font-size: 110%;
|
11
|
+
}
|
12
|
+
p.meter span {
|
13
|
+
display: inline-block;
|
14
|
+
margin: 0px 8px;
|
15
|
+
}
|
16
|
+
p.meter span.desc {
|
17
|
+
float: left;
|
18
|
+
}
|
19
|
+
p.meter span.info {
|
20
|
+
float: right;
|
21
|
+
}
|
22
|
+
|
23
|
+
/* Mobile devices only */
|
24
|
+
@media only screen and (max-width: 600px), only screen and (max-device-width: 600px) {
|
25
|
+
p.meter {
|
26
|
+
width: auto;
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
meter {
|
31
|
+
width: 100%;
|
32
|
+
height: 1.6em;
|
33
|
+
border-radius: 0.8em;
|
34
|
+
background: none; /* required to get rid of default style */
|
35
|
+
background-color: #eaeaea;
|
36
|
+
}
|
37
|
+
:-moz-meter-optimum::-moz-meter-bar {
|
38
|
+
background: #8bcf69;
|
39
|
+
border-radius: inherit;
|
40
|
+
transition: 1s width;
|
41
|
+
}
|
42
|
+
:-moz-meter-sub-optimum::-moz-meter-bar {
|
43
|
+
background: #e6d450;
|
44
|
+
border-radius: inherit;
|
45
|
+
transition: 1s width;
|
46
|
+
}
|
47
|
+
:-moz-meter-sub-sub-optimum::-moz-meter-bar {
|
48
|
+
background: #f28f68;
|
49
|
+
border-radius: inherit;
|
50
|
+
transition: 1s width;
|
51
|
+
}
|
52
|
+
|
53
|
+
canvas {
|
54
|
+
width: 100%;
|
55
|
+
}
|
56
|
+
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'socket'
|
4
|
+
|
5
|
+
module Intranet
|
6
|
+
module System
|
7
|
+
# Provides system statistics for a Linux system.
|
8
|
+
class LinuxStatsProvider
|
9
|
+
# @!visibility protected
|
10
|
+
CPUINFO_FILE = '/proc/cpuinfo'
|
11
|
+
# @!visibility protected
|
12
|
+
LOADAVG_FILE = '/proc/loadavg'
|
13
|
+
# @!visibility protected
|
14
|
+
MEMINFO_FILE = '/proc/meminfo'
|
15
|
+
# @!visibility protected
|
16
|
+
SWAPS_FILE = '/proc/swaps'
|
17
|
+
|
18
|
+
# The system statistics refresh delay, in seconds.
|
19
|
+
# Under Linux, it is not necessary to refresh statistics more frequently than 5s since this is
|
20
|
+
# the refresh rate of the load average.
|
21
|
+
STATS_REFRESH_DELAY_S = 5
|
22
|
+
|
23
|
+
# Initializes a new Linux statistics provider.
|
24
|
+
def initialize
|
25
|
+
@stats = Hash[
|
26
|
+
hostname: Socket.gethostname,
|
27
|
+
loadavg: nil,
|
28
|
+
cpu: { model: cpu_model, nb_cores: nb_cores },
|
29
|
+
ram: { total: ram_available },
|
30
|
+
swaps: {},
|
31
|
+
partitions: {}]
|
32
|
+
@stats_date = Time.now - STATS_REFRESH_DELAY_S
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns the latest system statistics. System statistics are updated if they are outdated.
|
36
|
+
# @return [Hash] The system statistics, see {Responder#generate_page} for the structure.
|
37
|
+
def stats
|
38
|
+
now = Time.now
|
39
|
+
if outdated?(now)
|
40
|
+
@stats[:loadavg] = load_average
|
41
|
+
@stats[:ram][:used] = ram_used
|
42
|
+
@stats[:swaps] = swaps_usage
|
43
|
+
@stats[:partitions] = partitions_usage
|
44
|
+
@stats_date = now
|
45
|
+
end
|
46
|
+
@stats
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def outdated?(at)
|
52
|
+
(at - @stats_date) >= STATS_REFRESH_DELAY_S
|
53
|
+
end
|
54
|
+
|
55
|
+
def cpu_model
|
56
|
+
File.readlines(CPUINFO_FILE).grep(/model name/)[0].split(/:/)[1].strip
|
57
|
+
end
|
58
|
+
|
59
|
+
def nb_cores
|
60
|
+
File.readlines(CPUINFO_FILE).grep(/^processor/).size
|
61
|
+
end
|
62
|
+
|
63
|
+
def load_average
|
64
|
+
File.read(LOADAVG_FILE).split(' ').first(3).map(&:to_f)
|
65
|
+
end
|
66
|
+
|
67
|
+
def ram_available
|
68
|
+
# /proc/meminfo & /proc/swaps display Kib values despite indicating 'Kb'.
|
69
|
+
File.readlines(MEMINFO_FILE).grep(/^MemTotal/)[0].split(' ')[1].to_i * 1024
|
70
|
+
end
|
71
|
+
|
72
|
+
def ram_used
|
73
|
+
free = File.readlines(MEMINFO_FILE).grep(/^MemAvailable/)[0].split(' ')[1].to_i * 1024
|
74
|
+
@stats[:ram][:total] - free
|
75
|
+
end
|
76
|
+
|
77
|
+
def swaps_usage
|
78
|
+
File.readlines(SWAPS_FILE).grep(%r{^/}).map do |swap|
|
79
|
+
infos = swap.split
|
80
|
+
{ infos[0] => { total: infos[2].to_i * 1024, used: infos[3].to_i * 1024 } }
|
81
|
+
end.reduce({}, :update)
|
82
|
+
end
|
83
|
+
|
84
|
+
def partitions_usage
|
85
|
+
`df -lB1`.lines.grep(%r{^/}).sort.map do |partition|
|
86
|
+
infos = partition.split
|
87
|
+
{ infos[5] => { total: infos[1].to_i, used: infos[2].to_i } }
|
88
|
+
end.reduce({}, :update)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'intranet/abstract_responder'
|
4
|
+
require 'intranet/core/haml_wrapper'
|
5
|
+
require 'intranet/core/locales'
|
6
|
+
require_relative 'version'
|
7
|
+
require 'json'
|
8
|
+
|
9
|
+
module Intranet
|
10
|
+
module System
|
11
|
+
# The responder for the System monitor module of the Intranet.
|
12
|
+
class Responder < AbstractResponder
|
13
|
+
include Core::HamlWrapper # 'inherits' from methods of HamlWrapper
|
14
|
+
|
15
|
+
# Returns the name of the module.
|
16
|
+
# @return [String] The name of the module
|
17
|
+
def self.module_name
|
18
|
+
NAME
|
19
|
+
end
|
20
|
+
|
21
|
+
# The version of the module, according to semantic versionning.
|
22
|
+
# @return [String] The version of the module
|
23
|
+
def self.module_version
|
24
|
+
VERSION
|
25
|
+
end
|
26
|
+
|
27
|
+
# The homepage of the module.
|
28
|
+
# @return [String] The homepage URL of the module.
|
29
|
+
def self.module_homepage
|
30
|
+
HOMEPAGE_URL
|
31
|
+
end
|
32
|
+
|
33
|
+
# Initializes a new System responder instance.
|
34
|
+
# @param provider [Object] The system statistics provider, responding to +#stats+.
|
35
|
+
# @param in_menu [Boolean] Whether the module instance should be displayed in the main
|
36
|
+
# navigation menu or not.
|
37
|
+
def initialize(provider, in_menu = true)
|
38
|
+
@provider = provider
|
39
|
+
@in_menu = in_menu
|
40
|
+
end
|
41
|
+
|
42
|
+
# Specifies if the responder instance should be displayed in the main navigation menu or not.
|
43
|
+
# @return [Boolean] True if the responder instance should be added to the main navigation
|
44
|
+
# menu, False otherwise.
|
45
|
+
def in_menu?
|
46
|
+
@in_menu
|
47
|
+
end
|
48
|
+
|
49
|
+
# Specifies the absolute path to the resources directory for that module.
|
50
|
+
# @return [String] The absolute path to the resources directory for the module.
|
51
|
+
def resources_dir
|
52
|
+
File.absolute_path(File.join('..', 'resources'), __dir__)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Generates the HTML content associated to the given +path+ and +query+.
|
56
|
+
# === REST API Description:
|
57
|
+
# Read-only access to system statistics under +/api+ using +GET+ method. Response is in JSON
|
58
|
+
# format with the following structure:
|
59
|
+
# {
|
60
|
+
# "hostname": "trantor",
|
61
|
+
# "loadavg": [0.42,0.48,0.56],
|
62
|
+
# "cpu": {
|
63
|
+
# "model": "Intel(R) Core(TM) i5-6500 CPU @ 3.20GHz",
|
64
|
+
# "nb_cores": 4
|
65
|
+
# },
|
66
|
+
# "ram": { "total": 8243249152, "used": 3149385728 },
|
67
|
+
# "swaps": {
|
68
|
+
# "/dev/sdb1": { "total": 19999485952, "used": 0 }
|
69
|
+
# },
|
70
|
+
# "partitions": {
|
71
|
+
# "/boot/efi": { "total": 509640704, "used": 135168 },
|
72
|
+
# "/": { "total": 58306199552, "used": 11482054656 }
|
73
|
+
# }
|
74
|
+
# }
|
75
|
+
# @param path [String] The requested URI, relative to that module root URI.
|
76
|
+
# @param query [Hash] The URI variable/value pairs, if any.
|
77
|
+
# @return [Array] The HTTP return code, the MIME type and the answer body.
|
78
|
+
def generate_page(path, query)
|
79
|
+
case path
|
80
|
+
when %r{^/index\.html$}
|
81
|
+
return 206, 'text/html', generate_home_html
|
82
|
+
when %r{^/api$}
|
83
|
+
return 200, 'application/json', @provider.stats.to_json
|
84
|
+
when %r{^/i18n\.js$}
|
85
|
+
return 200, 'text/javascript', generate_i18n_js
|
86
|
+
end
|
87
|
+
super(path, query)
|
88
|
+
end
|
89
|
+
|
90
|
+
# The title of the System monitor module, as displayed on the web page.
|
91
|
+
# @return [String] The title of the System monitor web page.
|
92
|
+
def title
|
93
|
+
I18n.t('system.monitor')
|
94
|
+
end
|
95
|
+
|
96
|
+
# Provides the list of Cascade Style Sheets (CSS) dependencies for this module.
|
97
|
+
# @return [Array] The list of CSS dependencies.
|
98
|
+
def css_dependencies
|
99
|
+
super + ['design/style.css']
|
100
|
+
end
|
101
|
+
|
102
|
+
# Provides the list of Javascript files (JS) dependencies for this module.
|
103
|
+
# @return [Array] The list of JS dependencies.
|
104
|
+
def js_dependencies
|
105
|
+
super + ['i18n.js', 'design/jsystem.js']
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
def generate_home_html
|
111
|
+
content = to_markup('system_home',
|
112
|
+
nav: { I18n.t('nav.home') => '/index.html', title => nil })
|
113
|
+
{ content: content, title: title }
|
114
|
+
end
|
115
|
+
|
116
|
+
def generate_i18n_js
|
117
|
+
'var byte_abbrev = \'' + I18n.t('system.byte_abbrev') + '\';' + "\n" \
|
118
|
+
'var out_of = \'' + I18n.t('system.out_of') + '\';' + "\n"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# The main Intranet namespace.
|
4
|
+
module Intranet
|
5
|
+
# The System monitor module for the Intranet.
|
6
|
+
module System
|
7
|
+
# The name of the gem.
|
8
|
+
NAME = 'intranet-system'
|
9
|
+
|
10
|
+
# The version of the gem, according to semantic versionning.
|
11
|
+
VERSION = '1.0.0'
|
12
|
+
|
13
|
+
# The URL of the gem homepage.
|
14
|
+
HOMEPAGE_URL = 'https://rubygems.org/gems/intranet-system'
|
15
|
+
|
16
|
+
# The URL of the gem source code.
|
17
|
+
SOURCES_URL = 'https://bitbucket.org/ebling-mis/intranet-system'
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'intranet/system/linux_stats_provider'
|
4
|
+
|
5
|
+
RSpec.describe Intranet::System::LinuxStatsProvider do
|
6
|
+
before do
|
7
|
+
expect(described_class::CPUINFO_FILE).to eql('/proc/cpuinfo')
|
8
|
+
stub_const('Intranet::System::LinuxStatsProvider::CPUINFO_FILE',
|
9
|
+
File.join(__dir__, 'sample_cpuinfo.txt'))
|
10
|
+
|
11
|
+
expect(described_class::LOADAVG_FILE).to eql('/proc/loadavg')
|
12
|
+
stub_const('Intranet::System::LinuxStatsProvider::LOADAVG_FILE',
|
13
|
+
File.join(__dir__, 'sample_loadavg.txt'))
|
14
|
+
|
15
|
+
expect(described_class::MEMINFO_FILE).to eql('/proc/meminfo')
|
16
|
+
stub_const('Intranet::System::LinuxStatsProvider::MEMINFO_FILE',
|
17
|
+
File.join(__dir__, 'sample_meminfo.txt'))
|
18
|
+
|
19
|
+
expect(described_class::SWAPS_FILE).to eql('/proc/swaps')
|
20
|
+
stub_const('Intranet::System::LinuxStatsProvider::SWAPS_FILE',
|
21
|
+
File.join(__dir__, 'sample_swaps.txt'))
|
22
|
+
|
23
|
+
allow(Time).to receive(:now).and_return(0)
|
24
|
+
|
25
|
+
@provider = Intranet::System::LinuxStatsProvider.new
|
26
|
+
allow(@provider).to receive(:`).with('df -lB1').and_return(
|
27
|
+
File.read(File.join(__dir__, 'sample_df-lB1_1.txt'))
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#stats' do
|
32
|
+
it 'should return the latest system statistics' do
|
33
|
+
expected1 = {
|
34
|
+
hostname: File.read('/etc/hostname').chomp,
|
35
|
+
loadavg: [0.76, 0.61, 0.42],
|
36
|
+
cpu: {
|
37
|
+
model: 'Intel(R) Pentium(R) CPU G4400 @ 3.30GHz',
|
38
|
+
nb_cores: 2
|
39
|
+
},
|
40
|
+
ram: { total: 4_014_243_840, used: 2_765_328_384 },
|
41
|
+
swaps: {
|
42
|
+
'/dev/sda3' => { total: 9_699_323_904, used: 464_449_536 },
|
43
|
+
'/swapfile' => { total: 134_209_536, used: 38_797_312 }
|
44
|
+
},
|
45
|
+
partitions: {
|
46
|
+
'/boot/efi' => { total: 509_640_704, used: 135_168 },
|
47
|
+
'/' => { total: 58_306_199_552, used: 11_480_780_800 },
|
48
|
+
'/home' => { total: 884_998_905_856, used: 248_169_803_776 },
|
49
|
+
'/media/user/disk' => { total: 1_000_202_039_296, used: 674_241_961_984 }
|
50
|
+
}
|
51
|
+
}
|
52
|
+
expected2 = {
|
53
|
+
hostname: File.read('/etc/hostname').chomp,
|
54
|
+
loadavg: [0.76, 0.61, 0.42],
|
55
|
+
cpu: {
|
56
|
+
model: 'Intel(R) Pentium(R) CPU G4400 @ 3.30GHz',
|
57
|
+
nb_cores: 2
|
58
|
+
},
|
59
|
+
ram: { total: 4_014_243_840, used: 2_765_328_384 },
|
60
|
+
swaps: {
|
61
|
+
'/dev/sda3' => { total: 9_699_323_904, used: 464_449_536 },
|
62
|
+
'/swapfile' => { total: 134_209_536, used: 38_797_312 }
|
63
|
+
},
|
64
|
+
partitions: {
|
65
|
+
'/boot/efi' => { total: 509_640_704, used: 135_168 },
|
66
|
+
'/' => { total: 58_306_199_552, used: 11_480_780_800 },
|
67
|
+
'/home' => { total: 884_998_905_856, used: 248_169_803_776 }
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
expect(@provider.stats).to eql(expected1)
|
72
|
+
|
73
|
+
# Modify df return to remove a disk
|
74
|
+
allow(@provider).to receive(:`).with('df -lB1').and_return(
|
75
|
+
File.read(File.join(__dir__, 'sample_df-lB1_2.txt'))
|
76
|
+
)
|
77
|
+
|
78
|
+
allow(Time).to receive(:now).and_return(4)
|
79
|
+
|
80
|
+
# Check that output is not modified: system statistics are still valid
|
81
|
+
expect(@provider.stats).to eql(expected1)
|
82
|
+
|
83
|
+
allow(Time).to receive(:now).and_return(5)
|
84
|
+
|
85
|
+
# Check that output is modified: system statistics have expired
|
86
|
+
expect(@provider.stats).to eql(expected2)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'intranet/core'
|
4
|
+
require 'intranet/logger'
|
5
|
+
require 'intranet/abstract_responder'
|
6
|
+
require 'intranet/system/responder'
|
7
|
+
|
8
|
+
RSpec.describe Intranet::System::Responder do
|
9
|
+
it 'should inherit from Intranet::AbstractResponder' do
|
10
|
+
expect(described_class.superclass).to eql(Intranet::AbstractResponder)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should define its name, version and homepage' do
|
14
|
+
expect { described_class.module_name }.not_to raise_error
|
15
|
+
expect { described_class.module_version }.not_to raise_error
|
16
|
+
expect { described_class.module_homepage }.not_to raise_error
|
17
|
+
end
|
18
|
+
|
19
|
+
before do
|
20
|
+
logger = Intranet::Logger.new(Intranet::Logger::FATAL)
|
21
|
+
@core = Intranet::Core.new(logger)
|
22
|
+
|
23
|
+
provider = double('Intranet::System::Provider')
|
24
|
+
@stats = {
|
25
|
+
hostname: File.read('/etc/hostname').chomp,
|
26
|
+
loadavg: [0.76, 0.61, 0.42],
|
27
|
+
cpu: {
|
28
|
+
model: 'Intel(R) Pentium(R) CPU G4400 @ 3.30GHz',
|
29
|
+
nb_cores: 2
|
30
|
+
},
|
31
|
+
ram: { total: 4_014_243_840, used: 2_765_328_384 },
|
32
|
+
swaps: {
|
33
|
+
'/dev/sda3' => { total: 9_699_323_904, used: 464_449_536 },
|
34
|
+
'/swapfile' => { total: 134_209_536, used: 38_797_312 }
|
35
|
+
},
|
36
|
+
partitions: {
|
37
|
+
'/boot/efi' => { total: 509_640_704, used: 135_168 },
|
38
|
+
'/' => { total: 58_306_199_552, used: 11_480_780_800 },
|
39
|
+
'/home' => { total: 884_998_905_856, used: 248_169_803_776 },
|
40
|
+
'/media/user/disk' => { total: 1_000_202_039_296, used: 674_241_961_984 }
|
41
|
+
}
|
42
|
+
}
|
43
|
+
allow(provider).to receive(:stats).and_return(@stats)
|
44
|
+
|
45
|
+
@responder = described_class.new(provider)
|
46
|
+
@core.register_module(
|
47
|
+
@responder, ['system'], File.absolute_path('../../../lib/intranet/resources', __dir__)
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#in_menu?' do
|
52
|
+
it 'should return the value provided at initialization' do
|
53
|
+
expect(described_class.new(nil, false).in_menu?).to be false
|
54
|
+
expect(described_class.new(nil, true).in_menu?).to be true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '#resources_dir' do
|
59
|
+
it 'should return the absolute path of the resources directory' do
|
60
|
+
expect(described_class.new(nil, false).resources_dir).to eql(
|
61
|
+
File.absolute_path('../../../lib/intranet/resources', __dir__)
|
62
|
+
)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe '#title' do
|
67
|
+
it 'should return the title of the webpage provided by the module' do
|
68
|
+
expect(@responder.title).to eql(I18n.t('system.monitor'))
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe '#css_dependencies' do
|
73
|
+
it 'should return the list of CSS dependencies' do
|
74
|
+
expect(@responder.css_dependencies).to include('design/style.css')
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe '#js_dependencies' do
|
79
|
+
it 'should return the list of JavaScript dependencies' do
|
80
|
+
expect(@responder.js_dependencies).to include('design/jsystem.js')
|
81
|
+
expect(@responder.js_dependencies).to include('i18n.js')
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe '#generate_page' do
|
86
|
+
context 'when asked for \'/index.html\'' do
|
87
|
+
it 'should return a partial HTML content with a loader' do
|
88
|
+
code, mime, content = @responder.generate_page('/index.html', {})
|
89
|
+
expect(code).to eql(206)
|
90
|
+
expect(mime).to eql('text/html')
|
91
|
+
expect(content).to eql(
|
92
|
+
Hash[content: "<section>\n<h2>#{I18n.t('system.monitor')}</h2>\n" \
|
93
|
+
"<ul class='breadcrumb'>\n" \
|
94
|
+
"<li>\n<a href='/index.html'>#{I18n.t('nav.home')}</a>\n</li>\n" \
|
95
|
+
"<li>#{I18n.t('system.monitor')}</li>\n" \
|
96
|
+
"</ul>\n\n" \
|
97
|
+
"<h3 id='cpu_model'>#{I18n.t('system.processor')} : </h3>\n" \
|
98
|
+
"<p class='meter' id='load1min'>\n<span class='desc'>1 min</span>\n<span " \
|
99
|
+
"class='info'>0.00</span>\n<meter high='90' low='80' max='100' min='0' op" \
|
100
|
+
"timum='50' title='0 %' value='0'></meter>\n</p>\n" \
|
101
|
+
"<p class='meter' id='load5min'>\n<span class='desc'>5 min</span>\n<span " \
|
102
|
+
"class='info'>0.00</span>\n<meter high='90' low='80' max='100' min='0' op" \
|
103
|
+
"timum='50' title='0 %' value='0'></meter>\n</p>\n" \
|
104
|
+
"<p class='meter' id='load15min'>\n<span class='desc'>15 min</span>\n<spa" \
|
105
|
+
"n class='info'>0.00</span>\n<meter high='90' low='80' max='100' min='0' " \
|
106
|
+
"optimum='50' title='0 %' value='0'></meter>\n</p>\n" \
|
107
|
+
"<h3>#{I18n.t('system.memory')}</h3>\n" \
|
108
|
+
"<p class='meter' id='ram'>\n<span class='desc'>RAM</span>\n<span class='" \
|
109
|
+
"info'>0 M#{I18n.t('system.byte_abbrev')} #{I18n.t('system.out_of')} 0 M" \
|
110
|
+
"#{I18n.t('system.byte_abbrev')}</span>\n<meter high='90' low='80' max='1" \
|
111
|
+
"00' min='0' optimum='50' title='0 %' value='0'></meter>\n</p>\n" \
|
112
|
+
"<div class='loading' id='swaps'></div>\n" \
|
113
|
+
"<h3>#{I18n.t('system.storage')}</h3>\n" \
|
114
|
+
"<div class='loading' id='storage'></div>\n" \
|
115
|
+
"</section>\n",
|
116
|
+
title: I18n.t('system.monitor')]
|
117
|
+
)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context 'when asked for \'/api\'' do
|
122
|
+
it 'should return a JSON representation of the system statistics' do
|
123
|
+
code, mime, content = @responder.generate_page('/api', {})
|
124
|
+
expect(code).to eql(200)
|
125
|
+
expect(mime).to eql('application/json')
|
126
|
+
expect(content).to eql(@stats.to_json)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context 'when asked for \'/i18n.js\'' do
|
131
|
+
it 'should return the internationalized version of required JavaScript variables' do
|
132
|
+
code, mime, content = @responder.generate_page('/i18n.js', {})
|
133
|
+
expect(code).to eql(200)
|
134
|
+
expect(mime).to eql('text/javascript')
|
135
|
+
expect(content).to eql(
|
136
|
+
"var byte_abbrev = '#{I18n.t('system.byte_abbrev')}';\n" \
|
137
|
+
"var out_of = '#{I18n.t('system.out_of')}';\n"
|
138
|
+
)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
context 'otherwise' do
|
143
|
+
it 'should return an HTTP 404 error' do
|
144
|
+
expect(@responder.generate_page('index.html', {})).to eql([404, '', ''])
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
processor : 0
|
2
|
+
vendor_id : GenuineIntel
|
3
|
+
cpu family : 6
|
4
|
+
model : 94
|
5
|
+
model name : Intel(R) Pentium(R) CPU G4400 @ 3.30GHz
|
6
|
+
stepping : 3
|
7
|
+
microcode : 0xba
|
8
|
+
cpu MHz : 799.822
|
9
|
+
cache size : 3072 KB
|
10
|
+
physical id : 0
|
11
|
+
siblings : 2
|
12
|
+
core id : 0
|
13
|
+
cpu cores : 2
|
14
|
+
apicid : 0
|
15
|
+
initial apicid : 0
|
16
|
+
fpu : yes
|
17
|
+
fpu_exception : yes
|
18
|
+
cpuid level : 22
|
19
|
+
wp : yes
|
20
|
+
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave rdrand lahf_lm abm 3dnowprefetch intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust erms invpcid rdseed smap clflushopt xsaveopt xsavec xgetbv1 xsaves dtherm arat pln pts hwp hwp_notify hwp_act_window hwp_epp
|
21
|
+
bugs :
|
22
|
+
bogomips : 6624.00
|
23
|
+
clflush size : 64
|
24
|
+
cache_alignment : 64
|
25
|
+
address sizes : 39 bits physical, 48 bits virtual
|
26
|
+
power management:
|
27
|
+
|
28
|
+
processor : 1
|
29
|
+
vendor_id : GenuineIntel
|
30
|
+
cpu family : 6
|
31
|
+
model : 94
|
32
|
+
model name : Intel(R) Pentium(R) CPU G4400 @ 3.30GHz
|
33
|
+
stepping : 3
|
34
|
+
microcode : 0xba
|
35
|
+
cpu MHz : 799.822
|
36
|
+
cache size : 3072 KB
|
37
|
+
physical id : 0
|
38
|
+
siblings : 2
|
39
|
+
core id : 1
|
40
|
+
cpu cores : 2
|
41
|
+
apicid : 2
|
42
|
+
initial apicid : 2
|
43
|
+
fpu : yes
|
44
|
+
fpu_exception : yes
|
45
|
+
cpuid level : 22
|
46
|
+
wp : yes
|
47
|
+
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave rdrand lahf_lm abm 3dnowprefetch intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust erms invpcid rdseed smap clflushopt xsaveopt xsavec xgetbv1 xsaves dtherm arat pln pts hwp hwp_notify hwp_act_window hwp_epp
|
48
|
+
bugs :
|
49
|
+
bogomips : 6625.05
|
50
|
+
clflush size : 64
|
51
|
+
cache_alignment : 64
|
52
|
+
address sizes : 39 bits physical, 48 bits virtual
|
53
|
+
power management:
|
54
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
Filesystem 1B-blocks Used Available Use% Mounted on
|
2
|
+
udev 4108283904 0 4108283904 0% /dev
|
3
|
+
tmpfs 824324096 18341888 805982208 3% /run
|
4
|
+
/dev/sda2 58306199552 11480780800 43833151488 21% /
|
5
|
+
tmpfs 4121620480 49844224 4071776256 2% /dev/shm
|
6
|
+
tmpfs 5242880 4096 5238784 1% /run/lock
|
7
|
+
tmpfs 4121620480 0 4121620480 0% /sys/fs/cgroup
|
8
|
+
/dev/sda1 509640704 135168 509505536 1% /boot/efi
|
9
|
+
/dev/sdb4 884998905856 248169803776 591802150912 30% /home
|
10
|
+
/dev/sdc1 1000202039296 674241961984 325960077312 68% /media/user/disk
|
11
|
+
tmpfs 824324096 16384 824307712 1% /run/user/115
|
12
|
+
tmpfs 824324096 53248 824270848 1% /run/user/1000
|
@@ -0,0 +1,11 @@
|
|
1
|
+
Filesystem 1B-blocks Used Available Use% Mounted on
|
2
|
+
udev 4108283904 0 4108283904 0% /dev
|
3
|
+
tmpfs 824324096 18341888 805982208 3% /run
|
4
|
+
/dev/sda2 58306199552 11480780800 43833151488 21% /
|
5
|
+
tmpfs 4121620480 49844224 4071776256 2% /dev/shm
|
6
|
+
tmpfs 5242880 4096 5238784 1% /run/lock
|
7
|
+
tmpfs 4121620480 0 4121620480 0% /sys/fs/cgroup
|
8
|
+
/dev/sda1 509640704 135168 509505536 1% /boot/efi
|
9
|
+
/dev/sdb4 884998905856 248169803776 591802150912 30% /home
|
10
|
+
tmpfs 824324096 16384 824307712 1% /run/user/115
|
11
|
+
tmpfs 824324096 53248 824270848 1% /run/user/1000
|
@@ -0,0 +1 @@
|
|
1
|
+
0.76 0.61 0.42 1/559 9547
|
@@ -0,0 +1,46 @@
|
|
1
|
+
MemTotal: 3920160 kB
|
2
|
+
MemFree: 395348 kB
|
3
|
+
MemAvailable: 1219644 kB
|
4
|
+
Buffers: 80692 kB
|
5
|
+
Cached: 1187364 kB
|
6
|
+
SwapCached: 61280 kB
|
7
|
+
Active: 1804312 kB
|
8
|
+
Inactive: 1542132 kB
|
9
|
+
Active(anon): 1316516 kB
|
10
|
+
Inactive(anon): 1024448 kB
|
11
|
+
Active(file): 487796 kB
|
12
|
+
Inactive(file): 517684 kB
|
13
|
+
Unevictable: 0 kB
|
14
|
+
Mlocked: 0 kB
|
15
|
+
SwapTotal: 9471996 kB
|
16
|
+
SwapFree: 9018424 kB
|
17
|
+
Dirty: 16 kB
|
18
|
+
Writeback: 0 kB
|
19
|
+
AnonPages: 2067636 kB
|
20
|
+
Mapped: 336356 kB
|
21
|
+
Shmem: 262580 kB
|
22
|
+
Slab: 89196 kB
|
23
|
+
SReclaimable: 46096 kB
|
24
|
+
SUnreclaim: 43100 kB
|
25
|
+
KernelStack: 8000 kB
|
26
|
+
PageTables: 38636 kB
|
27
|
+
NFS_Unstable: 0 kB
|
28
|
+
Bounce: 0 kB
|
29
|
+
WritebackTmp: 0 kB
|
30
|
+
CommitLimit: 11432076 kB
|
31
|
+
Committed_AS: 8624020 kB
|
32
|
+
VmallocTotal: 34359738367 kB
|
33
|
+
VmallocUsed: 0 kB
|
34
|
+
VmallocChunk: 0 kB
|
35
|
+
HardwareCorrupted: 0 kB
|
36
|
+
AnonHugePages: 0 kB
|
37
|
+
ShmemHugePages: 0 kB
|
38
|
+
ShmemPmdMapped: 0 kB
|
39
|
+
HugePages_Total: 0
|
40
|
+
HugePages_Free: 0
|
41
|
+
HugePages_Rsvd: 0
|
42
|
+
HugePages_Surp: 0
|
43
|
+
Hugepagesize: 2048 kB
|
44
|
+
DirectMap4k: 160356 kB
|
45
|
+
DirectMap2M: 3903488 kB
|
46
|
+
DirectMap1G: 0 kB
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This file is automatically loaded in each *_spec.rb file, so keep it light!
|
4
|
+
|
5
|
+
RSpec.configure do |config|
|
6
|
+
config.expect_with :rspec do |expectations|
|
7
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
8
|
+
end
|
9
|
+
|
10
|
+
config.mock_with :rspec do |mocks|
|
11
|
+
mocks.verify_partial_doubles = true
|
12
|
+
end
|
13
|
+
|
14
|
+
config.shared_context_metadata_behavior = :apply_to_host_groups
|
15
|
+
|
16
|
+
config.disable_monkey_patching!
|
17
|
+
|
18
|
+
# config.warnings = true
|
19
|
+
|
20
|
+
# Use the documentation formatter when RSpec is launched with one file only
|
21
|
+
config.default_formatter = 'doc' if config.files_to_run.one?
|
22
|
+
|
23
|
+
# Print the 10 slowest examples and example groups at the
|
24
|
+
# end of the spec run, to help surface which specs are running
|
25
|
+
# particularly slow.
|
26
|
+
# config.profile_examples = 10
|
27
|
+
|
28
|
+
# Run specs in random order to surface order dependencies. If you find an
|
29
|
+
# order dependency and want to debug it, you can fix the order by providing
|
30
|
+
# the seed, which is printed after each run.
|
31
|
+
# --seed 1234
|
32
|
+
config.order = :random
|
33
|
+
|
34
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
35
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
36
|
+
# test failures related to randomization by passing the same `--seed` value
|
37
|
+
# as the one that triggered the failure.
|
38
|
+
Kernel.srand config.seed
|
39
|
+
|
40
|
+
# Add lib/ directory to load path
|
41
|
+
$LOAD_PATH << File.absolute_path(File.join('..', 'lib'), __dir__)
|
42
|
+
|
43
|
+
# Load and start SimpleCov to gather code coverage information
|
44
|
+
unless config.files_to_run.one?
|
45
|
+
require 'simplecov'
|
46
|
+
SimpleCov.add_filter 'spec' # exclude 'spec' folder from coverage
|
47
|
+
SimpleCov.start
|
48
|
+
end
|
49
|
+
end
|
metadata
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: intranet-system
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ebling Mis
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-02-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: intranet-core
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 2.1.0
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '2.0'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 2.1.0
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: rake
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
type: :development
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rspec
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: rubocop
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: 0.63.0
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 0.63.0
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: simplecov
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: yard
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
description:
|
104
|
+
email: ebling.mis@protonmail.com
|
105
|
+
executables: []
|
106
|
+
extensions: []
|
107
|
+
extra_rdoc_files: []
|
108
|
+
files:
|
109
|
+
- lib/intranet/resources/haml/system_home.haml
|
110
|
+
- lib/intranet/resources/locales/en.yml
|
111
|
+
- lib/intranet/resources/locales/fr.yml
|
112
|
+
- lib/intranet/resources/www/jsystem.js
|
113
|
+
- lib/intranet/resources/www/style.css
|
114
|
+
- lib/intranet/system/linux_stats_provider.rb
|
115
|
+
- lib/intranet/system/responder.rb
|
116
|
+
- lib/intranet/system/version.rb
|
117
|
+
- spec/intranet/system/linux_stats_provider_spec.rb
|
118
|
+
- spec/intranet/system/responder_spec.rb
|
119
|
+
- spec/intranet/system/sample_cpuinfo.txt
|
120
|
+
- spec/intranet/system/sample_df-lB1_1.txt
|
121
|
+
- spec/intranet/system/sample_df-lB1_2.txt
|
122
|
+
- spec/intranet/system/sample_loadavg.txt
|
123
|
+
- spec/intranet/system/sample_meminfo.txt
|
124
|
+
- spec/intranet/system/sample_swaps.txt
|
125
|
+
- spec/spec_helper.rb
|
126
|
+
homepage: https://rubygems.org/gems/intranet-system
|
127
|
+
licenses:
|
128
|
+
- MIT
|
129
|
+
metadata:
|
130
|
+
source_code_uri: https://bitbucket.org/ebling-mis/intranet-system
|
131
|
+
post_install_message:
|
132
|
+
rdoc_options: []
|
133
|
+
require_paths:
|
134
|
+
- lib
|
135
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - "~>"
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '2.3'
|
140
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0'
|
145
|
+
requirements: []
|
146
|
+
rubyforge_project:
|
147
|
+
rubygems_version: 2.5.2.1
|
148
|
+
signing_key:
|
149
|
+
specification_version: 4
|
150
|
+
summary: System module for the intranet.
|
151
|
+
test_files: []
|