david
3 years ago
commit
693934ed65
17 changed files with 12399 additions and 0 deletions
@ -0,0 +1,5 @@
|
||||
node_modules/ |
||||
rubbish/ |
||||
db.sqlite3 |
||||
data.csv |
||||
public/styles/style.css |
@ -0,0 +1,5 @@
|
||||
node_modules/ |
||||
rubbish/ |
||||
db.sqlite3 |
||||
data.csv |
||||
public/styles/style.css |
@ -0,0 +1,10 @@
|
||||
FROM nikolaik/python-nodejs:python3.9-nodejs16-alpine |
||||
ENV PYTHONUNBUFFERED=1 NODE_ENV=production |
||||
WORKDIR /app |
||||
COPY ./netmon.py ./ |
||||
CMD [ "python", "./netmon.py" , "monitor" ] |
||||
|
||||
# copy index/views/public/pkg.json/postcss/tailwind configs |
||||
#npm i |
||||
#build css |
||||
#run supervisord or wrapper |
@ -0,0 +1,23 @@
|
||||
# netmon |
||||
|
||||
bunch of misc cmds |
||||
|
||||
``` |
||||
docker run --rm -it --name pls -w /app -p 3000:3000 -p 5000:5000 -e dstHost=localhost -e dstPort=8000 -e TZ=Australia/Sydney -v /home/blender/projects/netmon:/app nikolaik/python-nodejs:python3.9-nodejs16-alpine sh |
||||
docker exec -it pls sh |
||||
apk add sqlite |
||||
npm install -g nodemon |
||||
npm install express sqlite |
||||
python -m http.server 8000 |
||||
export dstHost=localhost dstPort=8000 |
||||
python netmon.py monitor |
||||
|
||||
sqlite3 db.sqlite3 |
||||
.headers on |
||||
.mode column |
||||
`SELECT * FROM netmonResults;` |
||||
|
||||
npm install -D tailwindcss@latest postcss@latest autoprefixer@latest postcss-cli |
||||
|
||||
npm install tailwindcss postcss autoprefixer postcss-cli |
||||
``` |
@ -0,0 +1,17 @@
|
||||
var sqlite3 = require('sqlite3').verbose(); |
||||
|
||||
const db = new sqlite3.Database('db.sqlite3', (err) => { |
||||
if (err) { |
||||
console.error(err.message); |
||||
throw err; |
||||
} else { |
||||
console.log('Connected to the SQLite database.'); |
||||
db.run( |
||||
`CREATE TABLE IF NOT EXISTS netmonResults (id integer PRIMARY KEY,
|
||||
timestamp text,
|
||||
status integer)` |
||||
); |
||||
} |
||||
}); |
||||
|
||||
module.exports = db; |
@ -0,0 +1,36 @@
|
||||
const express = require('express'); |
||||
const app = express(); |
||||
const port = 3000; |
||||
const path = require('path'); |
||||
|
||||
require('dotenv').config(); |
||||
|
||||
app.set('views', __dirname + '/views'); |
||||
app.set('view engine', 'jsx'); |
||||
app.engine('jsx', require('express-react-views').createEngine()); |
||||
app.use(express.static(path.join(__dirname, '/public'))); |
||||
|
||||
const db = require('./database.js'); |
||||
|
||||
async function db_all(query) { |
||||
return new Promise(function (resolve, reject) { |
||||
db.all(query, function (err, rows) { |
||||
if (err) { |
||||
return reject(err); |
||||
} |
||||
resolve(rows); |
||||
}); |
||||
}); |
||||
} |
||||
|
||||
app.get('/', async (req, res) => { |
||||
const sql = `SELECT * FROM netmonResults`; |
||||
const dbdata = await db_all(sql); |
||||
res.render('index', { dbdata }); |
||||
}); |
||||
|
||||
app.listen(port, () => { |
||||
console.log( |
||||
`app listening at http://localhost:${port} in ${app.settings.env} mode. netmon is monitoring ${process.env.dstHost}` |
||||
); |
||||
}); |
@ -0,0 +1,181 @@
|
||||
#!/usr/bin/python3 |
||||
# -*- coding: utf-8 -*- |
||||
|
||||
import os |
||||
import csv |
||||
import sys |
||||
import time |
||||
import socket |
||||
import datetime |
||||
import sqlite3 |
||||
from pathlib import Path |
||||
|
||||
path = Path(__file__).resolve().parent |
||||
|
||||
def update_db(payload): |
||||
conn = None |
||||
try: |
||||
db = (str(path) + "/db.sqlite3") |
||||
conn = sqlite3.connect(db) |
||||
|
||||
c = conn.cursor() |
||||
c.execute( |
||||
''' |
||||
CREATE TABLE IF NOT EXISTS netmonResults ( |
||||
id integer PRIMARY KEY, |
||||
timestamp text, |
||||
status integer |
||||
) |
||||
''' |
||||
) |
||||
c.execute( |
||||
''' |
||||
INSERT OR REPLACE into netmonResults ( |
||||
timestamp, status |
||||
) |
||||
VALUES (?, ?) |
||||
''', |
||||
(payload) |
||||
) |
||||
conn.commit() |
||||
print (f"db update successful") |
||||
except Exception as ex: |
||||
print(ex) |
||||
print (f"db operation failed") |
||||
finally: |
||||
if conn: |
||||
conn.close() |
||||
|
||||
def internet(dstHost, dstPort, timeout=3): |
||||
""" Generates a new request""" |
||||
try: |
||||
socket.setdefaulttimeout(timeout) |
||||
socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((dstHost, int(dstPort))) |
||||
return True |
||||
except Exception as ex: |
||||
# print(ex) |
||||
return False |
||||
|
||||
|
||||
def current_timestamp(): |
||||
""" Get Current timestamp string """ |
||||
return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") |
||||
|
||||
|
||||
def str_to_date(timestamp): |
||||
""" Convert timestamp string to date object """ |
||||
return datetime.datetime.strptime(timestamp, "%Y-%m-%d %H:%M:%S") |
||||
|
||||
|
||||
def secs_to_HMS(secs): |
||||
""" Convert seconds to second and minutes |
||||
Format : HH:MM:SS |
||||
""" |
||||
return str(datetime.timedelta(seconds=secs)); |
||||
|
||||
|
||||
def record_file_exist(): |
||||
""" Check if records file exist """ |
||||
return os.path.isfile('data.csv') |
||||
|
||||
|
||||
def create_record_file(): |
||||
""" Create a new record file """ |
||||
with open('data.csv', 'a') as csvfile: |
||||
columns = ['timestamp', 'status'] |
||||
writer = csv.DictWriter(csvfile, fieldnames=columns) |
||||
writer.writeheader() |
||||
|
||||
|
||||
def last_record_status(): |
||||
""" Get last record """ |
||||
result = None |
||||
with open('data.csv', 'r') as csvfile: |
||||
reader = csv.DictReader(csvfile) |
||||
for row in reader: |
||||
result = row |
||||
return None if result is None else result['status'] |
||||
|
||||
|
||||
def write_record(status): |
||||
""" Create a new record """ |
||||
with open('data.csv', 'a') as csvfile: |
||||
columns = ['timestamp', 'status'] |
||||
writer = csv.DictWriter(csvfile, fieldnames=columns) |
||||
writer.writerow({'timestamp': str(current_timestamp()), 'status': status}) |
||||
|
||||
|
||||
def get_total_downtime(): |
||||
""" Calculate downtime """ |
||||
seconds = 0 |
||||
down = None |
||||
up = None |
||||
with open('data.csv', 'r') as csvfile: |
||||
reader = csv.DictReader(csvfile) |
||||
for record in reader: |
||||
try: |
||||
if record['status'] == '0': |
||||
print('Went Down at : ', record['timestamp']) |
||||
down = str_to_date(record['timestamp']) |
||||
next_record = next(reader) |
||||
up = str_to_date(next_record['timestamp']) |
||||
seconds += (up - down).total_seconds() |
||||
print('Went up at : ', next_record['timestamp']) |
||||
except Exception as ex: |
||||
print('\nCurrent Status : Still Down') |
||||
seconds += (str_to_date(current_timestamp()) - down).total_seconds() |
||||
return secs_to_HMS(seconds); |
||||
|
||||
|
||||
def monitor_connection(sleep_time): |
||||
try: |
||||
dstHost = os.getenv('dstHost') |
||||
dstPort = os.getenv('dstPort') |
||||
if dstHost is None or dstPort is None: |
||||
raise Exception('vars are fucked') |
||||
except Exception as error: |
||||
print('Caught this error: ' + repr(error)) |
||||
print (""" |
||||
pls set vars |
||||
|
||||
eg; |
||||
|
||||
for host: |
||||
export dstHost=localhost dstPort=8000 |
||||
|
||||
for docker: |
||||
-e dstHost=localhost -e dstPort=8000 |
||||
|
||||
""") |
||||
sys.exit(1) |
||||
|
||||
""" Start monitoring """ |
||||
print('Monitoring your connection for ' + dstHost) |
||||
while True: |
||||
last_record = last_record_status() |
||||
if not internet(dstHost, dstPort): |
||||
if last_record == '1' or last_record is None: |
||||
print('Internet went down') |
||||
write_record(0) |
||||
update_db((str(current_timestamp()), '0')) |
||||
else: |
||||
if last_record == '0' or last_record is None: |
||||
print('Internet is up') |
||||
write_record(1) |
||||
update_db((str(current_timestamp()), '1')) |
||||
time.sleep(sleep_time) |
||||
|
||||
|
||||
def args_error(): |
||||
print('Please provide an argument\nOptions\n./internet.py monitor\n./internet.py downtime') |
||||
|
||||
|
||||
args = sys.argv |
||||
if not len(args) > 1: |
||||
args_error() |
||||
elif args[1] == 'monitor': |
||||
if not record_file_exist(): |
||||
create_record_file() |
||||
monitor_connection(1) |
||||
elif args[1] == 'downtime': |
||||
print('\nRemained down for : ', get_total_downtime(), ' HH:MM:SS ') |
@ -0,0 +1,26 @@
|
||||
{ |
||||
"dependencies": { |
||||
"dayjs": "^1.10.6", |
||||
"dotenv": "^10.0.0", |
||||
"express": "^4.17.1", |
||||
"express-react-views": "^0.11.0", |
||||
"react": "^16.14.0", |
||||
"react-dom": "^16.14.0", |
||||
"sqlite3": "^5.0.2" |
||||
}, |
||||
"devDependencies": { |
||||
"autoprefixer": "^10.3.1", |
||||
"nodemon": "^2.0.12", |
||||
"postcss": "^8.3.6", |
||||
"postcss-cli": "^8.3.1", |
||||
"tailwindcss": "^2.2.7" |
||||
}, |
||||
"scripts": { |
||||
"start": "NODE_ENV=development node index.js", |
||||
"build:css": "postcss public/styles/tailwind.css -o public/styles/style.css", |
||||
"dev": "npm run build:css && nodemon index.js", |
||||
"testHost": "python -m http.server 8000", |
||||
"netmon": "python netmon.py monitor", |
||||
"prod": "NODE_ENV=production node index.js " |
||||
} |
||||
} |
@ -0,0 +1,6 @@
|
||||
module.exports = { |
||||
plugins: { |
||||
tailwindcss: {}, |
||||
autoprefixer: {} |
||||
}, |
||||
}; |
@ -0,0 +1,3 @@
|
||||
@tailwind base; |
||||
@tailwind components; |
||||
@tailwind utilities; |
@ -0,0 +1,36 @@
|
||||
#!/bin/bash |
||||
|
||||
# Start the first process |
||||
./my_first_process -D |
||||
status=$? |
||||
if [ $status -ne 0 ]; then |
||||
echo "Failed to start my_first_process: $status" |
||||
exit $status |
||||
fi |
||||
|
||||
# Start the second process |
||||
./my_second_process -D |
||||
status=$? |
||||
if [ $status -ne 0 ]; then |
||||
echo "Failed to start my_second_process: $status" |
||||
exit $status |
||||
fi |
||||
|
||||
# Naive check runs checks once a minute to see if either of the processes exited. |
||||
# This illustrates part of the heavy lifting you need to do if you want to run |
||||
# more than one service in a container. The container exits with an error |
||||
# if it detects that either of the processes has exited. |
||||
# Otherwise it loops forever, waking up every 60 seconds |
||||
|
||||
while sleep 60; do |
||||
ps aux |grep my_first_process |grep -q -v grep |
||||
PROCESS_1_STATUS=$? |
||||
ps aux |grep my_second_process |grep -q -v grep |
||||
PROCESS_2_STATUS=$? |
||||
# If the greps above find anything, they exit with 0 status |
||||
# If they are not both 0, then something is wrong |
||||
if [ $PROCESS_1_STATUS -ne 0 -o $PROCESS_2_STATUS -ne 0 ]; then |
||||
echo "One of the processes has already exited." |
||||
exit 1 |
||||
fi |
||||
done |
@ -0,0 +1,12 @@
|
||||
module.exports = { |
||||
purge: [], |
||||
darkMode: false, // or 'media' or 'class'
|
||||
purge: ['./public/**/*.html', './views/*.{js,jsx,ts,tsx,vue}', '.index.js'], |
||||
theme: { |
||||
extend: {}, |
||||
}, |
||||
variants: { |
||||
extend: {}, |
||||
}, |
||||
plugins: [require('tailwindcss'), require('autoprefixer')], |
||||
}; |
@ -0,0 +1,27 @@
|
||||
const React = require('react'); |
||||
import { TimeBetweenRows } from './DiffCard'; |
||||
|
||||
export const Card = ({ dbdata }) => { |
||||
return dbdata.reverse().map((row, index) => { |
||||
const nextRow = dbdata[index + 1]; |
||||
|
||||
return ( |
||||
<div className="grid grid-cols-1 px-2"> |
||||
<div |
||||
className={`shadow-md p-1 flex items-center justify-between ${ |
||||
row.status === 1 ? 'bg-green-50' : 'bg-red-50' |
||||
} `} |
||||
> |
||||
<div className="flex-grow pl-2">{row.timestamp}</div> |
||||
<div className="p-1 pr-2">{row.status === 1 ? '🟢' : '🔴'}</div> |
||||
</div> |
||||
|
||||
{!!nextRow ? ( |
||||
<div className="flex justify-center items-center py-1 text-gray-400"> |
||||
<TimeBetweenRows row={row} nextRow={nextRow} /> |
||||
</div> |
||||
) : null} |
||||
</div> |
||||
); |
||||
}); |
||||
}; |
@ -0,0 +1,88 @@
|
||||
const React = require('react'); |
||||
const dayjs = require('dayjs'); |
||||
|
||||
export const TimeBetweenRows = ({ row, nextRow }) => { |
||||
if (nextRow) { |
||||
const firstDate = dayjs(row.timestamp); |
||||
const secondDate = dayjs(nextRow.timestamp); |
||||
const theDiff = firstDate.diff(secondDate); |
||||
const theSeconds = theDiff / 1000; |
||||
const theMinutes = theSeconds / 60; |
||||
const theHours = theMinutes / 60; |
||||
const theDays = theHours / 24; |
||||
|
||||
// console.log(theDiff, theSeconds, theMinutes, theHours, theDays); |
||||
|
||||
const renderSecs = <>{theSeconds === 1 ? 'sec' : 'secs'}</>; |
||||
const renderMins = <>{theMinutes === 1 ? 'min ' : 'mins '}</>; |
||||
const renderHrs = <>{theHours === 1 ? 'hr ' : 'hrs '}</>; |
||||
const renderDays = <>{theDays === 1 ? 'day ' : 'days '}</>; |
||||
|
||||
return ( |
||||
<> |
||||
{/* secs */} |
||||
{theSeconds < 60 ? ( |
||||
<> |
||||
{Math.round(theSeconds)} |
||||
{renderSecs} |
||||
</> |
||||
) : null} |
||||
|
||||
{/* mins secs */} |
||||
{theMinutes > 1 && theMinutes < 60 ? ( |
||||
<> |
||||
<> |
||||
{Math.round(theMinutes)} |
||||
{renderMins} |
||||
</> |
||||
<> |
||||
{Math.round((theMinutes % 1) * 60)} |
||||
{renderSecs} |
||||
</> |
||||
</> |
||||
) : null} |
||||
|
||||
{/* hrs mins secs */} |
||||
{theHours > 1 && theHours < 24 ? ( |
||||
<> |
||||
<> |
||||
{Math.round(theHours)} |
||||
{renderHrs} |
||||
</> |
||||
<> |
||||
{Math.round((theHours % 1) * 60)} |
||||
{renderMins} |
||||
</> |
||||
<> |
||||
{Math.round((theMinutes % 1) * 60)} |
||||
{renderSecs} |
||||
</> |
||||
</> |
||||
) : null} |
||||
|
||||
{/* days hrs mins secs */} |
||||
{theDays > 1 ? ( |
||||
<> |
||||
<> |
||||
{Math.round(theDays)} |
||||
{renderDays} |
||||
</> |
||||
<> |
||||
{Math.round((theDays % 1) * 24)} |
||||
{renderHrs} |
||||
</> |
||||
<> |
||||
{Math.round((theHours % 1) * 60)} |
||||
{renderMins} |
||||
</> |
||||
<> |
||||
{Math.round((theMinutes % 1) * 60)} |
||||
{renderSecs} |
||||
</> |
||||
</> |
||||
) : null} |
||||
</> |
||||
); |
||||
} |
||||
return false; |
||||
}; |
@ -0,0 +1,16 @@
|
||||
const React = require('react'); |
||||
|
||||
export const Title = ({ dbdata }) => { |
||||
const statusCheck = dbdata?.slice(-1)[0].status === 1; |
||||
|
||||
return ( |
||||
<div className="text-xl p-2 pb-4"> |
||||
<div> |
||||
Everything looks {statusCheck ? 'AMAZING 🔥🔥🔥' : 'HORRIFIC 💀💀💀'} |
||||
</div> |
||||
<div className="text-xs text-gray-400 pt-1"> |
||||
Monitoring {process.env.dstHost} |
||||
</div> |
||||
</div> |
||||
); |
||||
}; |
@ -0,0 +1,33 @@
|
||||
const React = require('react'); |
||||
|
||||
import { Title } from './Title'; |
||||
import { Card } from './Card'; |
||||
|
||||
function App({ dbdata }) { |
||||
return ( |
||||
<html lang="en"> |
||||
<head> |
||||
<meta charSet="UTF-8" /> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
||||
<link rel="stylesheet" href="/styles/style.css" /> |
||||
</head> |
||||
<title>netmon</title> |
||||
<body className="bg-indigo-50 max-w-sm antialiased tracking-wide leading-relaxed container mx-auto"> |
||||
<div className="shadow-2xl my-8 rounded p-2"> |
||||
{dbdata.length ? ( |
||||
<div> |
||||
<Title dbdata={dbdata} /> |
||||
<Card dbdata={dbdata} /> |
||||
</div> |
||||
) : ( |
||||
<div className="flex justify-center items-center "> |
||||
no information from the db was loaded, sad |
||||
</div> |
||||
)} |
||||
</div> |
||||
</body> |
||||
</html> |
||||
); |
||||
} |
||||
|
||||
module.exports = App; |
Loading…
Reference in new issue